Skip to content

Commit cc59a51

Browse files
committed
test(peers): add comprehensive peer cache tests
Includes tests for peer retrieval, cache saving with and without a valid path, and cache persistence. Ensures no panic occurs during edge cases.
1 parent 874b0ac commit cc59a51

File tree

1 file changed

+290
-0
lines changed

1 file changed

+290
-0
lines changed

client_peers_test.go

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
package p2p
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
"time"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestClientGetPeersWithTopicPeers(t *testing.T) {
15+
privKey1, err := GeneratePrivateKey()
16+
require.NoError(t, err)
17+
18+
config1 := Config{
19+
Name: "peer1",
20+
PrivateKey: privKey1,
21+
}
22+
23+
client1, err := NewClient(config1)
24+
require.NoError(t, err)
25+
defer func() {
26+
closeErr := client1.Close()
27+
require.NoError(t, closeErr)
28+
}()
29+
30+
// Initially should have no peers
31+
peers := client1.GetPeers()
32+
assert.Empty(t, peers)
33+
34+
// Subscribe to a topic
35+
_ = client1.Subscribe("test-topic")
36+
37+
// Give subscription time to initialize
38+
time.Sleep(100 * time.Millisecond)
39+
40+
// Still should have no peers (no other clients)
41+
peers = client1.GetPeers()
42+
assert.NotNil(t, peers)
43+
}
44+
45+
func TestClientGetPeersReturnsCorrectStructure(t *testing.T) {
46+
privKey, err := GeneratePrivateKey()
47+
require.NoError(t, err)
48+
49+
config := Config{
50+
Name: testPeerName,
51+
PrivateKey: privKey,
52+
}
53+
54+
cl, err := NewClient(config)
55+
require.NoError(t, err)
56+
defer func() {
57+
closeErr := cl.Close()
58+
require.NoError(t, closeErr)
59+
}()
60+
61+
peers := cl.GetPeers()
62+
63+
// Should return empty slice, not nil
64+
assert.NotNil(t, peers)
65+
assert.IsType(t, []PeerInfo{}, peers)
66+
}
67+
68+
func TestClientSavePeerCacheDisabled(t *testing.T) {
69+
privKey, err := GeneratePrivateKey()
70+
require.NoError(t, err)
71+
72+
config := Config{
73+
Name: testPeerName,
74+
PrivateKey: privKey,
75+
PeerCacheFile: "", // Cache disabled
76+
}
77+
78+
cl, err := NewClient(config)
79+
require.NoError(t, err)
80+
defer func() {
81+
closeErr := cl.Close()
82+
require.NoError(t, closeErr)
83+
}()
84+
85+
c := cl.(*client)
86+
87+
// Should not panic when caching is disabled
88+
c.savePeerCache()
89+
}
90+
91+
func TestClientSavePeerCacheEnabled(t *testing.T) {
92+
privKey, err := GeneratePrivateKey()
93+
require.NoError(t, err)
94+
95+
cacheFile := filepath.Join(t.TempDir(), "peers_save.json")
96+
97+
config := Config{
98+
Name: testPeerName,
99+
PrivateKey: privKey,
100+
PeerCacheFile: cacheFile,
101+
}
102+
103+
cl, err := NewClient(config)
104+
require.NoError(t, err)
105+
defer func() {
106+
closeErr := cl.Close()
107+
require.NoError(t, closeErr)
108+
}()
109+
110+
c := cl.(*client)
111+
112+
// Subscribe to a topic to initialize topics map
113+
_ = c.Subscribe("test-topic")
114+
time.Sleep(100 * time.Millisecond)
115+
116+
// Call savePeerCache
117+
c.savePeerCache()
118+
119+
// If there are no connected peers, cache file may or may not exist
120+
// This is acceptable behavior - just verify no panic occurred
121+
}
122+
123+
func TestClientSavePeerCacheWithInvalidPath(t *testing.T) {
124+
privKey, err := GeneratePrivateKey()
125+
require.NoError(t, err)
126+
127+
// Use an invalid path that cannot be written
128+
invalidPath := "/nonexistent/directory/peers.json"
129+
130+
config := Config{
131+
Name: testPeerName,
132+
PrivateKey: privKey,
133+
PeerCacheFile: invalidPath,
134+
}
135+
136+
cl, err := NewClient(config)
137+
require.NoError(t, err)
138+
defer func() {
139+
closeErr := cl.Close()
140+
require.NoError(t, closeErr)
141+
}()
142+
143+
c := cl.(*client)
144+
145+
// Subscribe to a topic
146+
_ = c.Subscribe("test-topic")
147+
time.Sleep(50 * time.Millisecond)
148+
149+
// Should not panic even with invalid path
150+
c.savePeerCache()
151+
}
152+
153+
func TestClientPeerCachePersistence(t *testing.T) {
154+
cacheFile := filepath.Join(t.TempDir(), "peers_persist.json")
155+
156+
// Pre-populate cache with a peer
157+
cachedPeers := []cachedPeer{
158+
{
159+
ID: "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N",
160+
Name: "existing-peer",
161+
Addrs: []string{"/ip4/127.0.0.1/tcp/4001"},
162+
LastSeen: time.Now(),
163+
},
164+
}
165+
166+
data, err := json.MarshalIndent(cachedPeers, "", " ")
167+
require.NoError(t, err)
168+
err = os.WriteFile(cacheFile, data, 0o600)
169+
require.NoError(t, err)
170+
171+
privKey, err := GeneratePrivateKey()
172+
require.NoError(t, err)
173+
174+
config := Config{
175+
Name: testPeerName,
176+
PrivateKey: privKey,
177+
PeerCacheFile: cacheFile,
178+
PeerCacheTTL: 24 * time.Hour,
179+
}
180+
181+
// Create client - should load cached peers
182+
cl, err := NewClient(config)
183+
require.NoError(t, err)
184+
defer func() {
185+
closeErr := cl.Close()
186+
require.NoError(t, closeErr)
187+
}()
188+
189+
// Give time for cache loading
190+
time.Sleep(100 * time.Millisecond)
191+
192+
// Verify cache file still exists
193+
_, err = os.Stat(cacheFile)
194+
require.NoError(t, err)
195+
}
196+
197+
func TestClientSavePeerCacheMergesExistingData(t *testing.T) {
198+
cacheFile := filepath.Join(t.TempDir(), "peers_merge.json")
199+
200+
// Pre-populate cache
201+
existingPeers := []cachedPeer{
202+
{
203+
ID: "QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N",
204+
Name: "old-peer",
205+
Addrs: []string{"/ip4/192.168.1.1/tcp/4001"},
206+
LastSeen: time.Now().Add(-48 * time.Hour),
207+
},
208+
}
209+
210+
data, err := json.MarshalIndent(existingPeers, "", " ")
211+
require.NoError(t, err)
212+
err = os.WriteFile(cacheFile, data, 0o600)
213+
require.NoError(t, err)
214+
215+
privKey, err := GeneratePrivateKey()
216+
require.NoError(t, err)
217+
218+
config := Config{
219+
Name: testPeerName,
220+
PrivateKey: privKey,
221+
PeerCacheFile: cacheFile,
222+
}
223+
224+
cl, err := NewClient(config)
225+
require.NoError(t, err)
226+
defer func() {
227+
closeErr := cl.Close()
228+
require.NoError(t, closeErr)
229+
}()
230+
231+
c := cl.(*client)
232+
233+
// Subscribe to trigger topic creation
234+
_ = c.Subscribe("test-topic")
235+
time.Sleep(50 * time.Millisecond)
236+
237+
// Save cache
238+
c.savePeerCache()
239+
}
240+
241+
func TestClientGetPeersConcurrency(t *testing.T) {
242+
privKey, err := GeneratePrivateKey()
243+
require.NoError(t, err)
244+
245+
config := Config{
246+
Name: testPeerName,
247+
PrivateKey: privKey,
248+
}
249+
250+
cl, err := NewClient(config)
251+
require.NoError(t, err)
252+
defer func() {
253+
closeErr := cl.Close()
254+
require.NoError(t, closeErr)
255+
}()
256+
257+
// Call GetPeers concurrently from multiple goroutines
258+
done := make(chan bool)
259+
for i := 0; i < 10; i++ {
260+
go func() {
261+
_ = cl.GetPeers()
262+
done <- true
263+
}()
264+
}
265+
266+
// Wait for all goroutines
267+
for i := 0; i < 10; i++ {
268+
<-done
269+
}
270+
}
271+
272+
func TestClientPeerCacheTTLNegativeValue(t *testing.T) {
273+
cacheFile := filepath.Join(t.TempDir(), "peers_ttl.json")
274+
275+
privKey, err := GeneratePrivateKey()
276+
require.NoError(t, err)
277+
278+
config := Config{
279+
Name: testPeerName,
280+
PrivateKey: privKey,
281+
PeerCacheFile: cacheFile,
282+
PeerCacheTTL: -1, // Negative TTL disables eviction
283+
}
284+
285+
cl, err := NewClient(config)
286+
require.NoError(t, err)
287+
288+
err = cl.Close()
289+
require.NoError(t, err)
290+
}

0 commit comments

Comments
 (0)