-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcheckpoint_ghost_nara_test.go
More file actions
107 lines (90 loc) · 3.46 KB
/
Copy pathcheckpoint_ghost_nara_test.go
File metadata and controls
107 lines (90 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package nara
import (
"testing"
"time"
"github.com/eljojo/nara/types"
)
// TestCheckpoint_VoterIDsNotUsedAsNames verifies that checkpoint VoterIDs (which are
// nara IDs, not names) don't create ghost naras with IDs as names.
// This was a bug where GetActor() returned VoterIDs[0], causing the event processing
// loop to treat IDs as names and create ghost naras like "EGqUnthqW8bNDb5SzNzPkkyzJbQVnkqo2Z4hjL4nrTVg".
func TestCheckpoint_VoterIDsNotUsedAsNames(t *testing.T) {
t.Parallel()
ln := testLocalNaraWithParams(t, "test-nara", 50, 1000)
network := ln.Network
// Create a checkpoint event with a VoterID (nara ID, not name)
voterID := types.NaraID("EGqUnthqW8bNDb5SzNzPkkyzJbQVnkqo2Z4hjL4nrTVg") // Example nara ID from production
voterName := types.NaraName("alice") // The actual nara name
// Import the real nara with proper name
alice := NewNara(voterName)
alice.ID = voterID
alice.Status.ID = voterID
network.importNara(alice)
// Create a checkpoint event with VoterID (ID, not name)
checkpoint := &CheckpointEventPayload{
Subject: "subject-nara",
SubjectID: types.NaraID("subject-id"),
Observation: NaraObservation{
Restarts: 5,
TotalUptime: 86400,
StartTime: time.Now().Unix() - 86400,
},
VoterIDs: []types.NaraID{voterID}, // This is an ID, not a name!
Signatures: []string{"fake-signature"},
AsOfTime: time.Now().Unix(),
Round: 1,
}
event := SyncEvent{
Timestamp: time.Now().UnixNano(),
Service: ServiceCheckpoint,
Checkpoint: checkpoint,
Emitter: voterName, // Emitter should be the name
}
event.ComputeID()
// Process the event through the discovery pipeline
network.discoverNarasFromEvents([]SyncEvent{event})
// Verify that we did NOT create a ghost nara with the VoterID as name
network.local.mu.Lock()
_, ghostExists := network.Neighbourhood[types.NaraName(voterID)]
_, realExists := network.Neighbourhood[voterName]
network.local.mu.Unlock()
if ghostExists {
t.Errorf("❌ BUG: Created ghost nara with ID as name: %s", voterID)
t.Errorf("This means GetActor() is still returning VoterIDs instead of empty string")
}
if !realExists {
t.Error("Real nara (alice) should still exist in neighbourhood")
}
// Also verify GetActor() returns empty string for checkpoint events
if actor := checkpoint.GetActor(); actor != "" {
t.Errorf("CheckpointEventPayload.GetActor() should return empty string, got: %s", actor)
}
}
// TestCheckpoint_GetActorReturnsEmpty verifies the fix at the interface level
func TestCheckpoint_GetActorReturnsEmpty(t *testing.T) {
t.Parallel()
checkpoint := &CheckpointEventPayload{
Subject: "subject",
SubjectID: types.NaraID("subject-id"),
Observation: NaraObservation{
Restarts: 5,
TotalUptime: 86400,
StartTime: time.Now().Unix(),
},
VoterIDs: []types.NaraID{types.NaraID("voter-id-1"), types.NaraID("voter-id-2")}, // IDs, not names
Signatures: []string{"sig1", "sig2"},
AsOfTime: time.Now().Unix(),
Round: 1,
}
// GetActor() should return empty string to prevent IDs from being used as names
actor := checkpoint.GetActor()
if actor != "" {
t.Errorf("Expected GetActor() to return empty string, got: %s", actor)
t.Error("This would cause VoterIDs (nara IDs) to be treated as names in event processing")
}
// GetTarget() should still work (returns Subject)
target := checkpoint.GetTarget()
if target != "subject" {
t.Errorf("Expected GetTarget() to return 'subject', got: %s", target)
}
}