472
expectNoProviders(t, cidChunk, peers...)
473
})
474
475
+ // addLargeFilestoreFile writes a 2 MiB file to the publisher's
476
+ // node directory and adds it via --nocopy, returning the root CID
477
+ // and a chunk CID from the file's DAG links. With the configured
478
+ // 1 MiB chunker the file produces multiple leaf blocks so we can
479
+ // distinguish root-level from chunk-level provide behavior.
480
+ addLargeFilestoreFile := func(t *testing.T, publisher *harness.Node, addArgs ...string) (cidRoot, cidChunk string) {
481
+ t.Helper()
482
+ filePath := filepath.Join(publisher.Dir, "filestore-"+strconv.FormatInt(time.Now().UnixNano(), 10)+".bin")
483
+ require.NoError(t, os.WriteFile(filePath, random.Bytes(2*1024*1024), 0o644))
484
+
485
+ args := append([]string{"add", "-q", "--nocopy"}, addArgs...)
486
+ args = append(args, filePath)
487
+ cidRoot = strings.TrimSpace(publisher.IPFS(args...).Stdout.String())
488
+
489
+ dagOut := publisher.IPFS("dag", "get", cidRoot)
490
+ var dagNode struct {
491
+ Links []struct {
492
+ Hash map[string]string `json:"Hash"`
493
+ } `json:"Links"`
494
+ }
495
+ require.NoError(t, json.Unmarshal(dagOut.Stdout.Bytes(), &dagNode))
496
+ require.Greater(t, len(dagNode.Links), 1, "filestore file should have multiple chunks")
497
+ cidChunk = dagNode.Links[0].Hash["/"]
498
+ require.NotEmpty(t, cidChunk)
499
+ return cidRoot, cidChunk
500
+ }
501
+
502
+ t.Run("Filestore --nocopy with 'all' strategy provides every block", func(t *testing.T) {
503
+ t.Parallel()
504
+
505
+ nodes := initNodes(t, 2, func(n *harness.Node) {
506
+ n.SetIPFSConfig("Experimental.FilestoreEnabled", true)
507
+ n.SetIPFSConfig("Provide.Strategy", "all")
508
+ n.SetIPFSConfig("Import.UnixFSChunker", "size-1048576") // 1 MiB chunks
509
+ })
510
+ defer nodes.StopDaemons()
511
+ publisher, peers := nodes[0], nodes[1:]
512
+
513
+ // Positive control: with the default 'all' strategy the
514
+ // filestore Put path provides every block as it is written,
515
+ // including non-root chunks.
516
+ cidRoot, cidChunk := addLargeFilestoreFile(t, publisher)
517
+
518
+ pid := publisher.PeerID().String()
519
+ expectProviders(t, cidRoot, pid, peers...)
520
+ expectProviders(t, cidChunk, pid, peers...)
521
+ })
522
+
523
+ t.Run("Filestore --nocopy with selective strategy skips write-time provide", func(t *testing.T) {
524
+ t.Parallel()
525
+
526
+ nodes := initNodes(t, 2, func(n *harness.Node) {
527
+ n.SetIPFSConfig("Experimental.FilestoreEnabled", true)
528
+ n.SetIPFSConfig("Provide.Strategy", "pinned")
529
+ n.SetIPFSConfig("Import.UnixFSChunker", "size-1048576") // 1 MiB chunks
530
+ })
531
+ defer nodes.StopDaemons()
532
+ publisher, peers := nodes[0], nodes[1:]
533
+
534
+ // With a selective strategy the filestore must not eagerly
535
+ // announce blocks at write time. --pin=false skips the pin
536
+ // (so fast-provide-root has nothing to do) and
537
+ // --fast-provide-root=false disables it explicitly, isolating
538
+ // the assertion to the filestore's internal provide path.
539
+ cidRoot, cidChunk := addLargeFilestoreFile(t, publisher,
540
+ "--pin=false", "--fast-provide-root=false")
541
+
542
+ expectNoProviders(t, cidRoot, peers...)
543
+ expectNoProviders(t, cidChunk, peers...)
544
+ })
545
+
546
+ t.Run("Filestore --nocopy + selective strategy + --fast-provide-dag walks DAG", func(t *testing.T) {
547
+ t.Parallel()
548
+
549
+ nodes := initNodes(t, 2, func(n *harness.Node) {
550
+ n.SetIPFSConfig("Experimental.FilestoreEnabled", true)
551
+ n.SetIPFSConfig("Provide.Strategy", "pinned")
552
+ n.SetIPFSConfig("Import.UnixFSChunker", "size-1048576") // 1 MiB chunks
553
+ })
554
+ defer nodes.StopDaemons()
555
+ publisher, peers := nodes[0], nodes[1:]
556
+
557
+ // The selective-strategy gate skips the filestore's write-time
558
+ // provide, but the post-add ExecuteFastProvideDAG walk reads
559
+ // blocks through the wrapping blockstore (which transparently
560
+ // serves filestore-backed content) and announces each block,
561
+ // honoring the active strategy. This is the integration test
562
+ // behind the changelog claim that filestore content now plays
563
+ // well with the fast-provide-dag flag.
564
+ cidRoot, cidChunk := addLargeFilestoreFile(t, publisher,
565
+ "--fast-provide-dag", "--fast-provide-wait")
566
+
567
+ pid := publisher.PeerID().String()
568
+ expectProviders(t, cidRoot, pid, peers...)
569
+ expectProviders(t, cidChunk, pid, peers...)
570
+ })
571
+
572
t.Run("Provide with 'roots' strategy", func(t *testing.T) {
573
t.Parallel()
574