Skip to content

Commit 22e305d

Browse files
authored
test(e2e): Add e2e coverage for kraft system (#2929)
Reviewed-by: Cezar Craciunoiu <cezar.craciunoiu@unikraft.com> Approved-by: Cezar Craciunoiu <cezar.craciunoiu@unikraft.com>
2 parents 2f8635f + eb38a75 commit 22e305d

1 file changed

Lines changed: 253 additions & 0 deletions

File tree

‎test/e2e/cli/system_test.go‎

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2026, Unikraft GmbH and The KraftKit Authors.
3+
// Licensed under the BSD-3-Clause License (the "License").
4+
// You may not use this file except in compliance with the License.
5+
6+
package cli_test
7+
8+
import (
9+
"fmt"
10+
"path/filepath"
11+
"strings"
12+
13+
. "github.com/onsi/ginkgo/v2" //nolint:stylecheck
14+
. "github.com/onsi/gomega" //nolint:stylecheck
15+
16+
fcmd "kraftkit.sh/test/e2e/framework/cmd"
17+
fcfg "kraftkit.sh/test/e2e/framework/config"
18+
)
19+
20+
var _ = Describe("kraft system", func() {
21+
var stdout *fcmd.IOStream
22+
var stderr *fcmd.IOStream
23+
24+
var cfg *fcfg.Config
25+
26+
kraft := func(args ...string) *fcmd.Cmd {
27+
stdout = fcmd.NewIOStream()
28+
stderr = fcmd.NewIOStream()
29+
30+
cmd := fcmd.NewKraft(stdout, stderr, cfg.Path())
31+
tmpBase := filepath.Dir(filepath.Dir(cfg.Path()))
32+
cmd.Env = append(cmd.Env, "HOME="+tmpBase)
33+
cmd.Dir = tmpBase
34+
35+
cmd.Args = append(cmd.Args, "system")
36+
cmd.Args = append(cmd.Args, args...)
37+
cmd.Args = append(cmd.Args, "--log-level", "error", "--log-type", "json")
38+
39+
return cmd
40+
}
41+
42+
// run executes a kraft system command that is expected to succeed.
43+
run := func(args ...string) {
44+
GinkgoHelper()
45+
46+
cmd := kraft(args...)
47+
err := cmd.Run()
48+
if err != nil {
49+
fmt.Print(cmd.DumpError(stdout, stderr, err))
50+
}
51+
Expect(err).ToNot(HaveOccurred())
52+
}
53+
54+
// list runs `kraft system list` and returns its output as lines. The order
55+
// of map entries (toolchain.*, aliases.*, auth.*) is random, so callers
56+
// must only check for the presence or absence of lines.
57+
list := func() []string {
58+
GinkgoHelper()
59+
60+
run("list")
61+
Expect(stderr.String()).To(BeEmpty())
62+
63+
return strings.Split(strings.TrimRight(stdout.String(), "\n"), "\n")
64+
}
65+
66+
// value reads a scalar value from the config file on disk.
67+
value := func(path ...string) string {
68+
GinkgoHelper()
69+
70+
node := cfg.Read(path...)
71+
Expect(node).ToNot(BeNil(), "expected %v in config file", path)
72+
73+
return node.YNode().Value
74+
}
75+
76+
BeforeEach(func() {
77+
cfg = fcfg.NewTempConfig()
78+
})
79+
80+
Context("set", func() {
81+
It("should write a toolchain variable and keep existing keys", func() {
82+
run("set", "toolchain.CC=clang")
83+
Expect(stdout.String()).To(BeEmpty())
84+
Expect(stderr.String()).To(BeEmpty())
85+
86+
Expect(value("toolchain", "CC")).To(Equal("clang"))
87+
Expect(cfg.Read("paths", "manifests")).ToNot(BeNil())
88+
Expect(cfg.Read("paths", "sources")).ToNot(BeNil())
89+
})
90+
91+
It("should overwrite an existing value", func() {
92+
run("set", "toolchain.CC=clang")
93+
run("set", "toolchain.CC=gcc")
94+
95+
Expect(value("toolchain", "CC")).To(Equal("gcc"))
96+
})
97+
98+
It("should set several keys in one call", func() {
99+
run("set", "toolchain.CC=clang", "collect_anonymous_telemetry=true")
100+
101+
Expect(value("toolchain", "CC")).To(Equal("clang"))
102+
Expect(value("collect_anonymous_telemetry")).To(Equal("true"))
103+
})
104+
})
105+
106+
Context("list", func() {
107+
It("should print set values as key=value lines", func() {
108+
run("set", "toolchain.CC=clang", "toolchain.CXX=clang++", "collect_anonymous_telemetry=true")
109+
110+
lines := list()
111+
Expect(lines).To(ContainElements(
112+
"toolchain.CC=clang",
113+
"toolchain.CXX=clang++",
114+
"collect_anonymous_telemetry=true",
115+
))
116+
})
117+
118+
It("should not print the config directory", func() {
119+
Expect(list()).ToNot(ContainElement(HavePrefix("paths.config=")))
120+
})
121+
})
122+
123+
Context("unset", func() {
124+
It("should remove a toolchain variable from the file and from list", func() {
125+
run("set", "toolchain.CC=clang")
126+
run("unset", "toolchain.CC")
127+
128+
Expect(cfg.Read("toolchain", "CC")).To(BeNil())
129+
Expect(list()).ToNot(ContainElement(HavePrefix("toolchain.CC=")))
130+
})
131+
132+
It("should only remove the given key", func() {
133+
run("set", "toolchain.CC=clang", "toolchain.UK_CFLAGS=-O2")
134+
run("unset", "toolchain.CC")
135+
136+
Expect(cfg.Read("toolchain", "CC")).To(BeNil())
137+
Expect(value("toolchain", "UK_CFLAGS")).To(Equal("-O2"))
138+
Expect(value("paths", "manifests")).ToNot(BeEmpty())
139+
140+
lines := list()
141+
Expect(lines).To(ContainElement("toolchain.UK_CFLAGS=-O2"))
142+
Expect(lines).ToNot(ContainElement(HavePrefix("toolchain.CC=")))
143+
})
144+
145+
It("should remove several keys in one call", func() {
146+
run("set", "toolchain.CC=clang", "toolchain.UK_CFLAGS=-O2")
147+
run("unset", "toolchain.CC", "toolchain.UK_CFLAGS")
148+
149+
Expect(cfg.Read("toolchain", "CC")).To(BeNil())
150+
Expect(cfg.Read("toolchain", "UK_CFLAGS")).To(BeNil())
151+
Expect(list()).ToNot(ContainElement(HavePrefix("toolchain.")))
152+
})
153+
154+
It("should keep the key removed after a later set of another key", func() {
155+
run("set", "toolchain.CC=clang")
156+
run("unset", "toolchain.CC")
157+
run("set", "toolchain.UK_CFLAGS=-O2")
158+
159+
Expect(cfg.Read("toolchain", "CC")).To(BeNil())
160+
Expect(value("toolchain", "UK_CFLAGS")).To(Equal("-O2"))
161+
})
162+
163+
It("should succeed when the toolchain key does not exist", func() {
164+
run("unset", "toolchain.DOES_NOT_EXIST")
165+
166+
Expect(cfg.Read("toolchain", "DOES_NOT_EXIST")).To(BeNil())
167+
})
168+
})
169+
170+
Context("error cases", func() {
171+
expectFailure := func(substr string, args ...string) {
172+
GinkgoHelper()
173+
174+
cmd := kraft(args...)
175+
err := cmd.Run()
176+
Expect(err).To(HaveOccurred())
177+
Expect(err).To(MatchError("exit status 1"))
178+
179+
Expect(stdout.String()).To(BeEmpty())
180+
Expect(stderr.String()).To(ContainSubstring(substr))
181+
}
182+
183+
It("should reject set without a value", func() {
184+
expectFailure("invalid argument: expected KEY=VALUE", "set", "toolchain.CC")
185+
})
186+
187+
It("should reject set of an unknown key", func() {
188+
expectFailure("invalid key: does_not_exist", "set", "does_not_exist=1")
189+
})
190+
191+
It("should reject set of a bool with a non-bool value", func() {
192+
expectFailure("unsupported type conversion", "set", "no_prompt=maybe")
193+
})
194+
195+
It("should reject unset of an unknown key", func() {
196+
expectFailure("invalid key: does_not_exist", "unset", "does_not_exist")
197+
})
198+
199+
It("should reject set without arguments", func() {
200+
expectFailure("requires at least 1 arg", "set")
201+
})
202+
203+
It("should reject unset without arguments", func() {
204+
expectFailure("requires at least 1 arg", "unset")
205+
})
206+
207+
It("should reject list with an argument", func() {
208+
expectFailure("unknown command", "list", "some-arg")
209+
})
210+
})
211+
212+
Context("help", func() {
213+
It("should print system help", func() {
214+
cmd := kraft("--help")
215+
err := cmd.Run()
216+
Expect(err).ToNot(HaveOccurred())
217+
Expect(stderr.String()).To(BeEmpty())
218+
Expect(stdout.String()).To(MatchRegexp(`(?m)^Manage KraftKit and host system$`))
219+
Expect(stdout.String()).To(MatchRegexp(`(?m)^USAGE$`))
220+
Expect(stdout.String()).To(MatchRegexp(`(?m)^ kraft system SUBCOMMAND$`))
221+
})
222+
223+
It("should print set help", func() {
224+
cmd := kraft("set", "--help")
225+
err := cmd.Run()
226+
Expect(err).ToNot(HaveOccurred())
227+
Expect(stderr.String()).To(BeEmpty())
228+
Expect(stdout.String()).To(MatchRegexp(`(?m)^Set a KraftKit configuration option$`))
229+
Expect(stdout.String()).To(MatchRegexp(`(?m)^USAGE$`))
230+
Expect(stdout.String()).To(MatchRegexp(`(?m)^ kraft system set KEY=VALUE$`))
231+
})
232+
233+
It("should print list help", func() {
234+
cmd := kraft("list", "--help")
235+
err := cmd.Run()
236+
Expect(err).ToNot(HaveOccurred())
237+
Expect(stderr.String()).To(BeEmpty())
238+
Expect(stdout.String()).To(MatchRegexp(`(?m)^List all KraftKit configuration options and their current values\.$`))
239+
Expect(stdout.String()).To(MatchRegexp(`(?m)^USAGE$`))
240+
Expect(stdout.String()).To(MatchRegexp(`(?m)^ kraft system list \[FLAGS\]$`))
241+
})
242+
243+
It("should print unset help", func() {
244+
cmd := kraft("unset", "--help")
245+
err := cmd.Run()
246+
Expect(err).ToNot(HaveOccurred())
247+
Expect(stderr.String()).To(BeEmpty())
248+
Expect(stdout.String()).To(MatchRegexp(`(?m)^Unset a KraftKit configuration option$`))
249+
Expect(stdout.String()).To(MatchRegexp(`(?m)^USAGE$`))
250+
Expect(stdout.String()).To(MatchRegexp(`(?m)^ kraft system unset KEY \[KEY \.\.\.\]$`))
251+
})
252+
})
253+
})

0 commit comments

Comments
 (0)