273 lines
6.3 KiB
Go
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")
|
|
}
|
|
}
|