Files
chart/viewer/render_test.go
2026-03-15 21:41:38 +03:00

273 lines
6.3 KiB
Go

package viewer
import (
"reflect"
"strings"
"testing"
)
func TestRenderHTMLIncludesKnownSectionsAndFields(t *testing.T) {
snapshot := []byte(`{
"target_host": "test-host",
"collected_at": "2026-03-15T12:00:00Z",
"hardware": {
"board": {
"serial_number": "BOARD-001",
"product_name": "Test Server"
},
"cpus": [
{
"socket": 0,
"model": "Xeon",
"status": "OK",
"temperature_c": 61.5
}
]
}
}`)
html, err := RenderHTML(snapshot, "Reanimator Chart")
if err != nil {
t.Fatalf("RenderHTML() error = %v", err)
}
text := string(html)
for _, needle := range []string{
"Reanimator Chart",
"test-host",
"15 Mar 2026, 12:00 UTC",
"Board",
"CPUs",
"BOARD-001",
"Xeon",
"temperature_c",
"status-ok",
} {
if !strings.Contains(text, needle) {
t.Fatalf("expected rendered html to contain %q", needle)
}
}
if strings.Contains(text, "2026-03-15T12:00:00Z") {
t.Fatalf("expected RFC3339 timestamp to be rendered in human-readable form")
}
}
func TestRenderHTMLFormatsNestedObjectsWithoutRawJSON(t *testing.T) {
snapshot := []byte(`{
"target_host": "nested-host",
"hardware": {
"board": {
"status_history": {
"last_change": "2026-03-15T12:30:00Z",
"reason": "manual review"
}
}
}
}`)
html, err := RenderHTML(snapshot, "Reanimator Chart")
if err != nil {
t.Fatalf("RenderHTML() error = %v", err)
}
text := string(html)
for _, needle := range []string{
"status_history",
"last_change: 15 Mar 2026, 12:30 UTC",
"reason: manual review",
} {
if !strings.Contains(text, needle) {
t.Fatalf("expected rendered html to contain %q", needle)
}
}
if strings.Contains(text, "{"") || strings.Contains(text, "\"last_change\"") {
t.Fatalf("expected nested object to render without raw JSON blob")
}
}
func TestCollectColumnsOrdersStatusThenLocationThenIdentity(t *testing.T) {
rows := []map[string]any{
{
"serial_number": "SN-1",
"model": "Model-X",
"vendor": "Vendor-A",
"vendor_id": "8086",
"device_id": "1234",
"firmware": "1.2.3",
"location": "Bay 4",
"slot": "Slot 9",
"status": "OK",
"status_at_collection": "OK",
"status_checked_at": "2026-03-15T12:40:00Z",
"temperature_c": 40,
},
}
got := collectColumns("storage", rows)
want := []string{
"status",
"slot",
"location",
"vendor",
"model",
"serial_number",
"ven:dev",
"firmware",
"temperature_c",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("collectColumns() = %v, want %v", got, want)
}
}
func TestCollectColumnsOrdersCPUFields(t *testing.T) {
rows := []map[string]any{
{
"model": "Xeon Gold",
"clock": "2.90 GHz",
"cores": 16,
"threads": 32,
"l1": "1 MiB",
"l2": "16 MiB",
"l3": "22 MiB",
"microcode": "0xd000375",
"socket": "CPU0",
"status": "OK",
},
}
got := collectColumns("cpus", rows)
want := []string{
"status",
"model",
"clock",
"cores",
"threads",
"l1",
"l2",
"l3",
"microcode",
"socket",
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("collectColumns() = %v, want %v", got, want)
}
}
func TestRenderHTMLHidesStatusAtCollection(t *testing.T) {
snapshot := []byte(`{
"target_host": "hidden-field-host",
"hardware": {
"storage": [
{
"status": "OK",
"status_at_collection": "OK",
"status_checked_at": "2026-03-15T12:45:00Z",
"location": "Bay 1",
"vendor": "Vendor-A",
"model": "Model-Z"
}
],
"board": {
"status": "OK",
"status_at_collection": "OK",
"status_checked_at": "2026-03-15T12:40:00Z"
}
}
}`)
html, err := RenderHTML(snapshot, "Reanimator Chart")
if err != nil {
t.Fatalf("RenderHTML() error = %v", err)
}
text := string(html)
if strings.Contains(text, "status_at_collection") {
t.Fatalf("expected status_at_collection to be hidden from rendered output")
}
if !strings.Contains(text, "<th>status_checked_at</th>") {
t.Fatalf("expected status_checked_at to remain visible in object sections")
}
if strings.Contains(text, "<thead>\n <tr>\n <th>status_checked_at</th>") {
t.Fatalf("expected status_checked_at to be hidden from table headers")
}
}
func TestRenderHTMLCombinesVendorAndDeviceID(t *testing.T) {
snapshot := []byte(`{
"target_host": "pci-host",
"hardware": {
"pcie_devices": [
{
"status": "OK",
"location": "Slot 3",
"vendor": "Intel",
"model": "Ethernet Adapter",
"vendor_id": "8086",
"device_id": "1234"
}
]
}
}`)
html, err := RenderHTML(snapshot, "Reanimator Chart")
if err != nil {
t.Fatalf("RenderHTML() error = %v", err)
}
text := string(html)
if !strings.Contains(text, "ven:dev") {
t.Fatalf("expected combined vendor/device id column to be rendered")
}
if !strings.Contains(text, "8086:1234") {
t.Fatalf("expected vendor/device id value to be rendered as ven:dev")
}
if strings.Contains(text, "<th>vendor_id</th>") || strings.Contains(text, "<th>device_id</th>") {
t.Fatalf("expected raw vendor_id and device_id columns to be hidden")
}
}
func TestRenderHTMLGroupsPCIeDevicesByClass(t *testing.T) {
snapshot := []byte(`{
"target_host": "pcie-group-host",
"hardware": {
"pcie_devices": [
{
"status": "OK",
"slot": "Slot 2",
"device_class": "Network controller",
"vendor": "Vendor-B",
"model": "NIC-B"
},
{
"status": "Warning",
"slot": "Slot 1",
"device_class": "Display controller",
"vendor": "Vendor-A",
"model": "GPU-A"
}
]
}
}`)
html, err := RenderHTML(snapshot, "Reanimator Chart")
if err != nil {
t.Fatalf("RenderHTML() error = %v", err)
}
text := string(html)
if !strings.Contains(text, "<h3>Display controller</h3>") || !strings.Contains(text, "<h3>Network controller</h3>") {
t.Fatalf("expected PCIe devices to be split into class subheadings")
}
if strings.Contains(text, "<th>device_class</th>") {
t.Fatalf("expected device_class column to be hidden from PCIe tables")
}
if strings.Index(text, "<h3>Display controller</h3>") > strings.Index(text, "<h3>Network controller</h3>") {
t.Fatalf("expected PCIe class groups to be sorted by device_class")
}
}