1package cmd
2
3import (
4 "bytes"
5 "os"
6 "path/filepath"
7 "testing"
8
9 "github.com/spf13/cobra"
10 "github.com/spf13/viper"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13)
14
15func TestRootCommand(t *testing.T) {
16 tests := []struct {
17 name string
18 args []string
19 }{
20 {
21 name: "no arguments shows help",
22 args: []string{},
23 },
24 {
25 name: "help flag",
26 args: []string{"--help"},
27 },
28 }
29
30 for _, tt := range tests {
31 t.Run(tt.name, func(t *testing.T) {
32 // Create new command to avoid state pollution
33 cmd := &cobra.Command{
34 Use: "gordon",
35 Short: "Gordon - Container Registry & Deployment System",
36 Long: `Gordon is a single-binary application that combines a container registry
37with an intelligent reverse proxy and automated deployment system.`,
38 }
39
40 cmd.SetArgs(tt.args)
41
42 var output bytes.Buffer
43 cmd.SetOut(&output)
44 cmd.SetErr(&output)
45
46 // Should not panic
47 assert.NotPanics(t, func() {
48 cmd.Execute()
49 })
50 })
51 }
52}
53
54func TestExecute(t *testing.T) {
55 // Create a temporary config file to avoid config errors
56 tempDir := t.TempDir()
57 configFile := filepath.Join(tempDir, "gordon.toml")
58
59 configContent := `
60[server]
61port = 8080
62registry_port = 5000
63runtime = "docker"
64
65[logging]
66enabled = false
67
68[routes]
69`
70 err := os.WriteFile(configFile, []byte(configContent), 0644)
71 require.NoError(t, err)
72
73 // Save original values
74 originalArgs := os.Args
75 originalCfgFile := cfgFile
76
77 // Test with help flag to avoid starting actual server
78 os.Args = []string{"gordon", "--help"}
79 cfgFile = configFile
80
81 // Restore after test
82 defer func() {
83 os.Args = originalArgs
84 cfgFile = originalCfgFile
85 viper.Reset()
86 }()
87
88 // Should not panic
89 assert.NotPanics(t, func() {
90 Execute()
91 })
92}
93
94func TestInitConfig(t *testing.T) {
95 tests := []struct {
96 name string
97 setupFunc func(t *testing.T) string // returns temp dir
98 configFile string
99 configContent string
100 expectedError bool
101 shouldFindFile bool
102 }{
103 {
104 name: "explicit config file",
105 setupFunc: func(t *testing.T) string {
106 return t.TempDir()
107 },
108 configFile: "test-gordon.toml",
109 configContent: `
110[server]
111port = 8080
112registry_port = 5000
113runtime = "docker"
114
115[logging]
116enabled = false
117
118[routes]
119`,
120 shouldFindFile: true,
121 },
122 {
123 name: "config file in current directory",
124 setupFunc: func(t *testing.T) string {
125 tempDir := t.TempDir()
126 // Change to temp directory
127 originalDir, _ := os.Getwd()
128 os.Chdir(tempDir)
129 t.Cleanup(func() {
130 os.Chdir(originalDir)
131 })
132 return tempDir
133 },
134 configFile: "gordon.toml",
135 configContent: `
136[server]
137port = 8080
138registry_port = 5000
139runtime = "docker"
140
141[logging]
142enabled = false
143
144[routes]
145`,
146 shouldFindFile: true,
147 },
148 }
149
150 for _, tt := range tests {
151 t.Run(tt.name, func(t *testing.T) {
152 // Reset viper for each test
153 viper.Reset()
154
155 tempDir := tt.setupFunc(t)
156
157 // Save original values
158 originalCfgFile := cfgFile
159
160 if tt.configContent != "" {
161 configPath := tt.configFile
162 if !filepath.IsAbs(configPath) {
163 configPath = filepath.Join(tempDir, tt.configFile)
164 }
165
166 err := os.WriteFile(configPath, []byte(tt.configContent), 0644)
167 require.NoError(t, err)
168
169 if tt.name == "explicit config file" {
170 cfgFile = configPath
171 }
172 }
173
174 // Restore after test
175 t.Cleanup(func() {
176 cfgFile = originalCfgFile
177 viper.Reset()
178 })
179
180 assert.NotPanics(t, func() {
181 initConfig()
182 })
183
184 if tt.shouldFindFile {
185 assert.NotEmpty(t, viper.ConfigFileUsed())
186 }
187 })
188 }
189}
190
191func TestRootCmdStructure(t *testing.T) {
192 // Test root command basic properties
193 assert.Equal(t, "gordon", rootCmd.Use)
194 assert.Contains(t, rootCmd.Short, "Gordon")
195 assert.Contains(t, rootCmd.Long, "container registry")
196
197 // Test that persistent flags are set
198 flag := rootCmd.PersistentFlags().Lookup("config")
199 assert.NotNil(t, flag)
200 assert.Equal(t, "config", flag.Name)
201}
202
203func TestRootCmdSubcommands(t *testing.T) {
204 // Check that expected subcommands are registered
205 commands := rootCmd.Commands()
206
207 commandNames := make([]string, len(commands))
208 for i, cmd := range commands {
209 commandNames[i] = cmd.Name()
210 }
211
212 // Should have start and reload commands
213 assert.Contains(t, commandNames, "start")
214 assert.Contains(t, commandNames, "reload")
215}
216
217func TestConfigFileFlag(t *testing.T) {
218 tempDir := t.TempDir()
219 configFile := filepath.Join(tempDir, "test-config.toml")
220
221 configContent := `
222[server]
223port = 9999
224registry_port = 6000
225runtime = "docker"
226
227[logging]
228enabled = false
229
230[routes]
231`
232 err := os.WriteFile(configFile, []byte(configContent), 0644)
233 require.NoError(t, err)
234
235 // Reset viper and cfgFile
236 viper.Reset()
237 originalCfgFile := cfgFile
238
239 defer func() {
240 cfgFile = originalCfgFile
241 viper.Reset()
242 }()
243
244 // Test setting config file via flag
245 cfgFile = configFile
246
247 assert.NotPanics(t, func() {
248 initConfig()
249 })
250
251 // Should use the specified config file
252 assert.Equal(t, configFile, viper.ConfigFileUsed())
253}
254
255func TestRootCmdHelp(t *testing.T) {
256 var output bytes.Buffer
257 rootCmd.SetOut(&output)
258 rootCmd.SetArgs([]string{"--help"})
259
260 err := rootCmd.Execute()
261 assert.NoError(t, err)
262
263 helpOutput := output.String()
264 assert.Contains(t, helpOutput, "Gordon")
265 assert.Contains(t, helpOutput, "container registry")
266 assert.Contains(t, helpOutput, "Available Commands:")
267 assert.Contains(t, helpOutput, "start")
268 assert.Contains(t, helpOutput, "reload")
269}