/topo blocked the HTTP request on live nvidia-smi calls with no timeout
(topo -m, nvlink -s/-e run on every page load), so a wedged driver hung
the page indefinitely. CaptureTechnicalDump now persists these dumps
once per audit cycle; the page reads them from techdump/ instead.
buildSocketIndex mapped NUMA node number to CPU by treating dmidecode's
Socket Designation (often 1-indexed, "CPU1"/"CPU2") as equal to the
NUMA node number (always 0-indexed) — GPUs/NICs on NUMA node 0 fell
into the "unknown" column, others attached to the wrong CPU box. Now
ranks CPUs by Socket value instead of assuming a shared numbering base.
Fixed a bug in ComponentStatusDB/applyComponentStatusDB where GPU SAT
results were keyed per-target ("pcie:gpu:nvidia-stress") instead of
per-vendor, which both broke cross-tier severity tracking (a later
clean "2. Check" run and an earlier failing "3. Load" run never
compared severities) and silently failed to match any real BDF, so the
DB overlay never reached the topology graph at all. GPU keys are now
normalized to vendor ("pcie:gpu:nvidia"/"pcie:gpu:amd"). Also skip
writing to the DB when a SAT task was aborted by the user (ctx
canceled), so a partial run can't stomp a previously recorded status.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
100 lines
3.5 KiB
Go
100 lines
3.5 KiB
Go
package app
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"bee/audit/internal/schema"
|
|
)
|
|
|
|
func TestExtractArchivePath(t *testing.T) {
|
|
cases := map[string]string{
|
|
"/appdata/bee/export/bee-sat/gpu-nvidia-20260706-174722": "/appdata/bee/export/bee-sat/gpu-nvidia-20260706-174722",
|
|
"Archive written to /appdata/bee/export/bee-sat/gpu-nvidia-20260706-174722": "/appdata/bee/export/bee-sat/gpu-nvidia-20260706-174722",
|
|
"Archive written to /path/with spaces/foo.tar.gz": "/path/with spaces/foo.tar.gz",
|
|
" Archive written to /path/foo.tar.gz ": "/path/foo.tar.gz",
|
|
}
|
|
for in, want := range cases {
|
|
if got := ExtractArchivePath(in); got != want {
|
|
t.Errorf("ExtractArchivePath(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadSATOverallStatus_HandlesActionResultPrefix(t *testing.T) {
|
|
runDir := t.TempDir()
|
|
summary := "run_at_utc=2026-07-06T17:47:22Z\noverall_status=FAILED\n"
|
|
if err := os.WriteFile(filepath.Join(runDir, "summary.txt"), []byte(summary), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Regression: RunNvidiaAcceptancePackWithOptions wraps the bare run dir as
|
|
// "Archive written to <dir>" before it reaches ReadSATOverallStatus. If the
|
|
// prefix isn't stripped, the summary.txt lookup silently fails and a FAILED
|
|
// sub-job never surfaces as a task failure.
|
|
wrapped := "Archive written to " + runDir
|
|
if got := ReadSATOverallStatus(ExtractArchivePath(wrapped)); got != "FAILED" {
|
|
t.Errorf("ReadSATOverallStatus(wrapped) = %q, want FAILED", got)
|
|
}
|
|
|
|
if got := ReadSATOverallStatus(ExtractArchivePath(runDir)); got != "FAILED" {
|
|
t.Errorf("ReadSATOverallStatus(bare) = %q, want FAILED", got)
|
|
}
|
|
}
|
|
|
|
func writeSATSummary(t *testing.T, overall string) string {
|
|
t.Helper()
|
|
runDir := t.TempDir()
|
|
summary := "run_at_utc=2026-07-06T17:47:22Z\noverall_status=" + overall + "\n"
|
|
if err := os.WriteFile(filepath.Join(runDir, "summary.txt"), []byte(summary), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return runDir
|
|
}
|
|
|
|
func TestApplySATResultToDBNormalizesGPUKeyByVendor(t *testing.T) {
|
|
// "nvidia" (Check tier) and "nvidia-stress" (Load/Burn tier) exercise the
|
|
// same physical GPUs and must collapse onto one component key so a later
|
|
// clean Check run can't erase an earlier Load-tier failure.
|
|
db, err := OpenComponentStatusDB(filepath.Join(t.TempDir(), "component-status.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ApplySATResultToDB(db, "nvidia-stress", writeSATSummary(t, "FAILED"))
|
|
ApplySATResultToDB(db, "nvidia", writeSATSummary(t, "OK"))
|
|
|
|
rec, ok := db.Get("pcie:gpu:nvidia")
|
|
if !ok {
|
|
t.Fatalf("expected pcie:gpu:nvidia record to exist")
|
|
}
|
|
if rec.Status != "Warning" {
|
|
t.Fatalf("status=%q, want Warning (FAILED) to survive the later OK Check run", rec.Status)
|
|
}
|
|
}
|
|
|
|
func TestApplyComponentStatusDBMatchesGPUByVendor(t *testing.T) {
|
|
db, err := OpenComponentStatusDB(filepath.Join(t.TempDir(), "component-status.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
db.Record("pcie:gpu:nvidia", "sat:nvidia-stress", "Critical", "nvidia-stress SAT: FAILED")
|
|
|
|
class := "VideoController"
|
|
vendor := 0x10de // collector.NvidiaVendorID
|
|
snap := &schema.HardwareSnapshot{
|
|
PCIeDevices: []schema.HardwarePCIeDevice{
|
|
{DeviceClass: &class, VendorID: &vendor, BDF: strPtr("0000:c8:00.0")},
|
|
},
|
|
}
|
|
|
|
applyComponentStatusDB(snap, db)
|
|
|
|
if snap.PCIeDevices[0].Status == nil || *snap.PCIeDevices[0].Status != "Critical" {
|
|
t.Fatalf("expected GPU device status Critical, got %v", snap.PCIeDevices[0].Status)
|
|
}
|
|
}
|
|
|
|
func strPtr(s string) *string { return &s }
|