github.com/opentofu/opentofu@v1.7.1/internal/command/fmt_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package command 7 8 import ( 9 "bytes" 10 "fmt" 11 "os" 12 "path/filepath" 13 "strings" 14 "testing" 15 16 "github.com/google/go-cmp/cmp" 17 "github.com/mitchellh/cli" 18 ) 19 20 func TestFmt_TestFiles(t *testing.T) { 21 const inSuffix = "_in.tftest.hcl" 22 const outSuffix = "_out.tftest.hcl" 23 const gotSuffix = "_got.tftest.hcl" 24 entries, err := os.ReadDir("testdata/tftest-fmt") 25 if err != nil { 26 t.Fatal(err) 27 } 28 29 tmpDir, err := filepath.EvalSymlinks(t.TempDir()) 30 if err != nil { 31 t.Fatal(err) 32 } 33 34 for _, info := range entries { 35 if info.IsDir() { 36 continue 37 } 38 filename := info.Name() 39 if !strings.HasSuffix(filename, inSuffix) { 40 continue 41 } 42 testName := filename[:len(filename)-len(inSuffix)] 43 t.Run(testName, func(t *testing.T) { 44 inFile := filepath.Join("testdata", "tftest-fmt", testName+inSuffix) 45 wantFile := filepath.Join("testdata", "tftest-fmt", testName+outSuffix) 46 gotFile := filepath.Join(tmpDir, testName+gotSuffix) 47 input, err := os.ReadFile(inFile) 48 if err != nil { 49 t.Fatal(err) 50 } 51 want, err := os.ReadFile(wantFile) 52 if err != nil { 53 t.Fatal(err) 54 } 55 err = os.WriteFile(gotFile, input, 0700) 56 if err != nil { 57 t.Fatal(err) 58 } 59 60 ui := cli.NewMockUi() 61 c := &FmtCommand{ 62 Meta: Meta{ 63 testingOverrides: metaOverridesForProvider(testProvider()), 64 Ui: ui, 65 }, 66 } 67 args := []string{gotFile} 68 if code := c.Run(args); code != 0 { 69 t.Fatalf("fmt command was unsuccessful:\n%s", ui.ErrorWriter.String()) 70 } 71 72 got, err := os.ReadFile(gotFile) 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 if diff := cmp.Diff(string(want), string(got)); diff != "" { 78 t.Errorf("wrong result\n%s", diff) 79 } 80 }) 81 } 82 } 83 84 func TestFmt(t *testing.T) { 85 const inSuffix = "_in.tf" 86 const outSuffix = "_out.tf" 87 const gotSuffix = "_got.tf" 88 entries, err := os.ReadDir("testdata/fmt") 89 if err != nil { 90 t.Fatal(err) 91 } 92 93 tmpDir, err := filepath.EvalSymlinks(t.TempDir()) 94 if err != nil { 95 t.Fatal(err) 96 } 97 98 for _, info := range entries { 99 if info.IsDir() { 100 continue 101 } 102 filename := info.Name() 103 if !strings.HasSuffix(filename, inSuffix) { 104 continue 105 } 106 testName := filename[:len(filename)-len(inSuffix)] 107 t.Run(testName, func(t *testing.T) { 108 inFile := filepath.Join("testdata", "fmt", testName+inSuffix) 109 wantFile := filepath.Join("testdata", "fmt", testName+outSuffix) 110 gotFile := filepath.Join(tmpDir, testName+gotSuffix) 111 input, err := os.ReadFile(inFile) 112 if err != nil { 113 t.Fatal(err) 114 } 115 want, err := os.ReadFile(wantFile) 116 if err != nil { 117 t.Fatal(err) 118 } 119 err = os.WriteFile(gotFile, input, 0700) 120 if err != nil { 121 t.Fatal(err) 122 } 123 124 ui := cli.NewMockUi() 125 c := &FmtCommand{ 126 Meta: Meta{ 127 testingOverrides: metaOverridesForProvider(testProvider()), 128 Ui: ui, 129 }, 130 } 131 args := []string{gotFile} 132 if code := c.Run(args); code != 0 { 133 t.Fatalf("fmt command was unsuccessful:\n%s", ui.ErrorWriter.String()) 134 } 135 136 got, err := os.ReadFile(gotFile) 137 if err != nil { 138 t.Fatal(err) 139 } 140 141 if diff := cmp.Diff(string(want), string(got)); diff != "" { 142 t.Errorf("wrong result\n%s", diff) 143 } 144 }) 145 } 146 } 147 148 func TestFmt_nonexist(t *testing.T) { 149 tempDir := fmtFixtureWriteDir(t) 150 151 ui := new(cli.MockUi) 152 c := &FmtCommand{ 153 Meta: Meta{ 154 testingOverrides: metaOverridesForProvider(testProvider()), 155 Ui: ui, 156 }, 157 } 158 159 missingDir := filepath.Join(tempDir, "doesnotexist") 160 args := []string{missingDir} 161 if code := c.Run(args); code != 2 { 162 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 163 } 164 165 expected := "No file or directory at" 166 if actual := ui.ErrorWriter.String(); !strings.Contains(actual, expected) { 167 t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) 168 } 169 } 170 171 func TestFmt_syntaxError(t *testing.T) { 172 tempDir := testTempDir(t) 173 174 invalidSrc := ` 175 a = 1 + 176 ` 177 178 err := os.WriteFile(filepath.Join(tempDir, "invalid.tf"), []byte(invalidSrc), 0644) 179 if err != nil { 180 t.Fatal(err) 181 } 182 183 ui := new(cli.MockUi) 184 c := &FmtCommand{ 185 Meta: Meta{ 186 testingOverrides: metaOverridesForProvider(testProvider()), 187 Ui: ui, 188 }, 189 } 190 191 args := []string{tempDir} 192 if code := c.Run(args); code != 2 { 193 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 194 } 195 196 expected := "Invalid expression" 197 if actual := ui.ErrorWriter.String(); !strings.Contains(actual, expected) { 198 t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) 199 } 200 } 201 202 func TestFmt_snippetInError(t *testing.T) { 203 tempDir := testTempDir(t) 204 205 backendSrc := `terraform {backend "s3" {}}` 206 207 err := os.WriteFile(filepath.Join(tempDir, "backend.tf"), []byte(backendSrc), 0644) 208 if err != nil { 209 t.Fatal(err) 210 } 211 212 ui := new(cli.MockUi) 213 c := &FmtCommand{ 214 Meta: Meta{ 215 testingOverrides: metaOverridesForProvider(testProvider()), 216 Ui: ui, 217 }, 218 } 219 220 args := []string{tempDir} 221 if code := c.Run(args); code != 2 { 222 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 223 } 224 225 substrings := []string{ 226 "Argument definition required", 227 "line 1, in terraform", 228 `1: terraform {backend "s3" {}}`, 229 } 230 for _, substring := range substrings { 231 if actual := ui.ErrorWriter.String(); !strings.Contains(actual, substring) { 232 t.Errorf("expected:\n%s\n\nto include: %q", actual, substring) 233 } 234 } 235 } 236 237 func TestFmt_manyArgs(t *testing.T) { 238 tempDir := fmtFixtureWriteDir(t) 239 // Add a second file 240 secondSrc := `locals { x = 1 }` 241 242 err := os.WriteFile(filepath.Join(tempDir, "second.tf"), []byte(secondSrc), 0644) 243 if err != nil { 244 t.Fatal(err) 245 } 246 247 ui := new(cli.MockUi) 248 c := &FmtCommand{ 249 Meta: Meta{ 250 testingOverrides: metaOverridesForProvider(testProvider()), 251 Ui: ui, 252 }, 253 } 254 255 args := []string{ 256 filepath.Join(tempDir, "main.tf"), 257 filepath.Join(tempDir, "second.tf"), 258 } 259 if code := c.Run(args); code != 0 { 260 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 261 } 262 263 got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) 264 if err != nil { 265 t.Fatal(err) 266 } 267 want := filepath.Join(tempDir, fmtFixture.filename) 268 269 if got != want { 270 t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) 271 } 272 } 273 274 func TestFmt_workingDirectory(t *testing.T) { 275 tempDir := fmtFixtureWriteDir(t) 276 277 cwd, err := os.Getwd() 278 if err != nil { 279 t.Fatalf("err: %s", err) 280 } 281 err = os.Chdir(tempDir) 282 if err != nil { 283 t.Fatalf("err: %s", err) 284 } 285 defer os.Chdir(cwd) 286 287 ui := new(cli.MockUi) 288 c := &FmtCommand{ 289 Meta: Meta{ 290 testingOverrides: metaOverridesForProvider(testProvider()), 291 Ui: ui, 292 }, 293 } 294 295 args := []string{} 296 if code := c.Run(args); code != 0 { 297 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 298 } 299 300 expected := fmt.Sprintf("%s\n", fmtFixture.filename) 301 if actual := ui.OutputWriter.String(); actual != expected { 302 t.Fatalf("got: %q\nexpected: %q", actual, expected) 303 } 304 } 305 306 func TestFmt_directoryArg(t *testing.T) { 307 tempDir := fmtFixtureWriteDir(t) 308 309 ui := new(cli.MockUi) 310 c := &FmtCommand{ 311 Meta: Meta{ 312 testingOverrides: metaOverridesForProvider(testProvider()), 313 Ui: ui, 314 }, 315 } 316 317 args := []string{tempDir} 318 if code := c.Run(args); code != 0 { 319 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 320 } 321 322 got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) 323 if err != nil { 324 t.Fatal(err) 325 } 326 want := filepath.Join(tempDir, fmtFixture.filename) 327 328 if got != want { 329 t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) 330 } 331 } 332 333 func TestFmt_fileArg(t *testing.T) { 334 tempDir := fmtFixtureWriteDir(t) 335 336 ui := new(cli.MockUi) 337 c := &FmtCommand{ 338 Meta: Meta{ 339 testingOverrides: metaOverridesForProvider(testProvider()), 340 Ui: ui, 341 }, 342 } 343 344 args := []string{filepath.Join(tempDir, fmtFixture.filename)} 345 if code := c.Run(args); code != 0 { 346 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 347 } 348 349 got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) 350 if err != nil { 351 t.Fatal(err) 352 } 353 want := filepath.Join(tempDir, fmtFixture.filename) 354 355 if got != want { 356 t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) 357 } 358 } 359 360 func TestFmt_stdinArg(t *testing.T) { 361 input := new(bytes.Buffer) 362 input.Write(fmtFixture.input) 363 364 ui := new(cli.MockUi) 365 c := &FmtCommand{ 366 Meta: Meta{ 367 testingOverrides: metaOverridesForProvider(testProvider()), 368 Ui: ui, 369 }, 370 input: input, 371 } 372 373 args := []string{"-"} 374 if code := c.Run(args); code != 0 { 375 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 376 } 377 378 expected := fmtFixture.golden 379 if actual := ui.OutputWriter.Bytes(); !bytes.Equal(actual, expected) { 380 t.Fatalf("got: %q\nexpected: %q", actual, expected) 381 } 382 } 383 384 func TestFmt_nonDefaultOptions(t *testing.T) { 385 tempDir := fmtFixtureWriteDir(t) 386 387 ui := new(cli.MockUi) 388 c := &FmtCommand{ 389 Meta: Meta{ 390 testingOverrides: metaOverridesForProvider(testProvider()), 391 Ui: ui, 392 }, 393 } 394 395 args := []string{ 396 "-list=false", 397 "-write=false", 398 "-diff", 399 tempDir, 400 } 401 if code := c.Run(args); code != 0 { 402 t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) 403 } 404 405 expected := fmt.Sprintf("-%s+%s", fmtFixture.input, fmtFixture.golden) 406 if actual := ui.OutputWriter.String(); !strings.Contains(actual, expected) { 407 t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) 408 } 409 } 410 411 func TestFmt_check(t *testing.T) { 412 tempDir := fmtFixtureWriteDir(t) 413 414 ui := new(cli.MockUi) 415 c := &FmtCommand{ 416 Meta: Meta{ 417 testingOverrides: metaOverridesForProvider(testProvider()), 418 Ui: ui, 419 }, 420 } 421 422 args := []string{ 423 "-check", 424 tempDir, 425 } 426 if code := c.Run(args); code != 3 { 427 t.Fatalf("wrong exit code. expected 3") 428 } 429 430 // Given that we give relative paths back to the user, normalize this temp 431 // dir so that we're comparing against a relative-ized (normalized) path 432 tempDir = c.normalizePath(tempDir) 433 434 if actual := ui.OutputWriter.String(); !strings.Contains(actual, tempDir) { 435 t.Fatalf("expected:\n%s\n\nto include: %q", actual, tempDir) 436 } 437 } 438 439 func TestFmt_checkStdin(t *testing.T) { 440 input := new(bytes.Buffer) 441 input.Write(fmtFixture.input) 442 443 ui := new(cli.MockUi) 444 c := &FmtCommand{ 445 Meta: Meta{ 446 testingOverrides: metaOverridesForProvider(testProvider()), 447 Ui: ui, 448 }, 449 input: input, 450 } 451 452 args := []string{ 453 "-check", 454 "-", 455 } 456 if code := c.Run(args); code != 3 { 457 t.Fatalf("wrong exit code. expected 3, got %d", code) 458 } 459 460 if ui.OutputWriter != nil { 461 t.Fatalf("expected no output, got: %q", ui.OutputWriter.String()) 462 } 463 } 464 465 var fmtFixture = struct { 466 filename string 467 input, golden []byte 468 }{ 469 "main.tf", 470 []byte(` foo = "bar" 471 `), 472 []byte(`foo = "bar" 473 `), 474 } 475 476 func fmtFixtureWriteDir(t *testing.T) string { 477 dir := testTempDir(t) 478 479 err := os.WriteFile(filepath.Join(dir, fmtFixture.filename), fmtFixture.input, 0644) 480 if err != nil { 481 t.Fatal(err) 482 } 483 484 return dir 485 }