master
go 229 lines 7.06 KB
Raw
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.True(t, strings.HasPrefix(lines[0], "github.com/ipfs/kubo@v"))
66
67 for _, depLine := range lines[1:] {
68 split := strings.SplitSeq(depLine, " => ")
69 for moduleVersion := range split {
70 splitModVers := strings.Split(moduleVersion, "@")
71 modPath := splitModVers[0]
72 modVers := splitModVers[1]
73 // Skip local replace paths (starting with "./")
74 if strings.HasPrefix(modPath, "./") {
75 continue
76 }
77 assert.NoError(t, gomod.Check(modPath, modVers), "path: %s, version: %s", modPath, modVers)
78 }
79 }
80 }
81
82 func TestIPFSCommands(t *testing.T) {
83 t.Parallel()
84 node := harness.NewT(t).NewNode()
85 cmds := node.IPFSCommands()
86 assert.Contains(t, cmds, "ipfs add")
87 assert.Contains(t, cmds, "ipfs daemon")
88 assert.Contains(t, cmds, "ipfs update")
89 }
90
91 func TestAllSubcommandsAcceptHelp(t *testing.T) {
92 t.Parallel()
93 node := harness.NewT(t).NewNode()
94 for _, cmd := range node.IPFSCommands() {
95 t.Run(fmt.Sprintf("command %q accepts help", cmd), func(t *testing.T) {
96 t.Parallel()
97 splitCmd := strings.Split(cmd, " ")[1:]
98 node.IPFS(StrCat("help", splitCmd)...)
99 node.IPFS(StrCat(splitCmd, "--help")...)
100 })
101 }
102 }
103
104 func TestAllRootCommandsAreMentionedInHelpText(t *testing.T) {
105 t.Parallel()
106 node := harness.NewT(t).NewNode()
107 cmds := node.IPFSCommands()
108 var rootCmds []string
109 for _, cmd := range cmds {
110 splitCmd := strings.Split(cmd, " ")
111 if len(splitCmd) == 2 {
112 rootCmds = append(rootCmds, splitCmd[1])
113 }
114 }
115
116 // a few base commands are not expected to be in the help message
117 // but we default to requiring them to be in the help message, so that we
118 // have to make a conscious decision to exclude them
119 notInHelp := map[string]bool{
120 "object": true,
121 "shutdown": true,
122 }
123
124 helpMsg := strings.TrimSpace(node.IPFS("--help").Stdout.String())
125 for _, rootCmd := range rootCmds {
126 if _, ok := notInHelp[rootCmd]; ok {
127 continue
128 }
129 assert.Contains(t, helpMsg, fmt.Sprintf(" %s", rootCmd))
130 }
131 }
132
133 func TestCommandDocsWidth(t *testing.T) {
134 t.Parallel()
135 node := harness.NewT(t).NewNode()
136
137 // require new commands to explicitly opt in to longer lines
138 allowList := map[string]bool{
139 "ipfs add": true,
140 "ipfs block put": true,
141 "ipfs daemon": true,
142 "ipfs config profile": true,
143 "ipfs pin remote service": true,
144 "ipfs name pubsub": true,
145 "ipfs object patch": true,
146 "ipfs swarm connect": true,
147 "ipfs p2p forward": true,
148 "ipfs p2p close": true,
149 "ipfs swarm disconnect": true,
150 "ipfs swarm addrs listen": true,
151 "ipfs dag resolve": true,
152 "ipfs dag get": 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 pin remote service add": true,
160 "ipfs pin update": true,
161 "ipfs pin rm": true,
162 "ipfs p2p": true,
163 "ipfs resolve": true,
164 "ipfs dag stat": true,
165 "ipfs name publish": true,
166 "ipfs object diff": true,
167 "ipfs object patch add-link": true,
168 "ipfs name": true,
169 "ipfs diag profile": true,
170 "ipfs diag cmds": true,
171 "ipfs swarm addrs local": true,
172 "ipfs files ls": true,
173 "ipfs stats bw": true,
174 "ipfs swarm peers": true,
175 "ipfs pubsub sub": true,
176 "ipfs files write": true,
177 "ipfs swarm limit": true,
178 "ipfs commands completion fish": true,
179 "ipfs key export": true,
180 "ipfs routing get": true,
181 "ipfs refs": true,
182 "ipfs refs local": true,
183 "ipfs cid base32": true,
184 "ipfs pubsub pub": true,
185 "ipfs repo ls": true,
186 "ipfs routing put": true,
187 "ipfs key import": true,
188 "ipfs swarm peering add": true,
189 "ipfs swarm peering rm": true,
190 "ipfs swarm peering ls": true,
191 "ipfs update": true,
192 "ipfs swarm stats": true,
193 }
194 for _, cmd := range node.IPFSCommands() {
195 if _, ok := allowList[cmd]; ok {
196 continue
197 }
198 t.Run(fmt.Sprintf("command %q conforms to docs width limit", cmd), func(t *testing.T) {
199 splitCmd := strings.Split(cmd, " ")
200 resStr := node.IPFS(StrCat(splitCmd[1:], "--help")...)
201 res := strings.TrimSpace(resStr.Stdout.String())
202 for _, line := range SplitLines(res) {
203 assert.LessOrEqualf(t, len(line), 80, "expected width %d < 80 for %q", len(line), cmd)
204 }
205 })
206 }
207 }
208
209 func TestAllCommandsFailWhenPassedBadFlag(t *testing.T) {
210 t.Parallel()
211 node := harness.NewT(t).NewNode()
212
213 for _, cmd := range node.IPFSCommands() {
214 t.Run(fmt.Sprintf("command %q fails when passed a bad flag", cmd), func(t *testing.T) {
215 splitCmd := strings.Split(cmd, " ")
216 res := node.RunIPFS(StrCat(splitCmd, "--badflag")...)
217 assert.Equal(t, 1, res.Cmd.ProcessState.ExitCode())
218 })
219 }
220 }
221
222 func TestCommandsFlags(t *testing.T) {
223 t.Parallel()
224 node := harness.NewT(t).NewNode()
225 resStr := node.IPFS("commands", "--flags").Stdout.String()
226 assert.Contains(t, resStr, "ipfs pin add --recursive / ipfs pin add -r")
227 assert.Contains(t, resStr, "ipfs id --format / ipfs id -f")
228 assert.Contains(t, resStr, "ipfs repo gc --quiet / ipfs repo gc -q")
229 }