Skip to content

Commit db28efe

Browse files
kanard38knard38
authored andcommitted
DAOS-18304 ddb: rewrite tests to use build-tag CGo stubs
Replace DdbContextStub (god interface) with the build-tag CGo stub infrastructure introduced in the previous commit: - Remove DdbContextStub and all its method fields - Add newTestContext(t) which resets all stubs via resetDdbStubs() and returns a *DdbContext ready for use in tests - Test cases set per-function _Fn hook variables directly instead of populating stub struct fields (e.g. ddb_run_open_Fn = func...) - All three test files now carry the //go:build test_stubs tag so they only compile when the stub infrastructure is present - Minor style fixes in command_completers_test.go Signed-off-by: Cedric Koch-Hofer <cedric.koch-hofer@hpe.com>
1 parent 4ff740d commit db28efe

4 files changed

Lines changed: 830 additions & 9 deletions

File tree

src/control/cmd/ddb/command_completers_test.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
//
2+
// (C) Copyright 2026 Hewlett Packard Enterprise Development LP
3+
//
4+
// SPDX-License-Identifier: BSD-2-Clause-Patent
5+
//
6+
17
package main
28

39
import (
@@ -18,7 +24,7 @@ func createFile(t *testing.T, filePath string) {
1824

1925
fd, err := os.Create(filePath)
2026
if err != nil {
21-
t.Fatalf("Failed to create test vos file %s: %v", filePath, err)
27+
t.Fatalf("failed to create test vos file %s: %v", filePath, err)
2228
}
2329
fd.Close()
2430
}
@@ -27,14 +33,14 @@ func createDirAll(t *testing.T, dirPath string) {
2733
t.Helper()
2834

2935
if err := os.MkdirAll(dirPath, 0755); err != nil {
30-
t.Fatalf("Failed to create test pool directory %s: %v", dirPath, err)
36+
t.Fatalf("failed to create test pool directory %s: %v", dirPath, err)
3137
}
3238
}
3339

34-
func testSetup(t *testing.T) (tmpDir string, teardown func()) {
40+
func testSetup(t *testing.T) string {
3541
t.Helper()
3642

37-
tmpDir, teardown = test.CreateTestDir(t)
43+
tmpDir := t.TempDir()
3844

3945
for _, dir := range testPoolDirs {
4046
createDirAll(t, filepath.Join(tmpDir, dir))
@@ -51,12 +57,11 @@ func testSetup(t *testing.T) (tmpDir string, teardown func()) {
5157
createDirAll(t, filepath.Join(tmpDir, "bar", "baz"))
5258
createFile(t, filepath.Join(tmpDir, "bar", "baz", "no_vos"))
5359

54-
return
60+
return tmpDir
5561
}
5662

5763
func TestListVosFiles(t *testing.T) {
58-
tmpDir, teardown := testSetup(t)
59-
t.Cleanup(teardown)
64+
tmpDir := testSetup(t)
6065

6166
for name, tc := range map[string]struct {
6267
args string
@@ -118,7 +123,7 @@ func TestListVosFiles(t *testing.T) {
118123
} {
119124
t.Run(name, func(t *testing.T) {
120125
results := listVosFiles(tc.args)
121-
test.AssertStringsEqual(t, tc.expRes, results, "listDirVos results do not match expected")
126+
test.AssertStringsEqual(t, tc.expRes, results, "unexpected listVosFiles results")
122127
})
123128
}
124129
}
@@ -169,7 +174,7 @@ func TestFilterSuggestions(t *testing.T) {
169174
} {
170175
t.Run(name, func(t *testing.T) {
171176
results := filterSuggestions(tc.prefix, initialSuggestions, additionalSuggestions)
172-
test.AssertStringsEqual(t, tc.expRes, results, "filterSuggestions results do not match expected")
177+
test.AssertStringsEqual(t, tc.expRes, results, "unexpected filterSuggestions results")
173178
})
174179
}
175180
}
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
//
2+
// (C) Copyright 2026 Hewlett Packard Enterprise Development LP
3+
//
4+
// SPDX-License-Identifier: BSD-2-Clause-Patent
5+
//
6+
7+
//go:build test_stubs
8+
9+
package main
10+
11+
import (
12+
"fmt"
13+
"os"
14+
"path"
15+
"path/filepath"
16+
"strings"
17+
"testing"
18+
19+
"github.com/daos-stack/daos/src/control/common/test"
20+
)
21+
22+
func runHelpCmd(t *testing.T, cmdStr string, helpSubStr string) {
23+
t.Helper()
24+
25+
ctx := newTestContext(t)
26+
27+
// Create a temporary config file with the help command
28+
tmpCfgDir := t.TempDir()
29+
tmpCfgFile := path.Join(tmpCfgDir, "ddb-cmd_file.txt")
30+
if err := os.WriteFile(tmpCfgFile, []byte(fmt.Sprintf("%s --help", cmdStr)), 0644); err != nil {
31+
t.Fatalf("failed to write temp config file: %v", err)
32+
}
33+
34+
// Run the help command with a command file
35+
args := test.JoinArgs(nil, "--cmd_file="+tmpCfgFile)
36+
stdoutCmdFile, err := runMainFlow(ctx, args)
37+
if err != nil {
38+
t.Fatalf("unexpected error when running '%s --help' via command file: want nil, got %v", cmdStr, err)
39+
}
40+
test.AssertTrue(t, strings.Contains(stdoutCmdFile, helpSubStr),
41+
fmt.Sprintf("expected stdout to contain %q: got\n%s", helpSubStr, stdoutCmdFile))
42+
43+
// Run the help command with a command line
44+
args = test.JoinArgs(nil, cmdStr, "--help")
45+
stdoutCmdLine, err := runMainFlow(ctx, args)
46+
if err != nil {
47+
t.Fatalf("unexpected error when running '%s --help' via command line: want nil, got %v", cmdStr, err)
48+
}
49+
test.AssertTrue(t, strings.Contains(stdoutCmdLine, helpSubStr),
50+
fmt.Sprintf("expected stdout to contain %q: got\n%s", helpSubStr, stdoutCmdLine))
51+
52+
// Compare command line and command file outputs
53+
test.AssertEqual(t, stdoutCmdFile, stdoutCmdLine,
54+
fmt.Sprintf("unexpected help output mismatch between command file and command line for '%s'", cmdStr))
55+
}
56+
57+
func TestHelpCmds(t *testing.T) {
58+
for name, tc := range map[string]struct {
59+
cmdStr string
60+
helpSubStr string
61+
}{
62+
"help for 'ls' command": {
63+
cmdStr: "ls",
64+
helpSubStr: "Usage:\n ls [flags] [path]\n",
65+
},
66+
"help for 'open' command": {
67+
cmdStr: "open",
68+
helpSubStr: "Usage:\n open [flags] path\n",
69+
},
70+
} {
71+
t.Run(name, func(t *testing.T) {
72+
runHelpCmd(t, tc.cmdStr, tc.helpSubStr)
73+
})
74+
}
75+
}
76+
77+
func TestCmds(t *testing.T) {
78+
for name, tc := range map[string]struct {
79+
args []string
80+
setup func()
81+
expStdout []string
82+
expErr error
83+
}{
84+
"ls invalid options": {
85+
args: []string{"ls", "--bar"},
86+
expErr: ddbTestErr("invalid flag: --bar"),
87+
},
88+
"ls default": {
89+
args: []string{"ls"},
90+
setup: func() {
91+
ddb_run_ls_Fn = func(path string, recursive bool, details bool) error {
92+
fmt.Println("ls called")
93+
if err := isArgEqual("", path, "path"); err != nil {
94+
return err
95+
}
96+
if err := isArgEqual(false, recursive, "recursive"); err != nil {
97+
return err
98+
}
99+
if err := isArgEqual(false, details, "details"); err != nil {
100+
return err
101+
}
102+
return nil
103+
}
104+
},
105+
expStdout: []string{"ls called"},
106+
},
107+
"ls path": {
108+
args: []string{"ls", "/[0]"},
109+
setup: func() {
110+
ddb_run_ls_Fn = func(path string, recursive bool, details bool) error {
111+
fmt.Println("ls called")
112+
if err := isArgEqual("/[0]", path, "path"); err != nil {
113+
return err
114+
}
115+
if err := isArgEqual(false, recursive, "recursive"); err != nil {
116+
return err
117+
}
118+
if err := isArgEqual(false, details, "details"); err != nil {
119+
return err
120+
}
121+
return nil
122+
}
123+
},
124+
expStdout: []string{"ls called"},
125+
},
126+
"ls long recursive opt": {
127+
args: []string{"ls", "--recursive"},
128+
setup: func() {
129+
ddb_run_ls_Fn = func(path string, recursive bool, details bool) error {
130+
fmt.Println("ls called")
131+
if err := isArgEqual("", path, "path"); err != nil {
132+
return err
133+
}
134+
if err := isArgEqual(true, recursive, "recursive"); err != nil {
135+
return err
136+
}
137+
if err := isArgEqual(false, details, "details"); err != nil {
138+
return err
139+
}
140+
return nil
141+
}
142+
},
143+
expStdout: []string{"ls called"},
144+
},
145+
"ls short details opt": {
146+
args: []string{"ls", "-d"},
147+
setup: func() {
148+
ddb_run_ls_Fn = func(path string, recursive bool, details bool) error {
149+
fmt.Println("ls called")
150+
if err := isArgEqual("", path, "path"); err != nil {
151+
return err
152+
}
153+
if err := isArgEqual(false, recursive, "recursive"); err != nil {
154+
return err
155+
}
156+
if err := isArgEqual(true, details, "details"); err != nil {
157+
return err
158+
}
159+
return nil
160+
}
161+
},
162+
expStdout: []string{"ls called"},
163+
},
164+
"ls details long opt": {
165+
args: []string{"ls", "--details"},
166+
setup: func() {
167+
ddb_run_ls_Fn = func(path string, recursive bool, details bool) error {
168+
fmt.Println("ls called")
169+
if err := isArgEqual("", path, "path"); err != nil {
170+
return err
171+
}
172+
if err := isArgEqual(false, recursive, "recursive"); err != nil {
173+
return err
174+
}
175+
if err := isArgEqual(true, details, "details"); err != nil {
176+
return err
177+
}
178+
return nil
179+
}
180+
},
181+
expStdout: []string{"ls called"},
182+
},
183+
} {
184+
t.Run(name, func(t *testing.T) {
185+
checkCmd := func(t *testing.T, stdout string, err error) {
186+
t.Helper()
187+
test.CmpErr(t, tc.expErr, err)
188+
if tc.expErr != nil {
189+
return
190+
}
191+
for _, msg := range tc.expStdout {
192+
test.AssertTrue(t, strings.Contains(stdout, msg),
193+
fmt.Sprintf("expected stdout to contain %q: got\n%s", msg, stdout))
194+
}
195+
}
196+
197+
t.Run("command-line", func(t *testing.T) {
198+
ctx := newTestContext(t)
199+
if tc.setup != nil {
200+
tc.setup()
201+
}
202+
stdout, err := runMainFlow(ctx, tc.args)
203+
checkCmd(t, stdout, err)
204+
})
205+
206+
t.Run("command-file", func(t *testing.T) {
207+
tmpDir := t.TempDir()
208+
cmdFile := filepath.Join(tmpDir, "cmds.txt")
209+
cmdLine := strings.Join(tc.args, " ")
210+
if err := os.WriteFile(cmdFile, []byte(cmdLine), 0644); err != nil {
211+
t.Fatalf("failed to write command file: %v", err)
212+
}
213+
ctx := newTestContext(t)
214+
if tc.setup != nil {
215+
tc.setup()
216+
}
217+
stdout, err := runMainFlow(ctx, []string{"--cmd_file=" + cmdFile})
218+
checkCmd(t, stdout, err)
219+
})
220+
})
221+
}
222+
}
223+
224+
func TestManPage(t *testing.T) {
225+
for name, tc := range map[string]struct {
226+
args []string
227+
expStdout []string
228+
expErr error
229+
}{
230+
"manpage to stdout contains sections and commands": {
231+
args: []string{"manpage"},
232+
expStdout: []string{
233+
manArgsHeader,
234+
manCmdsHeader,
235+
manPathSection[:20],
236+
manMdOnSsdSection[:20],
237+
manLoggingSection[:20],
238+
// Known commands must appear in the listing
239+
".B ls\n",
240+
".B open\n",
241+
},
242+
},
243+
} {
244+
t.Run(name, func(t *testing.T) {
245+
ctx := newTestContext(t)
246+
stdout, err := runMainFlow(ctx, tc.args)
247+
test.CmpErr(t, tc.expErr, err)
248+
if tc.expErr != nil {
249+
return
250+
}
251+
for _, msg := range tc.expStdout {
252+
test.AssertTrue(t, strings.Contains(stdout, msg),
253+
fmt.Sprintf("expected stdout to contain %q: got\n%s", msg, stdout))
254+
}
255+
})
256+
}
257+
258+
// Test --output flag: man page is written to a file, stdout is empty.
259+
t.Run("manpage to file", func(t *testing.T) {
260+
tmpDir := t.TempDir()
261+
outFile := filepath.Join(tmpDir, "ddb.groff")
262+
263+
ctx := newTestContext(t)
264+
stdout, err := runMainFlow(ctx, []string{"manpage", "--output=" + outFile})
265+
if err != nil {
266+
t.Fatalf("unexpected error when running 'manpage --output': want nil, got %v", err)
267+
}
268+
test.AssertTrue(t, stdout == "",
269+
fmt.Sprintf("expected empty stdout when --output is set: got\n%s", stdout))
270+
271+
content, err := os.ReadFile(outFile)
272+
if err != nil {
273+
t.Fatalf("failed to read output file: %v", err)
274+
}
275+
for _, section := range []string{manArgsHeader, manCmdsHeader, manLoggingSection[:20]} {
276+
test.AssertTrue(t, strings.Contains(string(content), section),
277+
fmt.Sprintf("expected file to contain %q: got\n%s", section, string(content)))
278+
}
279+
})
280+
}

0 commit comments

Comments
 (0)