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  }