package settings import ( "testing" "time" ) func TestNodeAuditRoundTripAndCap(t *testing.T) { dir := t.TempDir() store, err := LoadNodeAuditOrEmpty(dir) if err != nil { t.Fatalf("LoadNodeAuditOrEmpty: %v", err) } if len(store.Events) != 0 { t.Fatalf("expected empty, got %d", len(store.Events)) } event := NodeAuditEvent{ ID: "evt-1", At: time.Now().UTC(), Action: NodeAuditActionCreate, Actor: "admin", NodeID: "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee", NodeName: "ct1", NodeKind: NodeKindContainer, } if err := AppendNodeAuditEvent(dir, event); err != nil { t.Fatalf("AppendNodeAuditEvent: %v", err) } loaded, err := LoadNodeAudit(dir) if err != nil { t.Fatalf("LoadNodeAudit: %v", err) } if len(loaded.Events) != 1 || loaded.Events[0].Action != NodeAuditActionCreate { t.Fatalf("loaded = %+v", loaded) } } func TestAppendNodeAuditEventTrimsToCap(t *testing.T) { dir := t.TempDir() originalCap := MaxNodeAuditEvents // Use a small synthetic store by appending more than the cap. store := NodeAuditStore{Events: make([]NodeAuditEvent, 0, MaxNodeAuditEvents+5)} for index := 0; index < MaxNodeAuditEvents+5; index++ { store.Events = append(store.Events, NodeAuditEvent{ ID: "evt", At: time.Now().UTC(), Action: NodeAuditActionCreate, Actor: "admin", NodeID: "id", NodeName: "n", NodeKind: NodeKindVM, Detail: string(rune('a' + (index % 26))), }) } if err := SaveNodeAudit(dir, store); err != nil { t.Fatalf("SaveNodeAudit: %v", err) } if err := AppendNodeAuditEvent(dir, NodeAuditEvent{ ID: "newest", At: time.Now().UTC(), Action: NodeAuditActionDelete, Actor: "admin", NodeID: "id", NodeName: "n", NodeKind: NodeKindVM, }); err != nil { t.Fatalf("AppendNodeAuditEvent: %v", err) } loaded, err := LoadNodeAudit(dir) if err != nil { t.Fatalf("LoadNodeAudit: %v", err) } if len(loaded.Events) != originalCap { t.Fatalf("len = %d, want %d", len(loaded.Events), originalCap) } if loaded.Events[len(loaded.Events)-1].ID != "newest" { t.Fatalf("last id = %q", loaded.Events[len(loaded.Events)-1].ID) } }