diff --git a/data.go b/data.go index 99ca2b8..578487b 100644 --- a/data.go +++ b/data.go @@ -87,6 +87,9 @@ func SetForTest(agents []Agent) (restore func()) { if a.Hostname == "" { continue } + if other, exists := idx[a.NodeID]; exists { + panic(fmt.Sprintf("SetForTest: duplicate node_id %d (hostnames %q and %q)", a.NodeID, other, a.Hostname)) + } idx[a.NodeID] = a.Hostname } mu.Lock() @@ -202,6 +205,9 @@ func Load(raw []byte) error { if a.Hostname == "" { continue // empty hostname: missing required field — drop } + if other, exists := idx[a.NodeID]; exists { + return fmt.Errorf("duplicate node_id %d in trusted-agents list: %q and %q", a.NodeID, other, a.Hostname) + } idx[a.NodeID] = a.Hostname } mu.Lock() diff --git a/zz_test.go b/zz_test.go index fadf207..9e38a94 100644 --- a/zz_test.go +++ b/zz_test.go @@ -112,6 +112,22 @@ func TestLoadEmptyHostnameSkipped(t *testing.T) { _ = Load(embeddedJSON) } +func TestLoadDuplicateNodeID(t *testing.T) { + t.Parallel() + err := Load([]byte(`{"agents":[ + {"hostname":"a","node_id":1}, + {"hostname":"b","node_id":1} + ]}`)) + if err == nil { + t.Fatal("Load with duplicate node_id must return an error") + } + // Also verify the list wasn't corrupted by the failed load. + if name, ok := IsTrusted(1); ok { + t.Fatalf("IsTrusted(1)=%q after failed Load — list must not be updated", name) + } + _ = Load(embeddedJSON) // restore +} + func TestAllReturnsCopy(t *testing.T) { t.Parallel() restore := SetForTest([]Agent{{Hostname: "a", NodeID: 1}})