Skip to content

Commit ef75536

Browse files
authored
Updating TUI wizard (#564)
* Updating TUI wizard * Adding several tests
1 parent 3f66900 commit ef75536

File tree

5 files changed

+1360
-40
lines changed

5 files changed

+1360
-40
lines changed

cli/tools/context.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,27 @@ func newContextCopy() simplecobra.Commander {
8282
v := &domain.SimpleCommand{
8383
NameP: "copy",
8484
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *domain.RootCommand, args []string) error {
85+
// Guard against the validator not firing (defensive belt-and-suspenders).
86+
if len(args) < 2 {
87+
return errors.New("requires a src and destination argument")
88+
}
8589
src := args[0]
8690
dest := args[1]
8791
config.CopyContext(rootCmd.ConfigSvc(), src, dest)
8892
return nil
8993
},
90-
InitCFunc: func(cd *simplecobra.Commandeer, r *domain.RootCommand) error {
91-
cd.CobraCommand.Aliases = []string{"cp"}
92-
cd.CobraCommand.Args = func(cmd *cobra.Command, args []string) error {
94+
// WithCFunc runs during command-tree Init(), before any execution, so
95+
// cmd.Args set here is visible to Cobra's ValidateArgs call.
96+
// InitCFunc runs in PreRun(), which is after ValidateArgs — too late for
97+
// an Args validator to prevent RunFunc from being called.
98+
WithCFunc: func(cmd *cobra.Command, r *domain.RootCommand) {
99+
cmd.Aliases = []string{"cp"}
100+
cmd.Args = func(cmd *cobra.Command, args []string) error {
93101
if len(args) < 2 {
94102
return errors.New("requires a src and destination argument")
95103
}
96104
return nil
97105
}
98-
return nil
99106
},
100107
Short: "copy context <src> <dest>",
101108
Long: "copy contexts <src> <dest>",

cli/tools/context_test.go

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// context_test.go exercises the "gdg tools contexts" subcommand tree.
2+
// Tests follow the same pattern established in s3_test.go:
3+
// - SetupAndExecuteMockingServices for read-only / non-TUI commands
4+
// - Error-path commands swallow the cli.Execute error inside the closure
5+
// (SetupAndExecuteMockingServices would fail its assert.Nil otherwise)
6+
// and instead assert that the expected error text appears in the output.
7+
package tools_test
8+
9+
import (
10+
"strings"
11+
"testing"
12+
13+
"github.com/esnet/gdg/cli"
14+
"github.com/esnet/gdg/cli/domain"
15+
"github.com/esnet/gdg/internal/ports/mocks"
16+
"github.com/esnet/gdg/pkg/test_tooling"
17+
"github.com/stretchr/testify/assert"
18+
)
19+
20+
// ── Parent command ────────────────────────────────────────────────────────────
21+
22+
// TestContextsParentShowsHelp verifies that running "gdg tools contexts" with no
23+
// subcommand prints the help listing all child commands.
24+
func TestContextsParentShowsHelp(t *testing.T) {
25+
rootSvc := cli.NewRootService()
26+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
27+
return cli.Execute(rootSvc, []string{"tools", "contexts"}, optionMockSvc())
28+
}
29+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
30+
defer closeReader()
31+
32+
lower := strings.ToLower(outStr)
33+
assert.Contains(t, lower, "list")
34+
assert.Contains(t, lower, "new")
35+
assert.Contains(t, lower, "delete")
36+
assert.Contains(t, lower, "copy")
37+
}
38+
39+
// TestContextsAliasCtx verifies that the "ctx" alias resolves to the same command.
40+
func TestContextsAliasCtx(t *testing.T) {
41+
rootSvc := cli.NewRootService()
42+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
43+
return cli.Execute(rootSvc, []string{"tools", "ctx"}, optionMockSvc())
44+
}
45+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
46+
defer closeReader()
47+
48+
lower := strings.ToLower(outStr)
49+
assert.Contains(t, lower, "list")
50+
}
51+
52+
// ── contexts list ─────────────────────────────────────────────────────────────
53+
54+
// TestContextListShowsContextNames verifies that "contexts list" prints the
55+
// context names that are present in testing.yml.
56+
func TestContextListShowsContextNames(t *testing.T) {
57+
rootSvc := cli.NewRootService()
58+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
59+
return cli.Execute(rootSvc, []string{"tools", "contexts", "list"}, optionMockSvc())
60+
}
61+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
62+
defer closeReader()
63+
64+
lower := strings.ToLower(outStr)
65+
// testing.yml must contain at least a "testing" or "qa" context.
66+
assert.True(t,
67+
strings.Contains(lower, "testing") || strings.Contains(lower, "qa"),
68+
"list output should contain at least one context name from testing.yml")
69+
}
70+
71+
// TestContextListShowsActiveFlag verifies the table includes an "active" column.
72+
func TestContextListShowsActiveFlag(t *testing.T) {
73+
rootSvc := cli.NewRootService()
74+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
75+
return cli.Execute(rootSvc, []string{"tools", "contexts", "list"}, optionMockSvc())
76+
}
77+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
78+
defer closeReader()
79+
80+
lower := strings.ToLower(outStr)
81+
assert.Contains(t, lower, "active", "list table should have an 'active' column")
82+
}
83+
84+
// ── contexts show ─────────────────────────────────────────────────────────────
85+
86+
// TestContextShowPrintsCurrentContext verifies that "contexts show" prints some
87+
// YAML/config content for the active context.
88+
func TestContextShowPrintsCurrentContext(t *testing.T) {
89+
rootSvc := cli.NewRootService()
90+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
91+
return cli.Execute(rootSvc, []string{"tools", "contexts", "show"}, optionMockSvc())
92+
}
93+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
94+
defer closeReader()
95+
96+
// The output should contain config file path and YAML content markers.
97+
assert.Contains(t, outStr, "config file:", "show should print the config file path")
98+
}
99+
100+
// TestContextShowWithExplicitName verifies that "contexts show <name>" accepts
101+
// an optional argument.
102+
func TestContextShowWithExplicitName(t *testing.T) {
103+
rootSvc := cli.NewRootService()
104+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
105+
// "testing" is the context set by InterceptStdout via config.NewConfig("testing").
106+
return cli.Execute(rootSvc, []string{"tools", "contexts", "show", "testing"}, optionMockSvc())
107+
}
108+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
109+
defer closeReader()
110+
111+
assert.Contains(t, outStr, "config file:")
112+
}
113+
114+
// ── contexts new (no args → error) ───────────────────────────────────────────
115+
116+
// TestContextNewNoArgsReturnsError verifies that "contexts new" without an
117+
// argument returns an error and surfaces a meaningful message.
118+
func TestContextNewNoArgsReturnsError(t *testing.T) {
119+
rootSvc := cli.NewRootService()
120+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
121+
// Swallow the error so SetupAndExecuteMockingServices' assert.Nil doesn't
122+
// mark the test as failed; we check the output ourselves below.
123+
_ = cli.Execute(rootSvc, []string{"tools", "contexts", "new"}, optionMockSvc())
124+
return nil
125+
}
126+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
127+
defer closeReader()
128+
129+
lower := strings.ToLower(outStr)
130+
// cobra / simplecobra prints the error + usage on RunFunc error.
131+
assert.True(t,
132+
strings.Contains(lower, "context") || strings.Contains(lower, "error") || strings.Contains(lower, "requires"),
133+
"output should mention context name requirement")
134+
}
135+
136+
// ── contexts delete (no args → error) ────────────────────────────────────────
137+
138+
// TestContextDeleteNoArgsReturnsError verifies that "contexts delete" without
139+
// an argument returns an error.
140+
func TestContextDeleteNoArgsReturnsError(t *testing.T) {
141+
rootSvc := cli.NewRootService()
142+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
143+
_ = cli.Execute(rootSvc, []string{"tools", "contexts", "delete"}, optionMockSvc())
144+
return nil
145+
}
146+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
147+
defer closeReader()
148+
149+
lower := strings.ToLower(outStr)
150+
assert.True(t,
151+
strings.Contains(lower, "delete") || strings.Contains(lower, "requires") || strings.Contains(lower, "usage"),
152+
"output should reference the delete command or its usage")
153+
}
154+
155+
// TestContextDeleteAliasDelNoArgsReturnsError verifies the "del" alias works.
156+
func TestContextDeleteAliasDelNoArgsReturnsError(t *testing.T) {
157+
rootSvc := cli.NewRootService()
158+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
159+
_ = cli.Execute(rootSvc, []string{"tools", "contexts", "del"}, optionMockSvc())
160+
return nil
161+
}
162+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
163+
defer closeReader()
164+
165+
lower := strings.ToLower(outStr)
166+
assert.True(t,
167+
strings.Contains(lower, "delete") || strings.Contains(lower, "del") || strings.Contains(lower, "usage"),
168+
"del alias should resolve and produce output")
169+
}
170+
171+
// ── contexts set (no args → error) ───────────────────────────────────────────
172+
173+
// TestContextSetNoArgsReturnsError verifies that "contexts set" without an
174+
// argument returns an error.
175+
func TestContextSetNoArgsReturnsError(t *testing.T) {
176+
rootSvc := cli.NewRootService()
177+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
178+
_ = cli.Execute(rootSvc, []string{"tools", "contexts", "set"}, optionMockSvc())
179+
return nil
180+
}
181+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
182+
defer closeReader()
183+
184+
lower := strings.ToLower(outStr)
185+
assert.True(t,
186+
strings.Contains(lower, "set") || strings.Contains(lower, "requires") || strings.Contains(lower, "usage"),
187+
"output should reference the set command or its usage")
188+
}
189+
190+
// ── contexts copy (fewer than 2 args → error) ─────────────────────────────────
191+
192+
// TestContextCopyNoArgsReturnsError verifies that "contexts copy" without
193+
// arguments returns an error (args validator fires before RunFunc).
194+
func TestContextCopyNoArgsReturnsError(t *testing.T) {
195+
rootSvc := cli.NewRootService()
196+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
197+
_ = cli.Execute(rootSvc, []string{"tools", "contexts", "copy"}, optionMockSvc())
198+
return nil
199+
}
200+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
201+
defer closeReader()
202+
203+
lower := strings.ToLower(outStr)
204+
assert.True(t,
205+
strings.Contains(lower, "copy") || strings.Contains(lower, "requires") || strings.Contains(lower, "usage"),
206+
"output should reference the copy command or its usage")
207+
}
208+
209+
// TestContextCopyOneArgReturnsError verifies that "contexts copy <src>" (missing
210+
// destination) also fails argument validation.
211+
func TestContextCopyOneArgReturnsError(t *testing.T) {
212+
rootSvc := cli.NewRootService()
213+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
214+
_ = cli.Execute(rootSvc, []string{"tools", "contexts", "copy", "testing"}, optionMockSvc())
215+
return nil
216+
}
217+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
218+
defer closeReader()
219+
220+
lower := strings.ToLower(outStr)
221+
assert.True(t,
222+
strings.Contains(lower, "copy") || strings.Contains(lower, "requires") || strings.Contains(lower, "usage"),
223+
"output should reference the copy command or its usage")
224+
}
225+
226+
// TestContextCopyAliasCpReported verifies that the "cp" alias is registered for
227+
// the copy command (alias appears in help output).
228+
func TestContextCopyAliasCpReported(t *testing.T) {
229+
rootSvc := cli.NewRootService()
230+
execMe := func(_ *mocks.GrafanaService, optionMockSvc func() domain.RootOption) error {
231+
return cli.Execute(rootSvc, []string{"tools", "contexts", "copy", "--help"}, optionMockSvc())
232+
}
233+
outStr, closeReader := test_tooling.SetupAndExecuteMockingServices(t, execMe)
234+
defer closeReader()
235+
236+
lower := strings.ToLower(outStr)
237+
// cobra always prints aliases in the help page when they are set.
238+
assert.Contains(t, lower, "cp", "help output should mention the 'cp' alias")
239+
}

0 commit comments

Comments
 (0)