1
+package cli
2
+
3
+import (
4
+ "fmt"
5
+ "regexp"
6
+ "strings"
7
+ "testing"
8
+
9
+ "github.com/blang/semver/v4"
10
+ "github.com/ipfs/kubo/test/cli/harness"
11
+ . "github.com/ipfs/kubo/test/cli/testutils"
12
+ "github.com/stretchr/testify/assert"
13
+ gomod "golang.org/x/mod/module"
14
+)
15
+
16
+var versionRegexp = regexp.MustCompile(`^ipfs version (.+)$`)
17
+
18
+func parseVersionOutput(s string) semver.Version {
19
+ versString := versionRegexp.FindStringSubmatch(s)[1]
20
+ v, err := semver.Parse(versString)
21
+ if err != nil {
22
+ panic(err)
23
+ }
24
+ return v
25
+}
26
+
27
+func TestCurDirIsWritable(t *testing.T) {
28
+ t.Parallel()
29
+ h := harness.NewT(t)
30
+ h.WriteFile("test.txt", "It works!")
31
+}
32
+
33
+func TestIPFSVersionCommandMatchesFlag(t *testing.T) {
34
+ t.Parallel()
35
+ node := harness.NewT(t).NewNode()
36
+ commandVersionStr := node.IPFS("version").Stdout.String()
37
+ commandVersionStr = strings.TrimSpace(commandVersionStr)
38
+ commandVersion := parseVersionOutput(commandVersionStr)
39
+
40
+ flagVersionStr := node.IPFS("--version").Stdout.String()
41
+ flagVersionStr = strings.TrimSpace(flagVersionStr)
42
+ flagVersion := parseVersionOutput(flagVersionStr)
43
+
44
+ assert.Equal(t, commandVersion, flagVersion)
45
+}
46
+
47
+func TestIPFSVersionAll(t *testing.T) {
48
+ t.Parallel()
49
+ node := harness.NewT(t).NewNode()
50
+ res := node.IPFS("version", "--all").Stdout.String()
51
+ res = strings.TrimSpace(res)
52
+ assert.Contains(t, res, "Kubo version")
53
+ assert.Contains(t, res, "Repo version")
54
+ assert.Contains(t, res, "System version")
55
+ assert.Contains(t, res, "Golang version")
56
+}
57
+
58
+func TestIPFSVersionDeps(t *testing.T) {
59
+ t.Parallel()
60
+ node := harness.NewT(t).NewNode()
61
+ res := node.IPFS("version", "deps").Stdout.String()
62
+ res = strings.TrimSpace(res)
63
+ lines := SplitLines(res)
64
+
65
+ assert.Equal(t, "github.com/ipfs/kubo@(devel)", lines[0])
66
+
67
+ for _, depLine := range lines[1:] {
68
+ split := strings.Split(depLine, " => ")
69
+ for _, moduleVersion := range split {
70
+ splitModVers := strings.Split(moduleVersion, "@")
71
+ modPath := splitModVers[0]
72
+ modVers := splitModVers[1]
73
+ assert.NoError(t, gomod.Check(modPath, modVers), "path: %s, version: %s", modPath, modVers)
74
+ }
75
+ }
76
+}
77
+
78
+func TestIPFSCommands(t *testing.T) {
79
+ t.Parallel()
80
+ node := harness.NewT(t).NewNode()
81
+ cmds := node.IPFSCommands()
82
+ assert.Contains(t, cmds, "ipfs add")
83
+ assert.Contains(t, cmds, "ipfs daemon")
84
+ assert.Contains(t, cmds, "ipfs update")
85
+}
86
+
87
+func TestAllSubcommandsAcceptHelp(t *testing.T) {
88
+ t.Parallel()
89
+ node := harness.NewT(t).NewNode()
90
+ for _, cmd := range node.IPFSCommands() {
91
+ t.Run(fmt.Sprintf("command %q accepts help", cmd), func(t *testing.T) {
92
+ t.Parallel()
93
+ splitCmd := strings.Split(cmd, " ")[1:]
94
+ node.IPFS(StrCat("help", splitCmd)...)
95
+ node.IPFS(StrCat(splitCmd, "--help")...)
96
+ })
97
+ }
98
+}
99
+
100
+func TestAllRootCommandsAreMentionedInHelpText(t *testing.T) {
101
+ t.Parallel()
102
+ node := harness.NewT(t).NewNode()
103
+ cmds := node.IPFSCommands()
104
+ var rootCmds []string
105
+ for _, cmd := range cmds {
106
+ splitCmd := strings.Split(cmd, " ")
107
+ if len(splitCmd) == 2 {
108
+ rootCmds = append(rootCmds, splitCmd[1])
109
+ }
110
+ }
111
+
112
+ // a few base commands are not expected to be in the help message
113
+ // but we default to requiring them to be in the help message, so that we
114
+ // have to make an conscious decision to exclude them
115
+ notInHelp := map[string]bool{
116
+ "object": true,
117
+ "shutdown": true,
118
+ "tar": true,
119
+ "urlstore": true,
120
+ "dns": true,
121
+ }
122
+
123
+ helpMsg := strings.TrimSpace(node.IPFS("--help").Stdout.String())
124
+ for _, rootCmd := range rootCmds {
125
+ if _, ok := notInHelp[rootCmd]; ok {
126
+ continue
127
+ }
128
+ assert.Contains(t, helpMsg, fmt.Sprintf(" %s", rootCmd))
129
+ }
130
+}
131
+
132
+func TestCommandDocsWidth(t *testing.T) {
133
+ t.Parallel()
134
+ node := harness.NewT(t).NewNode()
135
+
136
+ // require new commands to explicitly opt in to longer lines
137
+ allowList := map[string]bool{
138
+ "ipfs add": true,
139
+ "ipfs block put": true,
140
+ "ipfs daemon": true,
141
+ "ipfs config profile": true,
142
+ "ipfs pin remote service": true,
143
+ "ipfs name pubsub": true,
144
+ "ipfs object patch": true,
145
+ "ipfs swarm connect": true,
146
+ "ipfs p2p forward": true,
147
+ "ipfs p2p close": true,
148
+ "ipfs swarm disconnect": true,
149
+ "ipfs swarm addrs listen": true,
150
+ "ipfs dag resolve": true,
151
+ "ipfs dag get": true,
152
+ "ipfs object stat": true,
153
+ "ipfs pin remote add": true,
154
+ "ipfs config show": true,
155
+ "ipfs config edit": true,
156
+ "ipfs pin remote rm": true,
157
+ "ipfs pin remote ls": true,
158
+ "ipfs pin verify": true,
159
+ "ipfs dht get": true,
160
+ "ipfs pin remote service add": true,
161
+ "ipfs file ls": true,
162
+ "ipfs pin update": true,
163
+ "ipfs pin rm": true,
164
+ "ipfs p2p": true,
165
+ "ipfs resolve": true,
166
+ "ipfs dag stat": true,
167
+ "ipfs name publish": true,
168
+ "ipfs object diff": true,
169
+ "ipfs object patch add-link": true,
170
+ "ipfs name": true,
171
+ "ipfs object patch append-data": true,
172
+ "ipfs object patch set-data": true,
173
+ "ipfs dht put": true,
174
+ "ipfs diag profile": true,
175
+ "ipfs diag cmds": true,
176
+ "ipfs swarm addrs local": true,
177
+ "ipfs files ls": true,
178
+ "ipfs stats bw": true,
179
+ "ipfs urlstore add": true,
180
+ "ipfs swarm peers": true,
181
+ "ipfs pubsub sub": true,
182
+ "ipfs repo fsck": true,
183
+ "ipfs files write": true,
184
+ "ipfs swarm limit": true,
185
+ "ipfs commands completion fish": true,
186
+ "ipfs key export": true,
187
+ "ipfs routing get": true,
188
+ "ipfs refs": true,
189
+ "ipfs refs local": true,
190
+ "ipfs cid base32": true,
191
+ "ipfs pubsub pub": true,
192
+ "ipfs repo ls": true,
193
+ "ipfs routing put": true,
194
+ "ipfs key import": true,
195
+ "ipfs swarm peering add": true,
196
+ "ipfs swarm peering rm": true,
197
+ "ipfs swarm peering ls": true,
198
+ "ipfs update": true,
199
+ "ipfs swarm stats": true,
200
+ }
201
+ for _, cmd := range node.IPFSCommands() {
202
+ if _, ok := allowList[cmd]; ok {
203
+ continue
204
+ }
205
+ t.Run(fmt.Sprintf("command %q conforms to docs width limit", cmd), func(t *testing.T) {
206
+ splitCmd := strings.Split(cmd, " ")
207
+ resStr := node.IPFS(StrCat(splitCmd[1:], "--help")...)
208
+ res := strings.TrimSpace(resStr.Stdout.String())
209
+ for _, line := range SplitLines(res) {
210
+ assert.LessOrEqualf(t, len(line), 80, "expected width %d < 80 for %q", len(line), cmd)
211
+ }
212
+
213
+ })
214
+ }
215
+}
216
+
217
+func TestAllCommandsFailWhenPassedBadFlag(t *testing.T) {
218
+ t.Parallel()
219
+ node := harness.NewT(t).NewNode()
220
+
221
+ for _, cmd := range node.IPFSCommands() {
222
+ t.Run(fmt.Sprintf("command %q fails when passed a bad flag", cmd), func(t *testing.T) {
223
+ splitCmd := strings.Split(cmd, " ")
224
+ res := node.RunIPFS(StrCat(splitCmd, "--badflag")...)
225
+ assert.Equal(t, 1, res.Cmd.ProcessState.ExitCode())
226
+ })
227
+ }
228
+
229
+}
230
+
231
+func TestCommandsFlags(t *testing.T) {
232
+ t.Parallel()
233
+ node := harness.NewT(t).NewNode()
234
+ resStr := node.IPFS("commands", "--flags").Stdout.String()
235
+ assert.Contains(t, resStr, "ipfs pin add --recursive / ipfs pin add -r")
236
+ assert.Contains(t, resStr, "ipfs id --format / ipfs id -f")
237
+ assert.Contains(t, resStr, "ipfs repo gc --quiet / ipfs repo gc -q")
238
+}