@@ -3,6 +3,8 @@ package terraform
33import (
44 "testing"
55
6+ "github.com/spf13/cobra"
7+ "github.com/spf13/viper"
68 "github.com/stretchr/testify/assert"
79 "github.com/stretchr/testify/require"
810
@@ -14,7 +16,7 @@ func TestTerraformFlags(t *testing.T) {
1416
1517 // Should have common flags (stack, dry-run) + Terraform-specific flags including identity.
1618 // Note: from-plan is defined in apply.go and deploy.go with NoOptDefVal, not here.
17- assert .GreaterOrEqual (t , registry .Count (), 12 )
19+ assert .GreaterOrEqual (t , registry .Count (), 17 )
1820
1921 // Should include common flags.
2022 assert .True (t , registry .Has ("stack" ))
@@ -35,6 +37,14 @@ func TestTerraformFlags(t *testing.T) {
3537 assert .True (t , registry .Has ("query" ))
3638 assert .True (t , registry .Has ("components" ))
3739
40+ // Should include execution flags for controlling terraform behavior.
41+ // These flags override atmos.yaml settings and are used by various terraform subcommands.
42+ assert .True (t , registry .Has ("auto-generate-backend-file" ), "auto-generate-backend-file should be in TerraformFlags" )
43+ assert .True (t , registry .Has ("deploy-run-init" ), "deploy-run-init should be in TerraformFlags" )
44+ assert .True (t , registry .Has ("init-run-reconfigure" ), "init-run-reconfigure should be in TerraformFlags" )
45+ assert .True (t , registry .Has ("planfile" ), "planfile should be in TerraformFlags" )
46+ assert .True (t , registry .Has ("skip-planfile" ), "skip-planfile should be in TerraformFlags" )
47+
3848 // Check upload-status flag.
3949 uploadFlag := registry .Get ("upload-status" )
4050 require .NotNil (t , uploadFlag )
@@ -76,10 +86,15 @@ func TestWithTerraformFlags(t *testing.T) {
7686 registry := parser .Registry ()
7787
7888 // Should have all terraform flags.
79- assert .GreaterOrEqual (t , registry .Count (), 12 )
89+ assert .GreaterOrEqual (t , registry .Count (), 17 )
8090 assert .True (t , registry .Has ("stack" ))
8191 assert .True (t , registry .Has ("upload-status" ))
8292 assert .True (t , registry .Has ("identity" ))
93+
94+ // Verify execution flags are present.
95+ assert .True (t , registry .Has ("auto-generate-backend-file" ))
96+ assert .True (t , registry .Has ("deploy-run-init" ))
97+ assert .True (t , registry .Has ("init-run-reconfigure" ))
8398}
8499
85100func TestWithTerraformAffectedFlags (t * testing.T ) {
@@ -107,7 +122,7 @@ func TestCombinedTerraformFlags(t *testing.T) {
107122 registry := parser .Registry ()
108123
109124 // Should have all flags from both registries.
110- assert .GreaterOrEqual (t , registry .Count (), 19 )
125+ assert .GreaterOrEqual (t , registry .Count (), 24 )
111126
112127 // Should include terraform flags.
113128 assert .True (t , registry .Has ("stack" ))
@@ -118,4 +133,283 @@ func TestCombinedTerraformFlags(t *testing.T) {
118133 assert .True (t , registry .Has ("repo-path" ))
119134 assert .True (t , registry .Has ("ref" ))
120135 assert .True (t , registry .Has ("include-dependents" ))
136+
137+ // Should include execution flags for controlling terraform behavior.
138+ assert .True (t , registry .Has ("auto-generate-backend-file" ))
139+ assert .True (t , registry .Has ("deploy-run-init" ))
140+ assert .True (t , registry .Has ("init-run-reconfigure" ))
141+ assert .True (t , registry .Has ("planfile" ))
142+ assert .True (t , registry .Has ("skip-planfile" ))
143+ }
144+
145+ // TestExecutionFlagsProperties verifies that all execution flags have correct properties.
146+ // These flags control terraform execution behavior and override atmos.yaml settings.
147+ func TestExecutionFlagsProperties (t * testing.T ) {
148+ registry := TerraformFlags ()
149+
150+ // Test skip-init flag (bool flag).
151+ t .Run ("skip-init" , func (t * testing.T ) {
152+ flag := registry .Get ("skip-init" )
153+ require .NotNil (t , flag , "skip-init flag should be registered" )
154+ boolFlag , ok := flag .(* flags.BoolFlag )
155+ require .True (t , ok , "skip-init should be a BoolFlag" )
156+ assert .Equal (t , false , boolFlag .Default , "skip-init default should be false" )
157+ assert .Equal (t , []string {"ATMOS_SKIP_INIT" }, boolFlag .EnvVars , "skip-init should have ATMOS_SKIP_INIT env var" )
158+ })
159+
160+ // Test auto-generate-backend-file flag (string flag for true/false override).
161+ t .Run ("auto-generate-backend-file" , func (t * testing.T ) {
162+ flag := registry .Get ("auto-generate-backend-file" )
163+ require .NotNil (t , flag , "auto-generate-backend-file flag should be registered" )
164+ strFlag , ok := flag .(* flags.StringFlag )
165+ require .True (t , ok , "auto-generate-backend-file should be a StringFlag" )
166+ assert .Equal (t , "" , strFlag .Default , "auto-generate-backend-file default should be empty" )
167+ assert .Equal (t , []string {"ATMOS_AUTO_GENERATE_BACKEND_FILE" }, strFlag .EnvVars )
168+ })
169+
170+ // Test deploy-run-init flag (string flag for true/false override).
171+ t .Run ("deploy-run-init" , func (t * testing.T ) {
172+ flag := registry .Get ("deploy-run-init" )
173+ require .NotNil (t , flag , "deploy-run-init flag should be registered" )
174+ strFlag , ok := flag .(* flags.StringFlag )
175+ require .True (t , ok , "deploy-run-init should be a StringFlag" )
176+ assert .Equal (t , "" , strFlag .Default , "deploy-run-init default should be empty" )
177+ assert .Equal (t , []string {"ATMOS_DEPLOY_RUN_INIT" }, strFlag .EnvVars )
178+ })
179+
180+ // Test init-run-reconfigure flag (string flag for true/false override).
181+ t .Run ("init-run-reconfigure" , func (t * testing.T ) {
182+ flag := registry .Get ("init-run-reconfigure" )
183+ require .NotNil (t , flag , "init-run-reconfigure flag should be registered" )
184+ strFlag , ok := flag .(* flags.StringFlag )
185+ require .True (t , ok , "init-run-reconfigure should be a StringFlag" )
186+ assert .Equal (t , "" , strFlag .Default , "init-run-reconfigure default should be empty" )
187+ assert .Equal (t , []string {"ATMOS_INIT_RUN_RECONFIGURE" }, strFlag .EnvVars )
188+ })
189+
190+ // Test init-pass-vars flag (bool flag).
191+ t .Run ("init-pass-vars" , func (t * testing.T ) {
192+ flag := registry .Get ("init-pass-vars" )
193+ require .NotNil (t , flag , "init-pass-vars flag should be registered" )
194+ boolFlag , ok := flag .(* flags.BoolFlag )
195+ require .True (t , ok , "init-pass-vars should be a BoolFlag" )
196+ assert .Equal (t , false , boolFlag .Default , "init-pass-vars default should be false" )
197+ assert .Equal (t , []string {"ATMOS_INIT_PASS_VARS" }, boolFlag .EnvVars )
198+ })
199+
200+ // Test planfile flag (string flag for path).
201+ t .Run ("planfile" , func (t * testing.T ) {
202+ flag := registry .Get ("planfile" )
203+ require .NotNil (t , flag , "planfile flag should be registered" )
204+ strFlag , ok := flag .(* flags.StringFlag )
205+ require .True (t , ok , "planfile should be a StringFlag" )
206+ assert .Equal (t , "" , strFlag .Default , "planfile default should be empty" )
207+ assert .Equal (t , []string {"ATMOS_PLANFILE" }, strFlag .EnvVars )
208+ })
209+
210+ // Test skip-planfile flag (string flag for true/false override).
211+ t .Run ("skip-planfile" , func (t * testing.T ) {
212+ flag := registry .Get ("skip-planfile" )
213+ require .NotNil (t , flag , "skip-planfile flag should be registered" )
214+ strFlag , ok := flag .(* flags.StringFlag )
215+ require .True (t , ok , "skip-planfile should be a StringFlag" )
216+ assert .Equal (t , "" , strFlag .Default , "skip-planfile default should be empty" )
217+ assert .Equal (t , []string {"ATMOS_SKIP_PLANFILE" }, strFlag .EnvVars )
218+ })
219+
220+ // Test append-user-agent flag (string flag).
221+ t .Run ("append-user-agent" , func (t * testing.T ) {
222+ flag := registry .Get ("append-user-agent" )
223+ require .NotNil (t , flag , "append-user-agent flag should be registered" )
224+ strFlag , ok := flag .(* flags.StringFlag )
225+ require .True (t , ok , "append-user-agent should be a StringFlag" )
226+ assert .Equal (t , "" , strFlag .Default , "append-user-agent default should be empty" )
227+ assert .Equal (t , []string {"ATMOS_APPEND_USER_AGENT" }, strFlag .EnvVars )
228+ })
229+
230+ // Test upload-status flag (bool flag).
231+ t .Run ("upload-status" , func (t * testing.T ) {
232+ flag := registry .Get ("upload-status" )
233+ require .NotNil (t , flag , "upload-status flag should be registered" )
234+ boolFlag , ok := flag .(* flags.BoolFlag )
235+ require .True (t , ok , "upload-status should be a BoolFlag" )
236+ assert .Equal (t , false , boolFlag .Default , "upload-status default should be false" )
237+ assert .Equal (t , []string {"ATMOS_UPLOAD_STATUS" }, boolFlag .EnvVars )
238+ })
239+ }
240+
241+ // TestFlagsCobraRegistration verifies that flags are properly registered on Cobra commands.
242+ // This test ensures the full pipeline from flag definition to CLI availability works.
243+ func TestFlagsCobraRegistration (t * testing.T ) {
244+ t .Run ("execution flags are visible on cobra command" , func (t * testing.T ) {
245+ cmd := & cobra.Command {Use : "test" }
246+ parser := flags .NewStandardParser (WithTerraformFlags ())
247+ parser .RegisterFlags (cmd )
248+
249+ // Verify all execution flags are registered and visible.
250+ executionFlags := []string {
251+ "skip-init" ,
252+ "auto-generate-backend-file" ,
253+ "deploy-run-init" ,
254+ "init-run-reconfigure" ,
255+ "planfile" ,
256+ "skip-planfile" ,
257+ "upload-status" ,
258+ "init-pass-vars" ,
259+ "append-user-agent" ,
260+ }
261+
262+ for _ , flagName := range executionFlags {
263+ flag := cmd .Flags ().Lookup (flagName )
264+ assert .NotNil (t , flag , "%s flag should be registered on cobra command" , flagName )
265+ }
266+ })
267+
268+ t .Run ("affected flags are visible on cobra command" , func (t * testing.T ) {
269+ cmd := & cobra.Command {Use : "test" }
270+ parser := flags .NewStandardParser (WithTerraformAffectedFlags ())
271+ parser .RegisterFlags (cmd )
272+
273+ // Verify all affected flags are registered.
274+ affectedFlags := []string {
275+ "repo-path" ,
276+ "ref" ,
277+ "sha" ,
278+ "ssh-key" ,
279+ "ssh-key-password" ,
280+ "include-dependents" ,
281+ "clone-target-ref" ,
282+ }
283+
284+ for _ , flagName := range affectedFlags {
285+ flag := cmd .Flags ().Lookup (flagName )
286+ assert .NotNil (t , flag , "%s flag should be registered on cobra command" , flagName )
287+ }
288+ })
289+ }
290+
291+ // TestFlagsViperBinding verifies that flags are properly bound to Viper for value retrieval.
292+ func TestFlagsViperBinding (t * testing.T ) {
293+ t .Run ("execution flags bind to viper" , func (t * testing.T ) {
294+ cmd := & cobra.Command {Use : "test" }
295+ v := viper .New ()
296+ parser := flags .NewStandardParser (WithTerraformFlags ())
297+ parser .RegisterFlags (cmd )
298+ err := parser .BindToViper (v )
299+ require .NoError (t , err )
300+
301+ // Set values via viper and verify they can be retrieved.
302+ v .Set ("skip-init" , true )
303+ v .Set ("auto-generate-backend-file" , "false" )
304+ v .Set ("planfile" , "/path/to/plan.tfplan" )
305+
306+ assert .True (t , v .GetBool ("skip-init" ))
307+ assert .Equal (t , "false" , v .GetString ("auto-generate-backend-file" ))
308+ assert .Equal (t , "/path/to/plan.tfplan" , v .GetString ("planfile" ))
309+ })
310+
311+ t .Run ("affected flags bind to viper" , func (t * testing.T ) {
312+ cmd := & cobra.Command {Use : "test" }
313+ v := viper .New ()
314+ parser := flags .NewStandardParser (WithTerraformAffectedFlags ())
315+ parser .RegisterFlags (cmd )
316+ err := parser .BindToViper (v )
317+ require .NoError (t , err )
318+
319+ // Set values via viper and verify they can be retrieved.
320+ v .Set ("repo-path" , "/path/to/repo" )
321+ v .Set ("ref" , "main" )
322+ v .Set ("include-dependents" , true )
323+
324+ assert .Equal (t , "/path/to/repo" , v .GetString ("repo-path" ))
325+ assert .Equal (t , "main" , v .GetString ("ref" ))
326+ assert .True (t , v .GetBool ("include-dependents" ))
327+ })
328+ }
329+
330+ // TestFlagsEnvironmentVariables verifies that environment variables are properly configured.
331+ func TestFlagsEnvironmentVariables (t * testing.T ) {
332+ t .Run ("execution flags have correct env var bindings" , func (t * testing.T ) {
333+ registry := TerraformFlags ()
334+
335+ envVarTests := []struct {
336+ flagName string
337+ envVar string
338+ }{
339+ {"skip-init" , "ATMOS_SKIP_INIT" },
340+ {"auto-generate-backend-file" , "ATMOS_AUTO_GENERATE_BACKEND_FILE" },
341+ {"deploy-run-init" , "ATMOS_DEPLOY_RUN_INIT" },
342+ {"init-run-reconfigure" , "ATMOS_INIT_RUN_RECONFIGURE" },
343+ {"planfile" , "ATMOS_PLANFILE" },
344+ {"skip-planfile" , "ATMOS_SKIP_PLANFILE" },
345+ {"upload-status" , "ATMOS_UPLOAD_STATUS" },
346+ {"init-pass-vars" , "ATMOS_INIT_PASS_VARS" },
347+ {"append-user-agent" , "ATMOS_APPEND_USER_AGENT" },
348+ }
349+
350+ for _ , tc := range envVarTests {
351+ flag := registry .Get (tc .flagName )
352+ require .NotNil (t , flag , "%s flag should exist" , tc .flagName )
353+
354+ var envVars []string
355+ switch f := flag .(type ) {
356+ case * flags.BoolFlag :
357+ envVars = f .EnvVars
358+ case * flags.StringFlag :
359+ envVars = f .EnvVars
360+ }
361+
362+ assert .Contains (t , envVars , tc .envVar ,
363+ "%s flag should have %s env var" , tc .flagName , tc .envVar )
364+ }
365+ })
366+
367+ t .Run ("affected flags have correct env var bindings" , func (t * testing.T ) {
368+ registry := TerraformAffectedFlags ()
369+
370+ envVarTests := []struct {
371+ flagName string
372+ envVar string
373+ }{
374+ {"repo-path" , "ATMOS_REPO_PATH" },
375+ {"ref" , "ATMOS_REF" },
376+ {"sha" , "ATMOS_SHA" },
377+ {"ssh-key" , "ATMOS_SSH_KEY" },
378+ {"ssh-key-password" , "ATMOS_SSH_KEY_PASSWORD" },
379+ {"include-dependents" , "ATMOS_INCLUDE_DEPENDENTS" },
380+ {"clone-target-ref" , "ATMOS_CLONE_TARGET_REF" },
381+ }
382+
383+ for _ , tc := range envVarTests {
384+ flag := registry .Get (tc .flagName )
385+ require .NotNil (t , flag , "%s flag should exist" , tc .flagName )
386+
387+ var envVars []string
388+ switch f := flag .(type ) {
389+ case * flags.BoolFlag :
390+ envVars = f .EnvVars
391+ case * flags.StringFlag :
392+ envVars = f .EnvVars
393+ }
394+
395+ assert .Contains (t , envVars , tc .envVar ,
396+ "%s flag should have %s env var" , tc .flagName , tc .envVar )
397+ }
398+ })
399+ }
400+
401+ // TestIdentityFlagConfiguration verifies the identity flag has correct NoOptDefVal for interactive selection.
402+ func TestIdentityFlagConfiguration (t * testing.T ) {
403+ registry := TerraformFlags ()
404+ flag := registry .Get ("identity" )
405+ require .NotNil (t , flag )
406+
407+ strFlag , ok := flag .(* flags.StringFlag )
408+ require .True (t , ok )
409+
410+ assert .Equal (t , "" , strFlag .Default , "identity default should be empty" )
411+ assert .Equal (t , "__SELECT__" , strFlag .NoOptDefVal , "identity NoOptDefVal should be __SELECT__ for interactive selection" )
412+ assert .Equal (t , "i" , strFlag .Shorthand , "identity should have -i shorthand" )
413+ assert .Contains (t , strFlag .EnvVars , "ATMOS_IDENTITY" )
414+ assert .Contains (t , strFlag .EnvVars , "IDENTITY" )
121415}
0 commit comments