Files
logpile/internal/server/device_repository_test.go

153 lines
4.3 KiB
Go

package server
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"git.mchus.pro/mchus/logpile/internal/models"
)
func TestBuildHardwareDevices_DedupSerialThenBDF(t *testing.T) {
hw := &models.HardwareConfig{
PCIeDevices: []models.PCIeDevice{
{Slot: "A1", SerialNumber: "SER-1", BDF: "0000:01:00.0", DeviceClass: "NetworkController"},
{Slot: "A2", SerialNumber: "SER-1", BDF: "0000:02:00.0", DeviceClass: "NetworkController"},
{Slot: "B1", SerialNumber: "", BDF: "0000:03:00.0", DeviceClass: "NetworkController"},
{Slot: "B2", SerialNumber: "", BDF: "0000:03:00.0", DeviceClass: "NetworkController"},
{Slot: "C1", SerialNumber: "", BDF: "", DeviceClass: "NetworkController"},
{Slot: "C2", SerialNumber: "", BDF: "", DeviceClass: "NetworkController"},
},
}
devices := BuildHardwareDevices(hw)
// 1 board + (SER-1 dedup -> 1) + (BDF 03 dedup -> 1) + (C1,C2 keep both) = 5
if len(devices) != 5 {
t.Fatalf("expected 5 devices after dedupe, got %d", len(devices))
}
bySlot := map[string]bool{}
for _, d := range devices {
bySlot[d.Slot] = true
}
if !bySlot["A1"] && !bySlot["A2"] {
t.Fatalf("expected one serial-deduped A* device")
}
if bySlot["B1"] && bySlot["B2"] {
t.Fatalf("expected B1/B2 to dedupe by bdf")
}
if !bySlot["C1"] || !bySlot["C2"] {
t.Fatalf("expected C1 and C2 to remain without serial/bdf")
}
}
func TestBuildHardwareDevices_SkipsEmptyMemorySlots(t *testing.T) {
hw := &models.HardwareConfig{
Memory: []models.MemoryDIMM{
{Slot: "A1", Present: true, SizeMB: 32768, SerialNumber: "DIMM-1"},
{Slot: "A2", Present: false, SizeMB: 0, SerialNumber: "DIMM-2"},
},
}
devices := BuildHardwareDevices(hw)
memoryCount := 0
for _, d := range devices {
if d.Kind == models.DeviceKindMemory {
memoryCount++
if d.Slot == "A2" {
t.Fatalf("empty memory slot should not be included")
}
}
}
if memoryCount != 1 {
t.Fatalf("expected 1 installed memory record, got %d", memoryCount)
}
}
func TestBuildHardwareDevices_DedupCrossKindByBDF(t *testing.T) {
hw := &models.HardwareConfig{
PCIeDevices: []models.PCIeDevice{
{
Slot: "SL0CP0_001",
BDF: "02:00.0",
DeviceClass: "DisplayController",
VendorID: 0x1a03,
DeviceID: 0x2000,
PartNumber: "ASPEED Graphics Family",
Manufacturer: "ASPEED Technology, Inc.",
},
},
GPUs: []models.GPU{
{
Slot: "SL0CP0_001",
BDF: "02:00.0",
Model: "ASPEED Graphics Family",
Manufacturer: "ASPEED Technology, Inc.",
VendorID: 0x1a03,
DeviceID: 0x2000,
},
},
}
devices := BuildHardwareDevices(hw)
count := 0
for _, d := range devices {
if d.BDF == "02:00.0" {
count++
}
}
if count != 1 {
t.Fatalf("expected 1 canonical device for bdf 02:00.0, got %d", count)
}
}
func TestBuildHardwareDevices_SkipsFirmwareOnlyNumericSlots(t *testing.T) {
hw := &models.HardwareConfig{
PCIeDevices: []models.PCIeDevice{
{Slot: "0", DeviceClass: "Unknown", Manufacturer: "-", PartNumber: "-", Description: "-"},
{Slot: "1", DeviceClass: "Other", Manufacturer: "unknown", PartNumber: "N/A", Description: "NULL"},
},
}
devices := BuildHardwareDevices(hw)
for _, d := range devices {
if d.Kind == models.DeviceKindPCIe && (d.Slot == "0" || d.Slot == "1") {
t.Fatalf("firmware-only numeric-slot pcie record must be filtered, got slot %q", d.Slot)
}
}
}
func TestHandleGetConfig_ReturnsCanonicalHardware(t *testing.T) {
srv := &Server{}
srv.SetResult(&models.AnalysisResult{
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{ProductName: "X", SerialNumber: "SN-1"},
CPUs: []models.CPU{{Socket: 0, Model: "CPU"}},
},
})
req := httptest.NewRequest(http.MethodGet, "/api/config", nil)
w := httptest.NewRecorder()
srv.handleGetConfig(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
var payload map[string]any
if err := json.NewDecoder(w.Body).Decode(&payload); err != nil {
t.Fatalf("decode response: %v", err)
}
hardware, ok := payload["hardware"].(map[string]any)
if !ok {
t.Fatalf("expected hardware object")
}
if _, ok := hardware["devices"]; !ok {
t.Fatalf("expected hardware.devices in config response")
}
if _, ok := hardware["cpus"]; ok {
t.Fatalf("did not expect legacy hardware.cpus in config response")
}
}