github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/meta_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package command 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "reflect" 12 "strings" 13 "testing" 14 15 "github.com/google/go-cmp/cmp" 16 17 "github.com/mitchellh/cli" 18 "github.com/terramate-io/tf/backend" 19 "github.com/terramate-io/tf/backend/local" 20 "github.com/terramate-io/tf/terraform" 21 ) 22 23 func TestMetaColorize(t *testing.T) { 24 var m *Meta 25 var args, args2 []string 26 27 // Test basic, color 28 m = new(Meta) 29 m.Color = true 30 args = []string{"foo", "bar"} 31 args2 = []string{"foo", "bar"} 32 args = m.process(args) 33 if !reflect.DeepEqual(args, args2) { 34 t.Fatalf("bad: %#v", args) 35 } 36 if m.Colorize().Disable { 37 t.Fatal("should not be disabled") 38 } 39 40 // Test basic, no change 41 m = new(Meta) 42 args = []string{"foo", "bar"} 43 args2 = []string{"foo", "bar"} 44 args = m.process(args) 45 if !reflect.DeepEqual(args, args2) { 46 t.Fatalf("bad: %#v", args) 47 } 48 if !m.Colorize().Disable { 49 t.Fatal("should be disabled") 50 } 51 52 // Test disable #1 53 m = new(Meta) 54 m.Color = true 55 args = []string{"foo", "-no-color", "bar"} 56 args2 = []string{"foo", "bar"} 57 args = m.process(args) 58 if !reflect.DeepEqual(args, args2) { 59 t.Fatalf("bad: %#v", args) 60 } 61 if !m.Colorize().Disable { 62 t.Fatal("should be disabled") 63 } 64 65 // Test disable #2 66 // Verify multiple -no-color options are removed from args slice. 67 // E.g. an additional -no-color arg could be added by TF_CLI_ARGS. 68 m = new(Meta) 69 m.Color = true 70 args = []string{"foo", "-no-color", "bar", "-no-color"} 71 args2 = []string{"foo", "bar"} 72 args = m.process(args) 73 if !reflect.DeepEqual(args, args2) { 74 t.Fatalf("bad: %#v", args) 75 } 76 if !m.Colorize().Disable { 77 t.Fatal("should be disabled") 78 } 79 } 80 81 func TestMetaInputMode(t *testing.T) { 82 test = false 83 defer func() { test = true }() 84 85 m := new(Meta) 86 args := []string{} 87 88 fs := m.extendedFlagSet("foo") 89 if err := fs.Parse(args); err != nil { 90 t.Fatalf("err: %s", err) 91 } 92 93 if m.InputMode() != terraform.InputModeStd { 94 t.Fatalf("bad: %#v", m.InputMode()) 95 } 96 } 97 98 func TestMetaInputMode_envVar(t *testing.T) { 99 test = false 100 defer func() { test = true }() 101 old := os.Getenv(InputModeEnvVar) 102 defer os.Setenv(InputModeEnvVar, old) 103 104 m := new(Meta) 105 args := []string{} 106 107 fs := m.extendedFlagSet("foo") 108 if err := fs.Parse(args); err != nil { 109 t.Fatalf("err: %s", err) 110 } 111 112 off := terraform.InputMode(0) 113 on := terraform.InputModeStd 114 cases := []struct { 115 EnvVar string 116 Expected terraform.InputMode 117 }{ 118 {"false", off}, 119 {"0", off}, 120 {"true", on}, 121 {"1", on}, 122 } 123 124 for _, tc := range cases { 125 os.Setenv(InputModeEnvVar, tc.EnvVar) 126 if m.InputMode() != tc.Expected { 127 t.Fatalf("expected InputMode: %#v, got: %#v", tc.Expected, m.InputMode()) 128 } 129 } 130 } 131 132 func TestMetaInputMode_disable(t *testing.T) { 133 test = false 134 defer func() { test = true }() 135 136 m := new(Meta) 137 args := []string{"-input=false"} 138 139 fs := m.extendedFlagSet("foo") 140 if err := fs.Parse(args); err != nil { 141 t.Fatalf("err: %s", err) 142 } 143 144 if m.InputMode() > 0 { 145 t.Fatalf("bad: %#v", m.InputMode()) 146 } 147 } 148 149 func TestMeta_initStatePaths(t *testing.T) { 150 m := new(Meta) 151 m.initStatePaths() 152 153 if m.statePath != DefaultStateFilename { 154 t.Fatalf("bad: %#v", m) 155 } 156 if m.stateOutPath != DefaultStateFilename { 157 t.Fatalf("bad: %#v", m) 158 } 159 if m.backupPath != DefaultStateFilename+DefaultBackupExtension { 160 t.Fatalf("bad: %#v", m) 161 } 162 163 m = new(Meta) 164 m.statePath = "foo" 165 m.initStatePaths() 166 167 if m.stateOutPath != "foo" { 168 t.Fatalf("bad: %#v", m) 169 } 170 if m.backupPath != "foo"+DefaultBackupExtension { 171 t.Fatalf("bad: %#v", m) 172 } 173 174 m = new(Meta) 175 m.stateOutPath = "foo" 176 m.initStatePaths() 177 178 if m.statePath != DefaultStateFilename { 179 t.Fatalf("bad: %#v", m) 180 } 181 if m.backupPath != "foo"+DefaultBackupExtension { 182 t.Fatalf("bad: %#v", m) 183 } 184 } 185 186 func TestMeta_Env(t *testing.T) { 187 td := t.TempDir() 188 os.MkdirAll(td, 0755) 189 defer testChdir(t, td)() 190 191 m := new(Meta) 192 193 env, err := m.Workspace() 194 if err != nil { 195 t.Fatal(err) 196 } 197 198 if env != backend.DefaultStateName { 199 t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env) 200 } 201 202 testEnv := "test_env" 203 if err := m.SetWorkspace(testEnv); err != nil { 204 t.Fatal("error setting env:", err) 205 } 206 207 env, _ = m.Workspace() 208 if env != testEnv { 209 t.Fatalf("expected env %q, got env %q", testEnv, env) 210 } 211 212 if err := m.SetWorkspace(backend.DefaultStateName); err != nil { 213 t.Fatal("error setting env:", err) 214 } 215 216 env, _ = m.Workspace() 217 if env != backend.DefaultStateName { 218 t.Fatalf("expected env %q, got env %q", backend.DefaultStateName, env) 219 } 220 } 221 222 func TestMeta_Workspace_override(t *testing.T) { 223 defer func(value string) { 224 os.Setenv(WorkspaceNameEnvVar, value) 225 }(os.Getenv(WorkspaceNameEnvVar)) 226 227 m := new(Meta) 228 229 testCases := map[string]struct { 230 workspace string 231 err error 232 }{ 233 "": { 234 "default", 235 nil, 236 }, 237 "development": { 238 "development", 239 nil, 240 }, 241 "invalid name": { 242 "", 243 errInvalidWorkspaceNameEnvVar, 244 }, 245 } 246 247 for name, tc := range testCases { 248 t.Run(name, func(t *testing.T) { 249 os.Setenv(WorkspaceNameEnvVar, name) 250 workspace, err := m.Workspace() 251 if workspace != tc.workspace { 252 t.Errorf("Unexpected workspace\n got: %s\nwant: %s\n", workspace, tc.workspace) 253 } 254 if err != tc.err { 255 t.Errorf("Unexpected error\n got: %s\nwant: %s\n", err, tc.err) 256 } 257 }) 258 } 259 } 260 261 func TestMeta_Workspace_invalidSelected(t *testing.T) { 262 td := t.TempDir() 263 os.MkdirAll(td, 0755) 264 defer testChdir(t, td)() 265 266 // this is an invalid workspace name 267 workspace := "test workspace" 268 269 // create the workspace directories 270 if err := os.MkdirAll(filepath.Join(local.DefaultWorkspaceDir, workspace), 0755); err != nil { 271 t.Fatal(err) 272 } 273 274 // create the workspace file to select it 275 if err := os.MkdirAll(DefaultDataDir, 0755); err != nil { 276 t.Fatal(err) 277 } 278 if err := ioutil.WriteFile(filepath.Join(DefaultDataDir, local.DefaultWorkspaceFile), []byte(workspace), 0644); err != nil { 279 t.Fatal(err) 280 } 281 282 m := new(Meta) 283 284 ws, err := m.Workspace() 285 if ws != workspace { 286 t.Errorf("Unexpected workspace\n got: %s\nwant: %s\n", ws, workspace) 287 } 288 if err != nil { 289 t.Errorf("Unexpected error: %s", err) 290 } 291 } 292 293 func TestMeta_process(t *testing.T) { 294 test = false 295 defer func() { test = true }() 296 297 // Create a temporary directory for our cwd 298 d := t.TempDir() 299 os.MkdirAll(d, 0755) 300 defer testChdir(t, d)() 301 302 // At one point it was the responsibility of this process function to 303 // insert fake additional -var-file options into the command line 304 // if the automatic tfvars files were present. This is no longer the 305 // responsibility of process (it happens in collectVariableValues instead) 306 // but we're still testing with these files in place to verify that 307 // they _aren't_ being interpreted by process, since that could otherwise 308 // cause them to be added more than once and mess up the precedence order. 309 defaultVarsfile := "terraform.tfvars" 310 err := ioutil.WriteFile( 311 filepath.Join(d, defaultVarsfile), 312 []byte(""), 313 0644) 314 if err != nil { 315 t.Fatalf("err: %s", err) 316 } 317 fileFirstAlphabetical := "a-file.auto.tfvars" 318 err = ioutil.WriteFile( 319 filepath.Join(d, fileFirstAlphabetical), 320 []byte(""), 321 0644) 322 if err != nil { 323 t.Fatalf("err: %s", err) 324 } 325 fileLastAlphabetical := "z-file.auto.tfvars" 326 err = ioutil.WriteFile( 327 filepath.Join(d, fileLastAlphabetical), 328 []byte(""), 329 0644) 330 if err != nil { 331 t.Fatalf("err: %s", err) 332 } 333 // Regular tfvars files will not be autoloaded 334 fileIgnored := "ignored.tfvars" 335 err = ioutil.WriteFile( 336 filepath.Join(d, fileIgnored), 337 []byte(""), 338 0644) 339 if err != nil { 340 t.Fatalf("err: %s", err) 341 } 342 343 tests := []struct { 344 GivenArgs []string 345 FilteredArgs []string 346 ExtraCheck func(*testing.T, *Meta) 347 }{ 348 { 349 []string{}, 350 []string{}, 351 func(t *testing.T, m *Meta) { 352 if got, want := m.color, true; got != want { 353 t.Errorf("wrong m.color value %#v; want %#v", got, want) 354 } 355 if got, want := m.Color, true; got != want { 356 t.Errorf("wrong m.Color value %#v; want %#v", got, want) 357 } 358 }, 359 }, 360 { 361 []string{"-no-color"}, 362 []string{}, 363 func(t *testing.T, m *Meta) { 364 if got, want := m.color, false; got != want { 365 t.Errorf("wrong m.color value %#v; want %#v", got, want) 366 } 367 if got, want := m.Color, false; got != want { 368 t.Errorf("wrong m.Color value %#v; want %#v", got, want) 369 } 370 }, 371 }, 372 } 373 374 for _, test := range tests { 375 t.Run(fmt.Sprintf("%s", test.GivenArgs), func(t *testing.T) { 376 m := new(Meta) 377 m.Color = true // this is the default also for normal use, overridden by -no-color 378 args := test.GivenArgs 379 args = m.process(args) 380 381 if !cmp.Equal(test.FilteredArgs, args) { 382 t.Errorf("wrong filtered arguments\n%s", cmp.Diff(test.FilteredArgs, args)) 383 } 384 385 if test.ExtraCheck != nil { 386 test.ExtraCheck(t, m) 387 } 388 }) 389 } 390 } 391 392 func TestCommand_checkRequiredVersion(t *testing.T) { 393 // Create a temporary working directory that is empty 394 td := t.TempDir() 395 testCopyDir(t, testFixturePath("command-check-required-version"), td) 396 defer testChdir(t, td)() 397 398 ui := cli.NewMockUi() 399 meta := Meta{ 400 Ui: ui, 401 } 402 403 diags := meta.checkRequiredVersion() 404 if diags == nil { 405 t.Fatalf("diagnostics should contain unmet version constraint, but is nil") 406 } 407 408 meta.showDiagnostics(diags) 409 410 // Required version diags are correct 411 errStr := ui.ErrorWriter.String() 412 if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) { 413 t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr) 414 } 415 if strings.Contains(errStr, `required_version = ">= 0.13.0"`) { 416 t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr) 417 } 418 }