// Copyright (c) The OpenTofu Authors // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2023 HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package command import ( "bytes" "fmt" "os" "path/filepath" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/mitchellh/cli" ) func TestFmt_TestFiles(t *testing.T) { const inSuffix = "_in.tftest.hcl" const outSuffix = "_out.tftest.hcl" const gotSuffix = "_got.tftest.hcl" entries, err := os.ReadDir("testdata/tftest-fmt") if err != nil { t.Fatal(err) } tmpDir, err := filepath.EvalSymlinks(t.TempDir()) if err != nil { t.Fatal(err) } for _, info := range entries { if info.IsDir() { continue } filename := info.Name() if !strings.HasSuffix(filename, inSuffix) { continue } testName := filename[:len(filename)-len(inSuffix)] t.Run(testName, func(t *testing.T) { inFile := filepath.Join("testdata", "tftest-fmt", testName+inSuffix) wantFile := filepath.Join("testdata", "tftest-fmt", testName+outSuffix) gotFile := filepath.Join(tmpDir, testName+gotSuffix) input, err := os.ReadFile(inFile) if err != nil { t.Fatal(err) } want, err := os.ReadFile(wantFile) if err != nil { t.Fatal(err) } err = os.WriteFile(gotFile, input, 0700) if err != nil { t.Fatal(err) } ui := cli.NewMockUi() c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{gotFile} if code := c.Run(args); code != 0 { t.Fatalf("fmt command was unsuccessful:\n%s", ui.ErrorWriter.String()) } got, err := os.ReadFile(gotFile) if err != nil { t.Fatal(err) } if diff := cmp.Diff(string(want), string(got)); diff != "" { t.Errorf("wrong result\n%s", diff) } }) } } func TestFmt(t *testing.T) { const inSuffix = "_in.tf" const outSuffix = "_out.tf" const gotSuffix = "_got.tf" entries, err := os.ReadDir("testdata/fmt") if err != nil { t.Fatal(err) } tmpDir, err := filepath.EvalSymlinks(t.TempDir()) if err != nil { t.Fatal(err) } for _, info := range entries { if info.IsDir() { continue } filename := info.Name() if !strings.HasSuffix(filename, inSuffix) { continue } testName := filename[:len(filename)-len(inSuffix)] t.Run(testName, func(t *testing.T) { inFile := filepath.Join("testdata", "fmt", testName+inSuffix) wantFile := filepath.Join("testdata", "fmt", testName+outSuffix) gotFile := filepath.Join(tmpDir, testName+gotSuffix) input, err := os.ReadFile(inFile) if err != nil { t.Fatal(err) } want, err := os.ReadFile(wantFile) if err != nil { t.Fatal(err) } err = os.WriteFile(gotFile, input, 0700) if err != nil { t.Fatal(err) } ui := cli.NewMockUi() c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{gotFile} if code := c.Run(args); code != 0 { t.Fatalf("fmt command was unsuccessful:\n%s", ui.ErrorWriter.String()) } got, err := os.ReadFile(gotFile) if err != nil { t.Fatal(err) } if diff := cmp.Diff(string(want), string(got)); diff != "" { t.Errorf("wrong result\n%s", diff) } }) } } func TestFmt_nonexist(t *testing.T) { tempDir := fmtFixtureWriteDir(t) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } missingDir := filepath.Join(tempDir, "doesnotexist") args := []string{missingDir} if code := c.Run(args); code != 2 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } expected := "No file or directory at" if actual := ui.ErrorWriter.String(); !strings.Contains(actual, expected) { t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) } } func TestFmt_syntaxError(t *testing.T) { tempDir := testTempDir(t) invalidSrc := ` a = 1 + ` err := os.WriteFile(filepath.Join(tempDir, "invalid.tf"), []byte(invalidSrc), 0644) if err != nil { t.Fatal(err) } ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{tempDir} if code := c.Run(args); code != 2 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } expected := "Invalid expression" if actual := ui.ErrorWriter.String(); !strings.Contains(actual, expected) { t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) } } func TestFmt_snippetInError(t *testing.T) { tempDir := testTempDir(t) backendSrc := `terraform {backend "s3" {}}` err := os.WriteFile(filepath.Join(tempDir, "backend.tf"), []byte(backendSrc), 0644) if err != nil { t.Fatal(err) } ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{tempDir} if code := c.Run(args); code != 2 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } substrings := []string{ "Argument definition required", "line 1, in terraform", `1: terraform {backend "s3" {}}`, } for _, substring := range substrings { if actual := ui.ErrorWriter.String(); !strings.Contains(actual, substring) { t.Errorf("expected:\n%s\n\nto include: %q", actual, substring) } } } func TestFmt_manyArgs(t *testing.T) { tempDir := fmtFixtureWriteDir(t) // Add a second file secondSrc := `locals { x = 1 }` err := os.WriteFile(filepath.Join(tempDir, "second.tf"), []byte(secondSrc), 0644) if err != nil { t.Fatal(err) } ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{ filepath.Join(tempDir, "main.tf"), filepath.Join(tempDir, "second.tf"), } if code := c.Run(args); code != 0 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) if err != nil { t.Fatal(err) } want := filepath.Join(tempDir, fmtFixture.filename) if got != want { t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) } } func TestFmt_workingDirectory(t *testing.T) { tempDir := fmtFixtureWriteDir(t) cwd, err := os.Getwd() if err != nil { t.Fatalf("err: %s", err) } err = os.Chdir(tempDir) if err != nil { t.Fatalf("err: %s", err) } defer os.Chdir(cwd) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } expected := fmt.Sprintf("%s\n", fmtFixture.filename) if actual := ui.OutputWriter.String(); actual != expected { t.Fatalf("got: %q\nexpected: %q", actual, expected) } } func TestFmt_directoryArg(t *testing.T) { tempDir := fmtFixtureWriteDir(t) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{tempDir} if code := c.Run(args); code != 0 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) if err != nil { t.Fatal(err) } want := filepath.Join(tempDir, fmtFixture.filename) if got != want { t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) } } func TestFmt_fileArg(t *testing.T) { tempDir := fmtFixtureWriteDir(t) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{filepath.Join(tempDir, fmtFixture.filename)} if code := c.Run(args); code != 0 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } got, err := filepath.Abs(strings.TrimSpace(ui.OutputWriter.String())) if err != nil { t.Fatal(err) } want := filepath.Join(tempDir, fmtFixture.filename) if got != want { t.Fatalf("wrong output\ngot: %s\nwant: %s", got, want) } } func TestFmt_stdinArg(t *testing.T) { input := new(bytes.Buffer) input.Write(fmtFixture.input) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, input: input, } args := []string{"-"} if code := c.Run(args); code != 0 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } expected := fmtFixture.golden if actual := ui.OutputWriter.Bytes(); !bytes.Equal(actual, expected) { t.Fatalf("got: %q\nexpected: %q", actual, expected) } } func TestFmt_nonDefaultOptions(t *testing.T) { tempDir := fmtFixtureWriteDir(t) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{ "-list=false", "-write=false", "-diff", tempDir, } if code := c.Run(args); code != 0 { t.Fatalf("wrong exit code. errors: \n%s", ui.ErrorWriter.String()) } expected := fmt.Sprintf("-%s+%s", fmtFixture.input, fmtFixture.golden) if actual := ui.OutputWriter.String(); !strings.Contains(actual, expected) { t.Fatalf("expected:\n%s\n\nto include: %q", actual, expected) } } func TestFmt_check(t *testing.T) { tempDir := fmtFixtureWriteDir(t) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, } args := []string{ "-check", tempDir, } if code := c.Run(args); code != 3 { t.Fatalf("wrong exit code. expected 3") } // Given that we give relative paths back to the user, normalize this temp // dir so that we're comparing against a relative-ized (normalized) path tempDir = c.normalizePath(tempDir) if actual := ui.OutputWriter.String(); !strings.Contains(actual, tempDir) { t.Fatalf("expected:\n%s\n\nto include: %q", actual, tempDir) } } func TestFmt_checkStdin(t *testing.T) { input := new(bytes.Buffer) input.Write(fmtFixture.input) ui := new(cli.MockUi) c := &FmtCommand{ Meta: Meta{ testingOverrides: metaOverridesForProvider(testProvider()), Ui: ui, }, input: input, } args := []string{ "-check", "-", } if code := c.Run(args); code != 3 { t.Fatalf("wrong exit code. expected 3, got %d", code) } if ui.OutputWriter != nil { t.Fatalf("expected no output, got: %q", ui.OutputWriter.String()) } } var fmtFixture = struct { filename string input, golden []byte }{ "main.tf", []byte(` foo = "bar" `), []byte(`foo = "bar" `), } func fmtFixtureWriteDir(t *testing.T) string { dir := testTempDir(t) err := os.WriteFile(filepath.Join(dir, fmtFixture.filename), fmtFixture.input, 0644) if err != nil { t.Fatal(err) } return dir }