master
go 732 lines 30.7 KB
Raw
1 package autoconf
2
3 import (
4 "encoding/json"
5 "net/http"
6 "net/http/httptest"
7 "os"
8 "testing"
9 "time"
10
11 "github.com/ipfs/kubo/test/cli/harness"
12 "github.com/stretchr/testify/assert"
13 "github.com/stretchr/testify/require"
14 )
15
16 func TestAutoConfExpand(t *testing.T) {
17 t.Parallel()
18
19 t.Run("config commands show auto values", func(t *testing.T) {
20 t.Parallel()
21 testConfigCommandsShowAutoValues(t)
22 })
23
24 t.Run("mixed configuration preserves both auto and static", func(t *testing.T) {
25 t.Parallel()
26 testMixedConfigurationPreserved(t)
27 })
28
29 t.Run("config replace preserves auto values", func(t *testing.T) {
30 t.Parallel()
31 testConfigReplacePreservesAuto(t)
32 })
33
34 t.Run("expand-auto filters unsupported URL paths with delegated routing", func(t *testing.T) {
35 t.Parallel()
36 testExpandAutoFiltersUnsupportedPathsDelegated(t)
37 })
38
39 t.Run("expand-auto with auto routing uses NewRoutingSystem", func(t *testing.T) {
40 t.Parallel()
41 testExpandAutoWithAutoRouting(t)
42 })
43
44 t.Run("expand-auto with auto routing shows AminoDHT native vs IPNI delegated", func(t *testing.T) {
45 t.Parallel()
46 testExpandAutoWithMixedSystems(t)
47 })
48
49 t.Run("expand-auto filters paths with NewRoutingSystem and auto routing", func(t *testing.T) {
50 t.Parallel()
51 testExpandAutoWithFiltering(t)
52 })
53
54 t.Run("expand-auto falls back to defaults without cache (delegated)", func(t *testing.T) {
55 t.Parallel()
56 testExpandAutoWithoutCacheDelegated(t)
57 })
58
59 t.Run("expand-auto with auto routing without cache", func(t *testing.T) {
60 t.Parallel()
61 testExpandAutoWithoutCacheAuto(t)
62 })
63 }
64
65 func testConfigCommandsShowAutoValues(t *testing.T) {
66 // Create IPFS node
67 node := harness.NewT(t).NewNode().Init("--profile=test")
68
69 // Set all fields to "auto"
70 node.SetIPFSConfig("Bootstrap", []string{"auto"})
71 node.SetIPFSConfig("DNS.Resolvers", map[string]string{"foo.": "auto"})
72 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
73 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
74
75 // Test individual field queries
76 t.Run("Bootstrap shows auto", func(t *testing.T) {
77 result := node.RunIPFS("config", "Bootstrap")
78 require.Equal(t, 0, result.ExitCode())
79
80 var bootstrap []string
81 err := json.Unmarshal([]byte(result.Stdout.String()), &bootstrap)
82 require.NoError(t, err)
83 assert.Equal(t, []string{"auto"}, bootstrap)
84 })
85
86 t.Run("DNS.Resolvers shows auto", func(t *testing.T) {
87 result := node.RunIPFS("config", "DNS.Resolvers")
88 require.Equal(t, 0, result.ExitCode())
89
90 var resolvers map[string]string
91 err := json.Unmarshal([]byte(result.Stdout.String()), &resolvers)
92 require.NoError(t, err)
93 assert.Equal(t, map[string]string{"foo.": "auto"}, resolvers)
94 })
95
96 t.Run("Routing.DelegatedRouters shows auto", func(t *testing.T) {
97 result := node.RunIPFS("config", "Routing.DelegatedRouters")
98 require.Equal(t, 0, result.ExitCode())
99
100 var routers []string
101 err := json.Unmarshal([]byte(result.Stdout.String()), &routers)
102 require.NoError(t, err)
103 assert.Equal(t, []string{"auto"}, routers)
104 })
105
106 t.Run("Ipns.DelegatedPublishers shows auto", func(t *testing.T) {
107 result := node.RunIPFS("config", "Ipns.DelegatedPublishers")
108 require.Equal(t, 0, result.ExitCode())
109
110 var publishers []string
111 err := json.Unmarshal([]byte(result.Stdout.String()), &publishers)
112 require.NoError(t, err)
113 assert.Equal(t, []string{"auto"}, publishers)
114 })
115
116 t.Run("config show contains all auto values", func(t *testing.T) {
117 result := node.RunIPFS("config", "show")
118 require.Equal(t, 0, result.ExitCode())
119
120 output := result.Stdout.String()
121
122 // Check that auto values are present in the full config
123 assert.Contains(t, output, `"Bootstrap": [
124 "auto"
125 ]`, "Bootstrap should contain auto")
126
127 assert.Contains(t, output, `"DNS": {
128 "Resolvers": {
129 "foo.": "auto"
130 }
131 }`, "DNS.Resolvers should contain auto")
132
133 assert.Contains(t, output, `"DelegatedRouters": [
134 "auto"
135 ]`, "Routing.DelegatedRouters should contain auto")
136
137 assert.Contains(t, output, `"DelegatedPublishers": [
138 "auto"
139 ]`, "Ipns.DelegatedPublishers should contain auto")
140 })
141
142 // Test with autoconf server for --expand-auto functionality
143 t.Run("config with --expand-auto expands auto values", func(t *testing.T) {
144 // Load test autoconf data
145 autoConfData := loadTestDataExpand(t, "valid_autoconf.json")
146
147 // Create HTTP server that serves autoconf.json
148 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
149 w.Header().Set("Content-Type", "application/json")
150 _, _ = w.Write(autoConfData)
151 }))
152 defer server.Close()
153
154 // Configure autoconf for the node
155 node.SetIPFSConfig("AutoConf.URL", server.URL)
156 node.SetIPFSConfig("AutoConf.Enabled", true)
157
158 // Test Bootstrap field expansion
159 result := node.RunIPFS("config", "Bootstrap", "--expand-auto")
160 require.Equal(t, 0, result.ExitCode(), "config Bootstrap --expand-auto should succeed")
161
162 var expandedBootstrap []string
163 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedBootstrap)
164 require.NoError(t, err)
165 assert.NotContains(t, expandedBootstrap, "auto", "Expanded bootstrap should not contain 'auto'")
166 assert.Greater(t, len(expandedBootstrap), 0, "Expanded bootstrap should contain expanded peers")
167
168 // Test DNS.Resolvers field expansion
169 result = node.RunIPFS("config", "DNS.Resolvers", "--expand-auto")
170 require.Equal(t, 0, result.ExitCode(), "config DNS.Resolvers --expand-auto should succeed")
171
172 var expandedResolvers map[string]string
173 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedResolvers)
174 require.NoError(t, err)
175 assert.NotEqual(t, "auto", expandedResolvers["foo."], "Expanded DNS resolver should not be 'auto'")
176
177 // Test Routing.DelegatedRouters field expansion
178 result = node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
179 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
180
181 var expandedRouters []string
182 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
183 require.NoError(t, err)
184 assert.NotContains(t, expandedRouters, "auto", "Expanded routers should not contain 'auto'")
185
186 // Test Ipns.DelegatedPublishers field expansion
187 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
188 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
189
190 var expandedPublishers []string
191 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
192 require.NoError(t, err)
193 assert.NotContains(t, expandedPublishers, "auto", "Expanded publishers should not contain 'auto'")
194
195 // Test config show --expand-auto (full config expansion)
196 result = node.RunIPFS("config", "show", "--expand-auto")
197 require.Equal(t, 0, result.ExitCode(), "config show --expand-auto should succeed")
198
199 expandedOutput := result.Stdout.String()
200 t.Logf("Expanded config output contains: %d characters", len(expandedOutput))
201
202 // Verify that auto values are expanded in the full config
203 assert.NotContains(t, expandedOutput, `"auto"`, "Expanded config should not contain literal 'auto' values")
204 assert.Contains(t, expandedOutput, `"Bootstrap"`, "Expanded config should contain Bootstrap section")
205 assert.Contains(t, expandedOutput, `"DNS"`, "Expanded config should contain DNS section")
206 })
207 }
208
209 func testMixedConfigurationPreserved(t *testing.T) {
210 // Create IPFS node
211 node := harness.NewT(t).NewNode().Init("--profile=test")
212
213 // Set mixed configuration
214 node.SetIPFSConfig("Bootstrap", []string{
215 "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWTest",
216 "auto",
217 "/ip4/127.0.0.2/tcp/4001/p2p/12D3KooWTest2",
218 })
219
220 node.SetIPFSConfig("DNS.Resolvers", map[string]string{
221 "eth.": "https://eth.resolver",
222 "foo.": "auto",
223 "bar.": "https://bar.resolver",
224 })
225
226 node.SetIPFSConfig("Routing.DelegatedRouters", []string{
227 "https://static.router",
228 "auto",
229 })
230
231 // Verify Bootstrap preserves order and mixes auto with static
232 result := node.RunIPFS("config", "Bootstrap")
233 require.Equal(t, 0, result.ExitCode())
234
235 var bootstrap []string
236 err := json.Unmarshal([]byte(result.Stdout.String()), &bootstrap)
237 require.NoError(t, err)
238 assert.Equal(t, []string{
239 "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWTest",
240 "auto",
241 "/ip4/127.0.0.2/tcp/4001/p2p/12D3KooWTest2",
242 }, bootstrap)
243
244 // Verify DNS.Resolvers preserves both auto and static
245 result = node.RunIPFS("config", "DNS.Resolvers")
246 require.Equal(t, 0, result.ExitCode())
247
248 var resolvers map[string]string
249 err = json.Unmarshal([]byte(result.Stdout.String()), &resolvers)
250 require.NoError(t, err)
251 assert.Equal(t, "https://eth.resolver", resolvers["eth."])
252 assert.Equal(t, "auto", resolvers["foo."])
253 assert.Equal(t, "https://bar.resolver", resolvers["bar."])
254
255 // Verify Routing.DelegatedRouters preserves order
256 result = node.RunIPFS("config", "Routing.DelegatedRouters")
257 require.Equal(t, 0, result.ExitCode())
258
259 var routers []string
260 err = json.Unmarshal([]byte(result.Stdout.String()), &routers)
261 require.NoError(t, err)
262 assert.Equal(t, []string{
263 "https://static.router",
264 "auto",
265 }, routers)
266 }
267
268 func testConfigReplacePreservesAuto(t *testing.T) {
269 // Create IPFS node
270 h := harness.NewT(t)
271 node := h.NewNode().Init("--profile=test")
272
273 // Set initial auto values
274 node.SetIPFSConfig("Bootstrap", []string{"auto"})
275 node.SetIPFSConfig("DNS.Resolvers", map[string]string{"foo.": "auto"})
276
277 // Export current config
278 result := node.RunIPFS("config", "show")
279 require.Equal(t, 0, result.ExitCode())
280 originalConfig := result.Stdout.String()
281
282 // Verify auto values are in the exported config
283 assert.Contains(t, originalConfig, `"Bootstrap": [
284 "auto"
285 ]`)
286 assert.Contains(t, originalConfig, `"foo.": "auto"`)
287
288 // Modify the config string to add a new field but preserve auto values
289 var configMap map[string]any
290 err := json.Unmarshal([]byte(originalConfig), &configMap)
291 require.NoError(t, err)
292
293 // Add a new field
294 configMap["NewTestField"] = "test-value"
295
296 // Marshal back to JSON
297 modifiedConfig, err := json.MarshalIndent(configMap, "", " ")
298 require.NoError(t, err)
299
300 // Write config to file and replace
301 configFile := h.WriteToTemp(string(modifiedConfig))
302 replaceResult := node.RunIPFS("config", "replace", configFile)
303 if replaceResult.ExitCode() != 0 {
304 t.Logf("Config replace failed: stdout=%s, stderr=%s", replaceResult.Stdout.String(), replaceResult.Stderr.String())
305 }
306 require.Equal(t, 0, replaceResult.ExitCode())
307
308 // Verify auto values are still present after replace
309 result = node.RunIPFS("config", "Bootstrap")
310 require.Equal(t, 0, result.ExitCode())
311
312 var bootstrap []string
313 err = json.Unmarshal([]byte(result.Stdout.String()), &bootstrap)
314 require.NoError(t, err)
315 assert.Equal(t, []string{"auto"}, bootstrap, "Bootstrap should still contain auto after config replace")
316
317 // Verify DNS resolver config is preserved after replace
318 result = node.RunIPFS("config", "DNS.Resolvers")
319 require.Equal(t, 0, result.ExitCode())
320
321 var resolvers map[string]string
322 err = json.Unmarshal([]byte(result.Stdout.String()), &resolvers)
323 require.NoError(t, err)
324 assert.Equal(t, "auto", resolvers["foo."], "DNS resolver for foo. should still be auto after config replace")
325 }
326
327 func testExpandAutoFiltersUnsupportedPathsDelegated(t *testing.T) {
328 // Test scenario: CLI with daemon started and autoconf cached using delegated routing
329 // This tests the production scenario where delegated routing is enabled and
330 // daemon has fetched and cached autoconf data, and CLI commands read from that cache
331
332 // Create IPFS node
333 node := harness.NewT(t).NewNode().Init("--profile=test")
334
335 // Configure delegated routing to use autoconf URLs
336 node.SetIPFSConfig("Routing.Type", "delegated")
337 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
338 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
339 // Disable content providing when using delegated routing
340 node.SetIPFSConfig("Provide.Enabled", false)
341 node.SetIPFSConfig("Provide.DHT.Interval", "0")
342
343 // Load test autoconf data with unsupported paths
344 autoConfData := loadTestDataExpand(t, "autoconf_with_unsupported_paths.json")
345
346 // Create HTTP server that serves autoconf.json with unsupported paths
347 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
348 w.Header().Set("Content-Type", "application/json")
349 _, _ = w.Write(autoConfData)
350 }))
351 defer server.Close()
352
353 // Configure autoconf for the node
354 node.SetIPFSConfig("AutoConf.URL", server.URL)
355 node.SetIPFSConfig("AutoConf.Enabled", true)
356
357 // Verify the autoconf URL is set correctly
358 result := node.RunIPFS("config", "AutoConf.URL")
359 require.Equal(t, 0, result.ExitCode(), "config AutoConf.URL should succeed")
360 t.Logf("AutoConf URL is set to: %s", result.Stdout.String())
361 assert.Contains(t, result.Stdout.String(), "127.0.0.1", "AutoConf URL should contain the test server address")
362
363 // Start daemon to fetch and cache autoconf data
364 t.Log("Starting daemon to fetch and cache autoconf data...")
365 daemon := node.StartDaemon()
366 defer daemon.StopDaemon()
367
368 // Wait for autoconf fetch (use autoconf default timeout + buffer)
369 time.Sleep(6 * time.Second) // defaultTimeout is 5s, add 1s buffer
370 t.Log("AutoConf should now be cached by daemon")
371
372 // Test Routing.DelegatedRouters field expansion filters unsupported paths
373 result = node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
374 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
375
376 var expandedRouters []string
377 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
378 require.NoError(t, err)
379
380 // After cache prewarming, should get URLs from autoconf that have supported paths
381 assert.Contains(t, expandedRouters, "https://supported.example.com/routing/v1/providers", "Should contain supported provider URL")
382 assert.Contains(t, expandedRouters, "https://supported.example.com/routing/v1/peers", "Should contain supported peers URL")
383 assert.Contains(t, expandedRouters, "https://mixed.example.com/routing/v1/providers", "Should contain mixed provider URL")
384 assert.Contains(t, expandedRouters, "https://mixed.example.com/routing/v1/peers", "Should contain mixed peers URL")
385
386 // Verify unsupported URLs from autoconf are filtered out (not in result)
387 assert.NotContains(t, expandedRouters, "https://unsupported.example.com/example/v0/read", "Should filter out unsupported path /example/v0/read")
388 assert.NotContains(t, expandedRouters, "https://unsupported.example.com/api/v1/custom", "Should filter out unsupported path /api/v1/custom")
389 assert.NotContains(t, expandedRouters, "https://mixed.example.com/unsupported/path", "Should filter out unsupported path /unsupported/path")
390
391 t.Logf("Filtered routers: %v", expandedRouters)
392
393 // Test Ipns.DelegatedPublishers field expansion filters unsupported paths
394 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
395 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
396
397 var expandedPublishers []string
398 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
399 require.NoError(t, err)
400
401 // After cache prewarming, should get URLs from autoconf that have supported paths
402 assert.Contains(t, expandedPublishers, "https://supported.example.com/routing/v1/ipns", "Should contain supported IPNS URL")
403 assert.Contains(t, expandedPublishers, "https://mixed.example.com/routing/v1/ipns", "Should contain mixed IPNS URL")
404
405 // Verify unsupported URLs from autoconf are filtered out (not in result)
406 assert.NotContains(t, expandedPublishers, "https://unsupported.example.com/example/v0/write", "Should filter out unsupported write path")
407
408 t.Logf("Filtered publishers: %v", expandedPublishers)
409 }
410
411 func testExpandAutoWithoutCacheDelegated(t *testing.T) {
412 // Test scenario: CLI without daemon ever starting (no cached autoconf) using delegated routing
413 // This tests the fallback scenario where delegated routing is configured but CLI commands
414 // cannot read from cache and must fall back to hardcoded defaults
415
416 // Create IPFS node but DO NOT start daemon
417 node := harness.NewT(t).NewNode().Init("--profile=test")
418
419 // Configure delegated routing to use autoconf URLs (but no daemon to fetch them)
420 node.SetIPFSConfig("Routing.Type", "delegated")
421 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
422 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
423 // Disable content providing when using delegated routing
424 node.SetIPFSConfig("Provide.Enabled", false)
425 node.SetIPFSConfig("Provide.DHT.Interval", "0")
426
427 // Load test autoconf data with unsupported paths (this won't be used since no daemon)
428 autoConfData := loadTestDataExpand(t, "autoconf_with_unsupported_paths.json")
429
430 // Create HTTP server that serves autoconf.json with unsupported paths
431 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
432 w.Header().Set("Content-Type", "application/json")
433 _, _ = w.Write(autoConfData)
434 }))
435 defer server.Close()
436
437 // Configure autoconf for the node (but daemon never starts to fetch it)
438 node.SetIPFSConfig("AutoConf.URL", server.URL)
439 node.SetIPFSConfig("AutoConf.Enabled", true)
440
441 // Test Routing.DelegatedRouters field expansion without cached autoconf
442 result := node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
443 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
444
445 var expandedRouters []string
446 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
447 require.NoError(t, err)
448
449 // Without cached autoconf, should get fallback URLs from GetMainnetFallbackConfig()
450 // NOTE: These values may change if autoconf library updates GetMainnetFallbackConfig()
451 assert.Contains(t, expandedRouters, "https://cid.contact/routing/v1/providers", "Should contain fallback provider URL from GetMainnetFallbackConfig()")
452
453 t.Logf("Fallback routers (no cache): %v", expandedRouters)
454
455 // Test Ipns.DelegatedPublishers field expansion without cached autoconf
456 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
457 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
458
459 var expandedPublishers []string
460 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
461 require.NoError(t, err)
462
463 // Without cached autoconf, should get fallback IPNS publishers from GetMainnetFallbackConfig()
464 // NOTE: These values may change if autoconf library updates GetMainnetFallbackConfig()
465 assert.Contains(t, expandedPublishers, "https://delegated-ipfs.dev/routing/v1/ipns", "Should contain fallback IPNS URL from GetMainnetFallbackConfig()")
466
467 t.Logf("Fallback publishers (no cache): %v", expandedPublishers)
468 }
469
470 func testExpandAutoWithAutoRouting(t *testing.T) {
471 // Test scenario: CLI with daemon started using auto routing with NewRoutingSystem
472 // This tests that non-native systems (NewRoutingSystem) ARE delegated even with auto routing
473 // Only native systems like AminoDHT are handled internally with auto routing
474
475 // Create IPFS node
476 node := harness.NewT(t).NewNode().Init("--profile=test")
477
478 // Configure auto routing with non-native system
479 node.SetIPFSConfig("Routing.Type", "auto")
480 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
481 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
482
483 // Load test autoconf data with NewRoutingSystem (non-native, will be delegated)
484 autoConfData := loadTestDataExpand(t, "autoconf_new_routing_system.json")
485
486 // Create HTTP server that serves autoconf.json
487 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
488 w.Header().Set("Content-Type", "application/json")
489 _, _ = w.Write(autoConfData)
490 }))
491 defer server.Close()
492
493 // Configure autoconf for the node
494 node.SetIPFSConfig("AutoConf.URL", server.URL)
495 node.SetIPFSConfig("AutoConf.Enabled", true)
496
497 // Start daemon to fetch and cache autoconf data
498 t.Log("Starting daemon to fetch and cache autoconf data...")
499 daemon := node.StartDaemon()
500 defer daemon.StopDaemon()
501
502 // Wait for autoconf fetch (use autoconf default timeout + buffer)
503 time.Sleep(6 * time.Second) // defaultTimeout is 5s, add 1s buffer
504 t.Log("AutoConf should now be cached by daemon")
505
506 // Test Routing.DelegatedRouters field expansion with auto routing
507 result := node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
508 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
509
510 var expandedRouters []string
511 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
512 require.NoError(t, err)
513
514 // With auto routing and NewRoutingSystem (non-native), delegated endpoints should be populated
515 assert.Contains(t, expandedRouters, "https://new-routing.example.com/routing/v1/providers", "Should contain NewRoutingSystem provider URL")
516 assert.Contains(t, expandedRouters, "https://new-routing.example.com/routing/v1/peers", "Should contain NewRoutingSystem peers URL")
517
518 t.Logf("Auto routing routers (NewRoutingSystem delegated): %v", expandedRouters)
519
520 // Test Ipns.DelegatedPublishers field expansion with auto routing
521 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
522 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
523
524 var expandedPublishers []string
525 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
526 require.NoError(t, err)
527
528 // With auto routing and NewRoutingSystem (non-native), delegated publishers should be populated
529 assert.Contains(t, expandedPublishers, "https://new-routing.example.com/routing/v1/ipns", "Should contain NewRoutingSystem IPNS URL")
530
531 t.Logf("Auto routing publishers (NewRoutingSystem delegated): %v", expandedPublishers)
532 }
533
534 func testExpandAutoWithMixedSystems(t *testing.T) {
535 // Test scenario: Auto routing with both AminoDHT (native) and IPNI (delegated) systems
536 // This explicitly confirms that AminoDHT is NOT delegated but IPNI at cid.contact IS delegated
537
538 // Create IPFS node
539 node := harness.NewT(t).NewNode().Init("--profile=test")
540
541 // Configure auto routing
542 node.SetIPFSConfig("Routing.Type", "auto")
543 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
544 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
545
546 // Load test autoconf data with both AminoDHT and IPNI systems
547 autoConfData := loadTestDataExpand(t, "autoconf_amino_and_ipni.json")
548
549 // Create HTTP server that serves autoconf.json
550 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
551 w.Header().Set("Content-Type", "application/json")
552 _, _ = w.Write(autoConfData)
553 }))
554 defer server.Close()
555
556 // Configure autoconf for the node
557 node.SetIPFSConfig("AutoConf.URL", server.URL)
558 node.SetIPFSConfig("AutoConf.Enabled", true)
559
560 // Start daemon to fetch and cache autoconf data
561 t.Log("Starting daemon to fetch and cache autoconf data...")
562 daemon := node.StartDaemon()
563 defer daemon.StopDaemon()
564
565 // Wait for autoconf fetch (use autoconf default timeout + buffer)
566 time.Sleep(6 * time.Second) // defaultTimeout is 5s, add 1s buffer
567 t.Log("AutoConf should now be cached by daemon")
568
569 // Test Routing.DelegatedRouters field expansion
570 result := node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
571 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
572
573 var expandedRouters []string
574 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
575 require.NoError(t, err)
576
577 // With auto routing: AminoDHT (native) should NOT be delegated, IPNI should be delegated
578 assert.Contains(t, expandedRouters, "https://cid.contact/routing/v1/providers", "Should contain IPNI provider URL (delegated)")
579 assert.NotContains(t, expandedRouters, "https://amino-dht.example.com", "Should NOT contain AminoDHT URLs (native)")
580
581 t.Logf("Mixed systems routers (IPNI delegated, AminoDHT native): %v", expandedRouters)
582
583 // Test Ipns.DelegatedPublishers field expansion
584 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
585 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
586
587 var expandedPublishers []string
588 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
589 require.NoError(t, err)
590
591 // IPNI system doesn't have write endpoints, so publishers should be empty
592 // (or contain other systems if they have write endpoints)
593 t.Logf("Mixed systems publishers (IPNI has no write endpoints): %v", expandedPublishers)
594 }
595
596 func testExpandAutoWithFiltering(t *testing.T) {
597 // Test scenario: Auto routing with NewRoutingSystem and path filtering
598 // This tests that path filtering works for delegated systems even with auto routing
599
600 // Create IPFS node
601 node := harness.NewT(t).NewNode().Init("--profile=test")
602
603 // Configure auto routing
604 node.SetIPFSConfig("Routing.Type", "auto")
605 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
606 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
607
608 // Load test autoconf data with NewRoutingSystem and mixed valid/invalid paths
609 autoConfData := loadTestDataExpand(t, "autoconf_new_routing_with_filtering.json")
610
611 // Create HTTP server that serves autoconf.json
612 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
613 w.Header().Set("Content-Type", "application/json")
614 _, _ = w.Write(autoConfData)
615 }))
616 defer server.Close()
617
618 // Configure autoconf for the node
619 node.SetIPFSConfig("AutoConf.URL", server.URL)
620 node.SetIPFSConfig("AutoConf.Enabled", true)
621
622 // Start daemon to fetch and cache autoconf data
623 t.Log("Starting daemon to fetch and cache autoconf data...")
624 daemon := node.StartDaemon()
625 defer daemon.StopDaemon()
626
627 // Wait for autoconf fetch (use autoconf default timeout + buffer)
628 time.Sleep(6 * time.Second) // defaultTimeout is 5s, add 1s buffer
629 t.Log("AutoConf should now be cached by daemon")
630
631 // Test Routing.DelegatedRouters field expansion with filtering
632 result := node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
633 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
634
635 var expandedRouters []string
636 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
637 require.NoError(t, err)
638
639 // Should contain supported paths from NewRoutingSystem
640 assert.Contains(t, expandedRouters, "https://supported-new.example.com/routing/v1/providers", "Should contain supported provider URL")
641 assert.Contains(t, expandedRouters, "https://supported-new.example.com/routing/v1/peers", "Should contain supported peers URL")
642 assert.Contains(t, expandedRouters, "https://mixed-new.example.com/routing/v1/providers", "Should contain mixed provider URL")
643 assert.Contains(t, expandedRouters, "https://mixed-new.example.com/routing/v1/peers", "Should contain mixed peers URL")
644
645 // Should NOT contain unsupported paths
646 assert.NotContains(t, expandedRouters, "https://unsupported-new.example.com/custom/v0/read", "Should filter out unsupported path")
647 assert.NotContains(t, expandedRouters, "https://unsupported-new.example.com/api/v1/nonstandard", "Should filter out unsupported path")
648 assert.NotContains(t, expandedRouters, "https://mixed-new.example.com/invalid/path", "Should filter out invalid path from mixed endpoint")
649
650 t.Logf("Filtered routers (NewRoutingSystem with auto routing): %v", expandedRouters)
651
652 // Test Ipns.DelegatedPublishers field expansion with filtering
653 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
654 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
655
656 var expandedPublishers []string
657 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
658 require.NoError(t, err)
659
660 // Should contain supported IPNS paths
661 assert.Contains(t, expandedPublishers, "https://supported-new.example.com/routing/v1/ipns", "Should contain supported IPNS URL")
662 assert.Contains(t, expandedPublishers, "https://mixed-new.example.com/routing/v1/ipns", "Should contain mixed IPNS URL")
663
664 // Should NOT contain unsupported write paths
665 assert.NotContains(t, expandedPublishers, "https://unsupported-new.example.com/custom/v0/write", "Should filter out unsupported write path")
666
667 t.Logf("Filtered publishers (NewRoutingSystem with auto routing): %v", expandedPublishers)
668 }
669
670 func testExpandAutoWithoutCacheAuto(t *testing.T) {
671 // Test scenario: CLI without daemon ever starting using auto routing (default)
672 // This tests the fallback scenario where auto routing is used but doesn't populate delegated config fields
673
674 // Create IPFS node but DO NOT start daemon
675 node := harness.NewT(t).NewNode().Init("--profile=test")
676
677 // Configure auto routing - delegated fields are set to "auto" but won't be populated
678 // because auto routing uses different internal mechanisms
679 node.SetIPFSConfig("Routing.Type", "auto")
680 node.SetIPFSConfig("Routing.DelegatedRouters", []string{"auto"})
681 node.SetIPFSConfig("Ipns.DelegatedPublishers", []string{"auto"})
682
683 // Load test autoconf data (this won't be used since no daemon and auto routing doesn't use these fields)
684 autoConfData := loadTestDataExpand(t, "autoconf_with_unsupported_paths.json")
685
686 // Create HTTP server (won't be contacted since no daemon)
687 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
688 w.Header().Set("Content-Type", "application/json")
689 _, _ = w.Write(autoConfData)
690 }))
691 defer server.Close()
692
693 // Configure autoconf for the node (but daemon never starts to fetch it)
694 node.SetIPFSConfig("AutoConf.URL", server.URL)
695 node.SetIPFSConfig("AutoConf.Enabled", true)
696
697 // Test Routing.DelegatedRouters field expansion without cached autoconf
698 result := node.RunIPFS("config", "Routing.DelegatedRouters", "--expand-auto")
699 require.Equal(t, 0, result.ExitCode(), "config Routing.DelegatedRouters --expand-auto should succeed")
700
701 var expandedRouters []string
702 err := json.Unmarshal([]byte(result.Stdout.String()), &expandedRouters)
703 require.NoError(t, err)
704
705 // With auto routing, some fallback URLs are still populated from GetMainnetFallbackConfig()
706 // NOTE: These values may change if autoconf library updates GetMainnetFallbackConfig()
707 assert.Contains(t, expandedRouters, "https://cid.contact/routing/v1/providers", "Should contain fallback provider URL from GetMainnetFallbackConfig()")
708
709 t.Logf("Auto routing fallback routers (with fallbacks): %v", expandedRouters)
710
711 // Test Ipns.DelegatedPublishers field expansion without cached autoconf
712 result = node.RunIPFS("config", "Ipns.DelegatedPublishers", "--expand-auto")
713 require.Equal(t, 0, result.ExitCode(), "config Ipns.DelegatedPublishers --expand-auto should succeed")
714
715 var expandedPublishers []string
716 err = json.Unmarshal([]byte(result.Stdout.String()), &expandedPublishers)
717 require.NoError(t, err)
718
719 // With auto routing, delegated publishers may be empty for fallback scenario
720 // This can vary based on which systems have write endpoints in the fallback config
721 t.Logf("Auto routing fallback publishers: %v", expandedPublishers)
722 }
723
724 // Helper function to load test data files
725 func loadTestDataExpand(t *testing.T, filename string) []byte {
726 t.Helper()
727
728 data, err := os.ReadFile("testdata/" + filename)
729 require.NoError(t, err, "Failed to read test data file: %s", filename)
730
731 return data
732 }