master
go 226 lines 7.26 KB
Raw
1 package cli
2
3 import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "github.com/ipfs/kubo/test/cli/harness"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/require"
12 )
13
14 func TestDiagDatastore(t *testing.T) {
15 t.Parallel()
16
17 t.Run("diag datastore get returns error for non-existent key", func(t *testing.T) {
18 t.Parallel()
19 node := harness.NewT(t).NewNode().Init()
20 // Don't start daemon - these commands require daemon to be stopped
21
22 res := node.RunIPFS("diag", "datastore", "get", "/nonexistent/key")
23 assert.Error(t, res.Err)
24 assert.Contains(t, res.Stderr.String(), "key not found")
25 })
26
27 t.Run("diag datastore get returns raw bytes by default", func(t *testing.T) {
28 t.Parallel()
29 node := harness.NewT(t).NewNode().Init()
30
31 // Add some data to create a known datastore key
32 // We need daemon for add, then stop it
33 node.StartDaemon()
34 cid := node.IPFSAddStr("test data for diag datastore")
35 node.IPFS("pin", "add", cid)
36 node.StopDaemon()
37
38 // Test count to verify we have entries
39 count := node.DatastoreCount("/")
40 t.Logf("total datastore entries: %d", count)
41 assert.NotEqual(t, int64(0), count, "should have datastore entries after pinning")
42 })
43
44 t.Run("diag datastore get --hex returns hex dump", func(t *testing.T) {
45 t.Parallel()
46 node := harness.NewT(t).NewNode().Init()
47
48 // Add and pin some data
49 node.StartDaemon()
50 cid := node.IPFSAddStr("test data for hex dump")
51 node.IPFS("pin", "add", cid)
52 node.StopDaemon()
53
54 // Test with existing keys in pins namespace
55 count := node.DatastoreCount("/pins/")
56 t.Logf("pins datastore entries: %d", count)
57
58 if count != 0 {
59 t.Log("pins datastore has entries, hex dump format tested implicitly")
60 }
61 })
62
63 t.Run("diag datastore count returns 0 for empty prefix", func(t *testing.T) {
64 t.Parallel()
65 node := harness.NewT(t).NewNode().Init()
66
67 count := node.DatastoreCount("/definitely/nonexistent/prefix/")
68 assert.Equal(t, int64(0), count)
69 })
70
71 t.Run("diag datastore count returns JSON with --enc=json", func(t *testing.T) {
72 t.Parallel()
73 node := harness.NewT(t).NewNode().Init()
74
75 res := node.IPFS("diag", "datastore", "count", "/pubsub/seqno/", "--enc=json")
76 assert.NoError(t, res.Err)
77
78 var result struct {
79 Prefix string `json:"prefix"`
80 Count int64 `json:"count"`
81 }
82 err := json.Unmarshal(res.Stdout.Bytes(), &result)
83 require.NoError(t, err)
84 assert.Equal(t, "/pubsub/seqno/", result.Prefix)
85 assert.Equal(t, int64(0), result.Count)
86 })
87
88 t.Run("diag datastore get returns JSON with --enc=json", func(t *testing.T) {
89 t.Parallel()
90 node := harness.NewT(t).NewNode().Init()
91
92 // Test error case with JSON encoding
93 res := node.RunIPFS("diag", "datastore", "get", "/nonexistent", "--enc=json")
94 assert.Error(t, res.Err)
95 })
96
97 t.Run("diag datastore count counts entries correctly", func(t *testing.T) {
98 t.Parallel()
99 node := harness.NewT(t).NewNode().Init()
100
101 // Add multiple pins to create multiple entries
102 node.StartDaemon()
103 cid1 := node.IPFSAddStr("data 1")
104 cid2 := node.IPFSAddStr("data 2")
105 cid3 := node.IPFSAddStr("data 3")
106
107 node.IPFS("pin", "add", cid1)
108 node.IPFS("pin", "add", cid2)
109 node.IPFS("pin", "add", cid3)
110 node.StopDaemon()
111
112 // Count should reflect the pins (plus any system entries)
113 count := node.DatastoreCount("/")
114 t.Logf("total entries after adding 3 pins: %d", count)
115
116 // Should have more than 0 entries
117 assert.NotEqual(t, int64(0), count)
118 })
119
120 t.Run("diag datastore commands work offline", func(t *testing.T) {
121 t.Parallel()
122 node := harness.NewT(t).NewNode().Init()
123 // Don't start daemon - these commands require daemon to be stopped
124
125 // Count should work offline
126 count := node.DatastoreCount("/pubsub/seqno/")
127 assert.Equal(t, int64(0), count)
128
129 // Get should return error for missing key (but command should work)
130 res := node.RunIPFS("diag", "datastore", "get", "/nonexistent/key")
131 assert.Error(t, res.Err)
132 assert.Contains(t, res.Stderr.String(), "key not found")
133 })
134
135 t.Run("diag datastore put and get roundtrip", func(t *testing.T) {
136 t.Parallel()
137 node := harness.NewT(t).NewNode().Init()
138
139 node.DatastorePut("/test/roundtrip", "hello world")
140 assert.True(t, node.DatastoreHasKey("/test/roundtrip"))
141 assert.Equal(t, []byte("hello world"), node.DatastoreGet("/test/roundtrip"))
142
143 count := node.DatastoreCount("/test/")
144 assert.Equal(t, int64(1), count)
145 })
146
147 t.Run("diag datastore commands require daemon to be stopped", func(t *testing.T) {
148 t.Parallel()
149 node := harness.NewT(t).NewNode().Init().StartDaemon()
150 defer node.StopDaemon()
151
152 // Both get and count require repo lock, which is held by the running daemon
153 res := node.RunIPFS("diag", "datastore", "get", "/test")
154 assert.Error(t, res.Err, "get should fail when daemon is running")
155 assert.Contains(t, res.Stderr.String(), "ipfs daemon is running")
156
157 res = node.RunIPFS("diag", "datastore", "count", "/pubsub/seqno/")
158 assert.Error(t, res.Err, "count should fail when daemon is running")
159 assert.Contains(t, res.Stderr.String(), "ipfs daemon is running")
160 })
161
162 t.Run("provider keystore datastores are visible in unified view", func(t *testing.T) {
163 t.Parallel()
164 node := harness.NewT(t).NewNode().Init()
165 node.SetIPFSConfig("Provide.DHT.SweepEnabled", true)
166 node.SetIPFSConfig("Provide.Enabled", true)
167
168 // Start daemon to create the provider-keystore datastores, then add data
169 node.StartDaemon()
170 cid := node.IPFSAddStr("data for provider keystore test")
171 node.IPFS("pin", "add", cid)
172 node.StopDaemon()
173
174 // Verify the provider-keystore directory was created
175 keystorePath := filepath.Join(node.Dir, "provider-keystore")
176 _, err := os.Stat(keystorePath)
177 require.NoError(t, err, "provider-keystore directory should exist after sweep-enabled daemon ran")
178
179 // Count entries in each keystore namespace via the unified view
180 for _, prefix := range []string{"/provider/keystore/0/", "/provider/keystore/1/"} {
181 res := node.IPFS("diag", "datastore", "count", prefix)
182 assert.NoError(t, res.Err)
183 t.Logf("count %s: %s", prefix, res.Stdout.String())
184 }
185
186 // The total count under /provider/keystore/ should include entries
187 // from both keystore instances (0 and 1)
188 count := node.DatastoreCount("/provider/keystore/")
189 t.Logf("total /provider/keystore/ entries: %d", count)
190 assert.Greater(t, count, int64(0), "should have provider keystore entries")
191 })
192
193 t.Run("provider keystore count JSON output", func(t *testing.T) {
194 t.Parallel()
195 node := harness.NewT(t).NewNode().Init()
196 node.SetIPFSConfig("Provide.DHT.SweepEnabled", true)
197 node.SetIPFSConfig("Provide.Enabled", true)
198
199 node.StartDaemon()
200 node.StopDaemon()
201
202 res := node.IPFS("diag", "datastore", "count", "/provider/keystore/0/", "--enc=json")
203 assert.NoError(t, res.Err)
204
205 var result struct {
206 Prefix string `json:"prefix"`
207 Count int64 `json:"count"`
208 }
209 err := json.Unmarshal(res.Stdout.Bytes(), &result)
210 require.NoError(t, err)
211 assert.Equal(t, "/provider/keystore/0/", result.Prefix)
212 assert.GreaterOrEqual(t, result.Count, int64(0), "count should be non-negative")
213 })
214
215 t.Run("works without provider keystore", func(t *testing.T) {
216 t.Parallel()
217 node := harness.NewT(t).NewNode().Init()
218
219 // No sweep enabled, no provider-keystore dirs — should still work fine
220 count := node.DatastoreCount("/provider/keystore/0/")
221 assert.Zero(t, count)
222
223 count = node.DatastoreCount("/")
224 assert.Greater(t, count, int64(0))
225 })
226 }