11package config_test
22
33import (
4- "runtime "
4+ "context "
55 "strings"
66 "testing"
7+ "time"
78
89 "github.com/gruntwork-io/terragrunt/pkg/config"
910 "github.com/gruntwork-io/terragrunt/test/helpers/logger"
11+ "github.com/stretchr/testify/require"
1012)
1113
12- // FuzzHCLStringHelpers: startswith / endswith / strcontains must never panic on any args shape .
14+ // FuzzHCLStringHelpers: wrong arity must return WrongNumberOfParamsError; otherwise the result must agree with the Go stdlib equivalent .
1315func FuzzHCLStringHelpers (f * testing.F ) {
1416 seeds := []string {
1517 "" ,
@@ -32,28 +34,53 @@ func FuzzHCLStringHelpers(f *testing.F) {
3234
3335 ctx , pctx := newTestParsingContext (t , "" )
3436
35- _ , _ = config .StartsWith (ctx , pctx , args )
36- _ , _ = config .EndsWith (ctx , pctx , args )
37- _ , _ = config .StrContains (ctx , pctx , args )
37+ swOut , swErr := config .StartsWith (ctx , pctx , args )
38+ ewOut , ewErr := config .EndsWith (ctx , pctx , args )
39+ scOut , scErr := config .StrContains (ctx , pctx , args )
40+
41+ if len (args ) != 2 {
42+ require .Error (t , swErr , "startswith with %d args must error" , len (args ))
43+ require .True (t , assertErrorType (t , config.WrongNumberOfParamsError {}, swErr ),
44+ "startswith expected WrongNumberOfParamsError, got %T: %v" , swErr , swErr )
45+ require .Error (t , ewErr , "endswith with %d args must error" , len (args ))
46+ require .True (t , assertErrorType (t , config.WrongNumberOfParamsError {}, ewErr ),
47+ "endswith expected WrongNumberOfParamsError, got %T: %v" , ewErr , ewErr )
48+ require .Error (t , scErr , "strcontains with %d args must error" , len (args ))
49+ require .True (t , assertErrorType (t , config.WrongNumberOfParamsError {}, scErr ),
50+ "strcontains expected WrongNumberOfParamsError, got %T: %v" , scErr , scErr )
51+
52+ return
53+ }
54+
55+ require .NoError (t , swErr , "startswith(%q,%q) must not error" , args [0 ], args [1 ])
56+ require .Equal (t , strings .HasPrefix (args [0 ], args [1 ]), swOut ,
57+ "startswith(%q,%q) must agree with strings.HasPrefix" , args [0 ], args [1 ])
58+
59+ require .NoError (t , ewErr , "endswith(%q,%q) must not error" , args [0 ], args [1 ])
60+ require .Equal (t , strings .HasSuffix (args [0 ], args [1 ]), ewOut ,
61+ "endswith(%q,%q) must agree with strings.HasSuffix" , args [0 ], args [1 ])
62+
63+ require .NoError (t , scErr , "strcontains(%q,%q) must not error" , args [0 ], args [1 ])
64+ require .Equal (t , strings .Contains (args [0 ], args [1 ]), scOut ,
65+ "strcontains(%q,%q) must agree with strings.Contains" , args [0 ], args [1 ])
3866 })
3967}
4068
41- // FuzzHCLRunCommandOptions: run_cmd must never panic on any mix of recognized option flags.
42- // Fuzz input is filtered to known --terragrunt-* flags so the fuzzer cannot launch arbitrary host commands.
43- func FuzzHCLRunCommandOptions (f * testing.F ) {
44- if runtime .GOOS == "windows" {
45- f .Skip ("run_cmd happy-path requires bash; skip on Windows" )
46- }
47-
69+ // FuzzRunCmdOptionsParsing: exercises run_cmd's option-stripping logic without ever reaching shell-out.
70+ // Args are filtered to known --terragrunt-* flags and NO command is appended, so RunCommand always returns
71+ // EmptyStringNotAllowedError or ConflictingRunCmdCacheOptionsError before shell.RunCommandWithOutput is called.
72+ func FuzzRunCmdOptionsParsing (f * testing.F ) {
4873 seeds := []string {
74+ "" ,
4975 "--terragrunt-quiet" ,
5076 "--terragrunt-no-cache" ,
5177 "--terragrunt-global-cache" ,
5278 "--terragrunt-quiet\x00 --terragrunt-no-cache" ,
5379 "--terragrunt-quiet\x00 --terragrunt-quiet" ,
5480 "--terragrunt-no-cache\x00 --terragrunt-global-cache" ,
81+ "--terragrunt-global-cache\x00 --terragrunt-no-cache" ,
82+ "--terragrunt-quiet\x00 --terragrunt-no-cache\x00 --terragrunt-global-cache" ,
5583 "--unknown-flag" ,
56- "" ,
5784 }
5885 for _ , s := range seeds {
5986 f .Add (s )
@@ -62,19 +89,42 @@ func FuzzHCLRunCommandOptions(f *testing.F) {
6289 f .Fuzz (func (t * testing.T , raw string ) {
6390 parts := strings .Split (raw , "\x00 " )
6491
65- args := make ([]string , 0 , len (parts )+ 2 )
92+ var hasNoCache , hasGlobalCache bool
93+
94+ args := make ([]string , 0 , len (parts ))
6695 for _ , part := range parts {
6796 switch part {
68- case "--terragrunt-quiet" , "--terragrunt-no-cache" , "--terragrunt-global-cache" :
97+ case "--terragrunt-quiet" :
98+ args = append (args , part )
99+ case "--terragrunt-no-cache" :
100+ args = append (args , part )
101+ hasNoCache = true
102+ case "--terragrunt-global-cache" :
69103 args = append (args , part )
104+ hasGlobalCache = true
70105 }
71106 }
72107
73- args = append (args , "/bin/echo" , "hi" )
108+ baseCtx , pctx := newTestParsingContext (t , "" )
109+
110+ ctx , cancel := context .WithTimeout (baseCtx , 2 * time .Second )
111+ defer cancel ()
74112
75113 l := logger .CreateLogger ()
76- ctx , pctx := newTestParsingContext (t , "" )
77114
78- _ , _ = config .RunCommand (ctx , pctx , l , args )
115+ out , err := config .RunCommand (ctx , pctx , l , args )
116+
117+ require .Empty (t , out , "options-only call must not produce output" )
118+ require .Error (t , err , "options-only call must error" )
119+
120+ if hasNoCache && hasGlobalCache {
121+ require .True (t , assertErrorType (t , config.ConflictingRunCmdCacheOptionsError {}, err ),
122+ "expected ConflictingRunCmdCacheOptionsError, got %T: %v" , err , err )
123+
124+ return
125+ }
126+
127+ require .True (t , assertErrorType (t , config .EmptyStringNotAllowedError ("" ), err ),
128+ "expected EmptyStringNotAllowedError, got %T: %v" , err , err )
79129 })
80130}
0 commit comments