master
go 217 lines 7.8 KB
Raw
1 package cli
2
3 import (
4 "bytes"
5 "encoding/json"
6 "strings"
7 "testing"
8
9 "github.com/ipfs/kubo/test/cli/harness"
10 "github.com/stretchr/testify/require"
11 )
12
13 // TestCidBase verifies that --cid-base is respected across commands
14 // and that CIDv0 is auto-upgraded to CIDv1 when a non-base58btc base
15 // is requested.
16 //
17 // Tests use base16 rather than base32 to avoid false positives if
18 // base32 ever becomes the default CID encoding.
19 func TestCidBase(t *testing.T) {
20 t.Parallel()
21
22 const cidBaseFlag = "--cid-base=base16"
23 // base16 CIDv1 starts with "f01" (f = base16 multibase prefix)
24 const cidV1Prefix = "f01"
25
26 makeDaemon := func(t *testing.T) *harness.Node {
27 t.Helper()
28 node := harness.NewT(t).NewNode().Init().StartDaemon("--offline")
29 t.Cleanup(func() { node.StopDaemon() })
30 return node
31 }
32
33 t.Run("add respects --cid-base", func(t *testing.T) {
34 t.Parallel()
35 node := makeDaemon(t)
36
37 // ipfs add -q
38 cid := node.IPFSAddStr("test-add", cidBaseFlag)
39 require.True(t, strings.HasPrefix(cid, cidV1Prefix), "expected base16 CIDv1 from add, got %s", cid)
40
41 // ipfs add -Q (quiet, only final CID)
42 cid = node.PipeStrToIPFS("test-add-Q", "add", "-Q", cidBaseFlag).Stdout.Trimmed()
43 require.True(t, strings.HasPrefix(cid, cidV1Prefix), "expected base16 CIDv1 from add -Q, got %s", cid)
44 })
45
46 t.Run("pin ls respects --cid-base", func(t *testing.T) {
47 t.Parallel()
48 node := makeDaemon(t)
49
50 node.IPFSAddStr("pin-ls-test")
51
52 lines := node.IPFS("pin", "ls", "-t", "recursive", cidBaseFlag).Stdout.Lines()
53 for _, line := range lines {
54 if line == "" {
55 continue
56 }
57 require.True(t, strings.HasPrefix(line, cidV1Prefix), "expected base16 CID in pin ls, got %s", line)
58 }
59 })
60
61 t.Run("dag import respects --cid-base", func(t *testing.T) {
62 t.Parallel()
63 node := makeDaemon(t)
64
65 // Add content and export as CAR
66 cid := node.IPFSAddStr("dag-import-test", "--pin=false")
67 carData := node.IPFS("dag", "export", cid).Stdout.Bytes()
68
69 // Import the CAR with --cid-base
70 out := node.PipeToIPFS(bytes.NewReader(carData), "dag", "import", cidBaseFlag).Stdout.Trimmed()
71 require.Contains(t, out, cidV1Prefix, "expected base16 CID in dag import output, got %s", out)
72 })
73
74 t.Run("block put returns base16 CIDv1", func(t *testing.T) {
75 t.Parallel()
76 node := makeDaemon(t)
77 cid := node.PipeStrToIPFS("hello", "block", "put", cidBaseFlag).Stdout.Trimmed()
78 require.True(t, strings.HasPrefix(cid, cidV1Prefix), "expected base16 CIDv1, got %s", cid)
79 })
80
81 t.Run("block put --format=v0 auto-upgrades to CIDv1 with --cid-base", func(t *testing.T) {
82 t.Parallel()
83 node := makeDaemon(t)
84
85 // Without --cid-base: CIDv0 in base58btc
86 cidV0 := node.PipeStrToIPFS("hello", "block", "put", "--format=v0").Stdout.Trimmed()
87 require.True(t, strings.HasPrefix(cidV0, "Qm"), "expected CIDv0, got %s", cidV0)
88
89 // With --cid-base: same content but displayed as CIDv1
90 cidV1 := node.PipeStrToIPFS("hello", "block", "put", "--format=v0", cidBaseFlag).Stdout.Trimmed()
91 require.True(t, strings.HasPrefix(cidV1, cidV1Prefix), "expected base16 CIDv1, got %s", cidV1)
92 })
93
94 t.Run("block stat respects --cid-base", func(t *testing.T) {
95 t.Parallel()
96 node := makeDaemon(t)
97
98 cidV0 := node.PipeStrToIPFS("test-block-stat", "block", "put", "--format=v0").Stdout.Trimmed()
99 require.True(t, strings.HasPrefix(cidV0, "Qm"))
100
101 // block stat without --cid-base returns CIDv0
102 stat := node.IPFS("block", "stat", cidV0).Stdout.Trimmed()
103 require.Contains(t, stat, cidV0)
104
105 // block stat with --cid-base returns CIDv1
106 stat = node.IPFS("block", "stat", cidBaseFlag, cidV0).Stdout.Trimmed()
107 require.NotContains(t, stat, cidV0, "should not contain CIDv0")
108 require.Contains(t, stat, cidV1Prefix, "should contain base16 CIDv1")
109 })
110
111 t.Run("block rm respects --cid-base", func(t *testing.T) {
112 t.Parallel()
113 node := makeDaemon(t)
114
115 cidV0 := node.PipeStrToIPFS("test-block-rm", "block", "put", "--format=v0").Stdout.Trimmed()
116 require.True(t, strings.HasPrefix(cidV0, "Qm"))
117
118 out := node.IPFS("block", "rm", cidBaseFlag, cidV0).Stdout.Trimmed()
119 require.Contains(t, out, cidV1Prefix, "removed block should be shown as base16 CIDv1")
120 require.NotContains(t, out, "Qm", "removed block should not contain CIDv0")
121 })
122
123 t.Run("dag stat respects --cid-base", func(t *testing.T) {
124 t.Parallel()
125 node := makeDaemon(t)
126
127 // ipfs add creates dag-pb blocks with CIDv0 by default
128 cidV0 := node.IPFSAddStr("test-dag-stat", "--pin=false")
129 require.True(t, strings.HasPrefix(cidV0, "Qm"))
130
131 // JSON output without --cid-base has CIDv0
132 out := node.IPFS("dag", "stat", "--progress=false", "--enc=json", cidV0).Stdout.Trimmed()
133 var data struct {
134 DagStats []struct{ Cid string } `json:"DagStats"`
135 }
136 require.NoError(t, json.Unmarshal([]byte(out), &data))
137 require.True(t, strings.HasPrefix(data.DagStats[0].Cid, "Qm"))
138
139 // JSON output with --cid-base has CIDv1
140 out = node.IPFS("dag", "stat", "--progress=false", "--enc=json", cidBaseFlag, cidV0).Stdout.Trimmed()
141 require.NoError(t, json.Unmarshal([]byte(out), &data))
142 require.True(t, strings.HasPrefix(data.DagStats[0].Cid, cidV1Prefix), "expected base16 CIDv1 in dag stat, got %s", data.DagStats[0].Cid)
143 })
144
145 t.Run("object patch add-link respects --cid-base", func(t *testing.T) {
146 t.Parallel()
147 node := makeDaemon(t)
148
149 // Parent must be a directory for add-link to work
150 node.IPFS("files", "mkdir", "/patch-add")
151 parent := node.IPFS("files", "stat", "--hash", "/patch-add").Stdout.Trimmed()
152 child := node.IPFSAddStr("child", "--pin=false")
153
154 // Without --cid-base: CIDv0
155 cidV0 := node.IPFS("object", "patch", "add-link", parent, "link", child).Stdout.Trimmed()
156 require.True(t, strings.HasPrefix(cidV0, "Qm"), "expected CIDv0, got %s", cidV0)
157
158 // With --cid-base: CIDv1
159 cidV1 := node.IPFS("object", "patch", "add-link", cidBaseFlag, parent, "link", child).Stdout.Trimmed()
160 require.True(t, strings.HasPrefix(cidV1, cidV1Prefix), "expected base16 CIDv1, got %s", cidV1)
161 })
162
163 t.Run("object patch rm-link respects --cid-base", func(t *testing.T) {
164 t.Parallel()
165 node := makeDaemon(t)
166
167 node.IPFS("files", "mkdir", "/patch-rm")
168 parent := node.IPFS("files", "stat", "--hash", "/patch-rm").Stdout.Trimmed()
169 child := node.IPFSAddStr("child", "--pin=false")
170
171 linked := node.IPFS("object", "patch", "add-link", parent, "link", child).Stdout.Trimmed()
172
173 cidV1 := node.IPFS("object", "patch", "rm-link", cidBaseFlag, linked, "link").Stdout.Trimmed()
174 require.True(t, strings.HasPrefix(cidV1, cidV1Prefix), "expected base16 CIDv1, got %s", cidV1)
175 })
176
177 t.Run("refs local respects --cid-base", func(t *testing.T) {
178 t.Parallel()
179 node := makeDaemon(t)
180
181 node.IPFSAddStr("refs-local-test", "--pin=false")
182
183 lines := node.IPFS("refs", "local", cidBaseFlag).Stdout.Lines()
184 for _, line := range lines {
185 if line == "" {
186 continue
187 }
188 require.True(t, strings.HasPrefix(line, cidV1Prefix), "expected base16 CID, got %s", line)
189 }
190 })
191
192 t.Run("object diff respects --cid-base", func(t *testing.T) {
193 t.Parallel()
194 node := makeDaemon(t)
195
196 cidA := node.IPFSAddStr("aaa", "--pin=false")
197 cidB := node.IPFSAddStr("bbb", "--pin=false")
198
199 // Create two directories with different children
200 node.IPFS("files", "mkdir", "/diff-a")
201 node.IPFS("files", "cp", "/ipfs/"+cidA, "/diff-a/file")
202 dirA := node.IPFS("files", "stat", "--hash", "/diff-a").Stdout.Trimmed()
203
204 node.IPFS("files", "mkdir", "/diff-b")
205 node.IPFS("files", "cp", "/ipfs/"+cidB, "/diff-b/file")
206 dirB := node.IPFS("files", "stat", "--hash", "/diff-b").Stdout.Trimmed()
207
208 // Without --cid-base: CIDs in diff output are CIDv0
209 out := node.IPFS("object", "diff", dirA, dirB).Stdout.Trimmed()
210 require.Contains(t, out, "Qm")
211
212 // With --cid-base: CIDs in diff output should be base16
213 out = node.IPFS("object", "diff", cidBaseFlag, dirA, dirB).Stdout.Trimmed()
214 require.Contains(t, out, cidV1Prefix, "expected base16 CIDs in diff output")
215 require.NotContains(t, out, "Qm", "should not contain CIDv0 in diff output")
216 })
217 }