7
7
"io"
8
8
"net/http"
9
9
"net/http/httptest"
10
+ "regexp"
11
+ "strconv"
10
12
"testing"
11
13
"time"
12
14
@@ -47,6 +49,7 @@ func TestHeaders(t *testing.T) {
47
49
require .Equal (t , 200 , resp .StatusCode )
48
50
header := resp .Header .Get ("Content-Type" )
49
51
require .Equal (t , mediaTypeJSON , header )
52
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
50
53
51
54
resp , err = http .Get (serverAddr + "/routing/v1/providers/" + "BAD_CID" )
52
55
require .NoError (t , err )
@@ -66,6 +69,13 @@ func makePeerID(t *testing.T) (crypto.PrivKey, peer.ID) {
66
69
return sk , pid
67
70
}
68
71
72
+ func requireCloseToNow (t * testing.T , lastModified string ) {
73
+ // inspecting fields like 'Last-Modified' is prone to one-off errors, we test with 1m buffer
74
+ lastModifiedTime , err := time .Parse (http .TimeFormat , lastModified )
75
+ require .NoError (t , err )
76
+ require .WithinDuration (t , time .Now (), lastModifiedTime , 1 * time .Minute )
77
+ }
78
+
69
79
func TestProviders (t * testing.T ) {
70
80
pidStr := "12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn"
71
81
pid2Str := "12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz"
@@ -79,25 +89,31 @@ func TestProviders(t *testing.T) {
79
89
cid , err := cid .Decode (cidStr )
80
90
require .NoError (t , err )
81
91
82
- runTest := func (t * testing.T , contentType string , expectedStream bool , expectedBody string ) {
92
+ runTest := func (t * testing.T , contentType string , empty bool , expectedStream bool , expectedBody string ) {
83
93
t .Parallel ()
84
94
85
- results := iter .FromSlice ([]iter.Result [types.Record ]{
86
- {Val : & types.PeerRecord {
87
- Schema : types .SchemaPeer ,
88
- ID : & pid ,
89
- Protocols : []string {"transport-bitswap" },
90
- Addrs : []types.Multiaddr {},
91
- }},
92
- //lint:ignore SA1019 // ignore staticcheck
93
- {Val : & types.BitswapRecord {
95
+ var results * iter.SliceIter [iter.Result [types.Record ]]
96
+
97
+ if empty {
98
+ results = iter .FromSlice ([]iter.Result [types.Record ]{})
99
+ } else {
100
+ results = iter .FromSlice ([]iter.Result [types.Record ]{
101
+ {Val : & types.PeerRecord {
102
+ Schema : types .SchemaPeer ,
103
+ ID : & pid ,
104
+ Protocols : []string {"transport-bitswap" },
105
+ Addrs : []types.Multiaddr {},
106
+ }},
94
107
//lint:ignore SA1019 // ignore staticcheck
95
- Schema : types .SchemaBitswap ,
96
- ID : & pid2 ,
97
- Protocol : "transport-bitswap" ,
98
- Addrs : []types.Multiaddr {},
99
- }}},
100
- )
108
+ {Val : & types.BitswapRecord {
109
+ //lint:ignore SA1019 // ignore staticcheck
110
+ Schema : types .SchemaBitswap ,
111
+ ID : & pid2 ,
112
+ Protocol : "transport-bitswap" ,
113
+ Addrs : []types.Multiaddr {},
114
+ }}},
115
+ )
116
+ }
101
117
102
118
router := & mockContentRouter {}
103
119
server := httptest .NewServer (Handler (router ))
@@ -117,8 +133,16 @@ func TestProviders(t *testing.T) {
117
133
resp , err := http .DefaultClient .Do (req )
118
134
require .NoError (t , err )
119
135
require .Equal (t , 200 , resp .StatusCode )
120
- header := resp .Header .Get ("Content-Type" )
121
- require .Equal (t , contentType , header )
136
+
137
+ require .Equal (t , contentType , resp .Header .Get ("Content-Type" ))
138
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
139
+
140
+ if empty {
141
+ require .Equal (t , "public, max-age=15, stale-while-revalidate=172800, stale-if-error=172800" , resp .Header .Get ("Cache-Control" ))
142
+ } else {
143
+ require .Equal (t , "public, max-age=300, stale-while-revalidate=172800, stale-if-error=172800" , resp .Header .Get ("Cache-Control" ))
144
+ }
145
+ requireCloseToNow (t , resp .Header .Get ("Last-Modified" ))
122
146
123
147
body , err := io .ReadAll (resp .Body )
124
148
require .NoError (t , err )
@@ -127,11 +151,19 @@ func TestProviders(t *testing.T) {
127
151
}
128
152
129
153
t .Run ("JSON Response" , func (t * testing.T ) {
130
- runTest (t , mediaTypeJSON , false , `{"Providers":[{"Addrs":[],"ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Protocols":["transport-bitswap"],"Schema":"peer"},{"Schema":"bitswap","Protocol":"transport-bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz"}]}` )
154
+ runTest (t , mediaTypeJSON , false , false , `{"Providers":[{"Addrs":[],"ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Protocols":["transport-bitswap"],"Schema":"peer"},{"Schema":"bitswap","Protocol":"transport-bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz"}]}` )
155
+ })
156
+
157
+ t .Run ("Empty JSON Response" , func (t * testing.T ) {
158
+ runTest (t , mediaTypeJSON , true , false , `{"Providers":null}` )
131
159
})
132
160
133
161
t .Run ("NDJSON Response" , func (t * testing.T ) {
134
- runTest (t , mediaTypeNDJSON , true , `{"Addrs":[],"ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Protocols":["transport-bitswap"],"Schema":"peer"}` + "\n " + `{"Schema":"bitswap","Protocol":"transport-bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz"}` + "\n " )
162
+ runTest (t , mediaTypeNDJSON , false , true , `{"Addrs":[],"ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vn","Protocols":["transport-bitswap"],"Schema":"peer"}` + "\n " + `{"Schema":"bitswap","Protocol":"transport-bitswap","ID":"12D3KooWM8sovaEGU1bmiWGWAzvs47DEcXKZZTuJnpQyVTkRs2Vz"}` + "\n " )
163
+ })
164
+
165
+ t .Run ("Empty NDJSON Response" , func (t * testing.T ) {
166
+ runTest (t , mediaTypeNDJSON , true , true , "" )
135
167
})
136
168
}
137
169
@@ -155,7 +187,26 @@ func TestPeers(t *testing.T) {
155
187
require .Equal (t , 400 , resp .StatusCode )
156
188
})
157
189
158
- t .Run ("GET /routing/v1/peers/{cid-libp2p-key-peer-id} returns 200 with correct body (JSON)" , func (t * testing.T ) {
190
+ t .Run ("GET /routing/v1/peers/{cid-libp2p-key-peer-id} returns 200 with correct body and headers (No Results, JSON)" , func (t * testing.T ) {
191
+ t .Parallel ()
192
+
193
+ _ , pid := makePeerID (t )
194
+ results := iter .FromSlice ([]iter.Result [* types.PeerRecord ]{})
195
+
196
+ router := & mockContentRouter {}
197
+ router .On ("FindPeers" , mock .Anything , pid , 20 ).Return (results , nil )
198
+
199
+ resp := makeRequest (t , router , mediaTypeJSON , peer .ToCid (pid ).String ())
200
+ require .Equal (t , 200 , resp .StatusCode )
201
+
202
+ require .Equal (t , mediaTypeJSON , resp .Header .Get ("Content-Type" ))
203
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
204
+ require .Equal (t , "public, max-age=15, stale-while-revalidate=172800, stale-if-error=172800" , resp .Header .Get ("Cache-Control" ))
205
+
206
+ requireCloseToNow (t , resp .Header .Get ("Last-Modified" ))
207
+ })
208
+
209
+ t .Run ("GET /routing/v1/peers/{cid-libp2p-key-peer-id} returns 200 with correct body and headers (JSON)" , func (t * testing.T ) {
159
210
t .Parallel ()
160
211
161
212
_ , pid := makePeerID (t )
@@ -181,8 +232,11 @@ func TestPeers(t *testing.T) {
181
232
resp := makeRequest (t , router , mediaTypeJSON , libp2pKeyCID )
182
233
require .Equal (t , 200 , resp .StatusCode )
183
234
184
- header := resp .Header .Get ("Content-Type" )
185
- require .Equal (t , mediaTypeJSON , header )
235
+ require .Equal (t , mediaTypeJSON , resp .Header .Get ("Content-Type" ))
236
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
237
+ require .Equal (t , "public, max-age=300, stale-while-revalidate=172800, stale-if-error=172800" , resp .Header .Get ("Cache-Control" ))
238
+
239
+ requireCloseToNow (t , resp .Header .Get ("Last-Modified" ))
186
240
187
241
body , err := io .ReadAll (resp .Body )
188
242
require .NoError (t , err )
@@ -191,7 +245,26 @@ func TestPeers(t *testing.T) {
191
245
require .Equal (t , expectedBody , string (body ))
192
246
})
193
247
194
- t .Run ("GET /routing/v1/peers/{cid-libp2p-key-peer-id} returns 200 with correct body (NDJSON)" , func (t * testing.T ) {
248
+ t .Run ("GET /routing/v1/peers/{cid-libp2p-key-peer-id} returns 200 with correct body and headers (No Results, NDJSON)" , func (t * testing.T ) {
249
+ t .Parallel ()
250
+
251
+ _ , pid := makePeerID (t )
252
+ results := iter .FromSlice ([]iter.Result [* types.PeerRecord ]{})
253
+
254
+ router := & mockContentRouter {}
255
+ router .On ("FindPeers" , mock .Anything , pid , 0 ).Return (results , nil )
256
+
257
+ resp := makeRequest (t , router , mediaTypeNDJSON , peer .ToCid (pid ).String ())
258
+ require .Equal (t , 200 , resp .StatusCode )
259
+
260
+ require .Equal (t , mediaTypeNDJSON , resp .Header .Get ("Content-Type" ))
261
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
262
+ require .Equal (t , "public, max-age=15, stale-while-revalidate=172800, stale-if-error=172800" , resp .Header .Get ("Cache-Control" ))
263
+
264
+ requireCloseToNow (t , resp .Header .Get ("Last-Modified" ))
265
+ })
266
+
267
+ t .Run ("GET /routing/v1/peers/{cid-libp2p-key-peer-id} returns 200 with correct body and headers (NDJSON)" , func (t * testing.T ) {
195
268
t .Parallel ()
196
269
197
270
_ , pid := makePeerID (t )
@@ -217,8 +290,9 @@ func TestPeers(t *testing.T) {
217
290
resp := makeRequest (t , router , mediaTypeNDJSON , libp2pKeyCID )
218
291
require .Equal (t , 200 , resp .StatusCode )
219
292
220
- header := resp .Header .Get ("Content-Type" )
221
- require .Equal (t , mediaTypeNDJSON , header )
293
+ require .Equal (t , mediaTypeNDJSON , resp .Header .Get ("Content-Type" ))
294
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
295
+ require .Equal (t , "public, max-age=300, stale-while-revalidate=172800, stale-if-error=172800" , resp .Header .Get ("Cache-Control" ))
222
296
223
297
body , err := io .ReadAll (resp .Body )
224
298
require .NoError (t , err )
@@ -254,6 +328,7 @@ func TestPeers(t *testing.T) {
254
328
require .Equal (t , 200 , resp .StatusCode )
255
329
256
330
header := resp .Header .Get ("Content-Type" )
331
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
257
332
require .Equal (t , mediaTypeJSON , header )
258
333
259
334
body , err := io .ReadAll (resp .Body )
@@ -290,6 +365,7 @@ func TestPeers(t *testing.T) {
290
365
require .Equal (t , 200 , resp .StatusCode )
291
366
292
367
header := resp .Header .Get ("Content-Type" )
368
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
293
369
require .Equal (t , mediaTypeNDJSON , header )
294
370
295
371
body , err := io .ReadAll (resp .Body )
@@ -306,10 +382,8 @@ func makeName(t *testing.T) (crypto.PrivKey, ipns.Name) {
306
382
return sk , ipns .NameFromPeer (pid )
307
383
}
308
384
309
- func makeIPNSRecord (t * testing.T , cid cid.Cid , sk crypto.PrivKey , opts ... ipns.Option ) (* ipns.Record , []byte ) {
385
+ func makeIPNSRecord (t * testing.T , cid cid.Cid , eol time. Time , ttl time. Duration , sk crypto.PrivKey , opts ... ipns.Option ) (* ipns.Record , []byte ) {
310
386
path := path .FromCid (cid )
311
- eol := time .Now ().Add (time .Hour * 48 )
312
- ttl := time .Second * 20
313
387
314
388
record , err := ipns .NewRecord (sk , path , 1 , eol , ttl , opts ... )
315
389
require .NoError (t , err )
@@ -339,7 +413,18 @@ func TestIPNS(t *testing.T) {
339
413
340
414
runWithRecordOptions := func (t * testing.T , opts ... ipns.Option ) {
341
415
sk , name1 := makeName (t )
342
- record1 , rawRecord1 := makeIPNSRecord (t , cid1 , sk )
416
+ now := time .Now ()
417
+ eol := now .Add (24 * time .Hour * 7 ) // record valid for a week
418
+ ttl := 42 * time .Second // distinct TTL
419
+ record1 , rawRecord1 := makeIPNSRecord (t , cid1 , eol , ttl , sk )
420
+
421
+ stringToDuration := func (s string ) time.Duration {
422
+ seconds , err := strconv .Atoi (s )
423
+ if err != nil {
424
+ return 0
425
+ }
426
+ return time .Duration (seconds ) * time .Second
427
+ }
343
428
344
429
_ , name2 := makeName (t )
345
430
@@ -355,8 +440,25 @@ func TestIPNS(t *testing.T) {
355
440
resp := makeRequest (t , router , "/routing/v1/ipns/" + name1 .String ())
356
441
require .Equal (t , 200 , resp .StatusCode )
357
442
require .Equal (t , mediaTypeIPNSRecord , resp .Header .Get ("Content-Type" ))
443
+ require .Equal (t , "Accept" , resp .Header .Get ("Vary" ))
358
444
require .NotEmpty (t , resp .Header .Get ("Etag" ))
359
- require .Equal (t , "max-age=20" , resp .Header .Get ("Cache-Control" ))
445
+
446
+ requireCloseToNow (t , resp .Header .Get ("Last-Modified" ))
447
+
448
+ require .Contains (t , resp .Header .Get ("Cache-Control" ), "public, max-age=42" )
449
+
450
+ // expected "stale" values are int(time.Until(eol).Seconds())
451
+ // but running test on slow machine may be off by a few seconds
452
+ // and we need to assert with some room for drift (1 minute just to not break any CI)
453
+ re := regexp .MustCompile (`(?:^|,\s*)(max-age|stale-while-revalidate|stale-if-error)=(\d+)` )
454
+ matches := re .FindAllStringSubmatch (resp .Header .Get ("Cache-Control" ), - 1 )
455
+ staleWhileRevalidate := stringToDuration (matches [1 ][2 ])
456
+ staleWhileError := stringToDuration (matches [2 ][2 ])
457
+ require .WithinDuration (t , eol , time .Now ().Add (staleWhileRevalidate ), 1 * time .Minute )
458
+ require .WithinDuration (t , eol , time .Now ().Add (staleWhileError ), 1 * time .Minute )
459
+
460
+ // 'Expires' on IPNS result is expected to match EOL of IPNS Record with ValidityType=0
461
+ require .Equal (t , eol .UTC ().Format (http .TimeFormat ), resp .Header .Get ("Expires" ))
360
462
361
463
body , err := io .ReadAll (resp .Body )
362
464
require .NoError (t , err )
0 commit comments