github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/internal/tests/integration/basic_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"io/fs"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/hairyhenderson/gomplate/v4/internal/iohelpers"
     9  	"github.com/stretchr/testify/require"
    10  	"gotest.tools/v3/assert"
    11  	"gotest.tools/v3/assert/cmp"
    12  	tfs "gotest.tools/v3/fs"
    13  )
    14  
    15  func setupBasicTest(t *testing.T) *tfs.Dir {
    16  	t.Helper()
    17  
    18  	tmpDir := tfs.NewDir(t, "gomplate-inttests",
    19  		tfs.WithFile("one", "hi\n", tfs.WithMode(0o640)),
    20  		tfs.WithFile("two", "hello\n"),
    21  		tfs.WithFile("broken", "", tfs.WithMode(0o000)),
    22  		tfs.WithDir("subdir",
    23  			tfs.WithFile("f1", "first\n", tfs.WithMode(0o640)),
    24  			tfs.WithFile("f2", "second\n"),
    25  		),
    26  	)
    27  	t.Cleanup(tmpDir.Remove)
    28  	return tmpDir
    29  }
    30  
    31  func TestBasic_ReportsVersion(t *testing.T) {
    32  	o, e, err := cmd(t, "-v").run()
    33  	require.NoError(t, err)
    34  	assert.Equal(t, "", e)
    35  	assert.Assert(t, cmp.Contains(o, "gomplate version "))
    36  }
    37  
    38  func TestBasic_TakesStdinByDefault(t *testing.T) {
    39  	o, e, err := cmd(t).withStdin("hello world").run()
    40  	assertSuccess(t, o, e, err, "hello world")
    41  }
    42  
    43  func TestBasic_TakesStdinWithFileFlag(t *testing.T) {
    44  	o, e, err := cmd(t, "--file", "-").withStdin("hello world").run()
    45  	assertSuccess(t, o, e, err, "hello world")
    46  }
    47  
    48  func TestBasic_WritesToStdoutWithOutFlag(t *testing.T) {
    49  	o, e, err := cmd(t, "--out", "-").withStdin("hello world").run()
    50  	assertSuccess(t, o, e, err, "hello world")
    51  }
    52  
    53  func TestBasic_IgnoresStdinWithInFlag(t *testing.T) {
    54  	o, e, err := cmd(t, "--in", "hi").withStdin("hello world").run()
    55  	assertSuccess(t, o, e, err, "hi")
    56  }
    57  
    58  func TestBasic_ErrorsWithInputOutputImbalance(t *testing.T) {
    59  	tmpDir := setupBasicTest(t)
    60  
    61  	_, _, err := cmd(t,
    62  		"-f", tmpDir.Join("one"),
    63  		"-f", tmpDir.Join("two"),
    64  		"-o", tmpDir.Join("out"),
    65  	).run()
    66  	assert.ErrorContains(t, err, "must provide same number of 'outputFiles' (1) as 'in' or 'inputFiles' (2) options")
    67  }
    68  
    69  func TestBasic_RoutesInputsToProperOutputs(t *testing.T) {
    70  	tmpDir := setupBasicTest(t)
    71  	oneOut := tmpDir.Join("one.out")
    72  	twoOut := tmpDir.Join("two.out")
    73  
    74  	o, e, err := cmd(t,
    75  		"-f", tmpDir.Join("one"),
    76  		"-f", tmpDir.Join("two"),
    77  		"-o", oneOut,
    78  		"-o", twoOut,
    79  	).run()
    80  	assertSuccess(t, o, e, err, "")
    81  
    82  	testdata := []struct {
    83  		path    string
    84  		content string
    85  		mode    os.FileMode
    86  	}{
    87  		{oneOut, "hi\n", 0o640},
    88  		{twoOut, "hello\n", 0o644},
    89  	}
    90  	for _, v := range testdata {
    91  		info, err := os.Stat(v.path)
    92  		require.NoError(t, err)
    93  		m := iohelpers.NormalizeFileMode(v.mode)
    94  		assert.Equal(t, m, info.Mode(), v.path)
    95  		content, err := os.ReadFile(v.path)
    96  		require.NoError(t, err)
    97  		assert.Equal(t, v.content, string(content))
    98  	}
    99  }
   100  
   101  func TestBasic_FlagRules(t *testing.T) {
   102  	testdata := []struct {
   103  		errmsg string
   104  		args   []string
   105  	}{
   106  		{
   107  			"only one of these options is supported at a time: 'in', 'inputFiles'",
   108  			[]string{"-f", "-", "-i", "HELLO WORLD"},
   109  		},
   110  		{
   111  			"these options must be set together: 'outputDir', 'inputDir'",
   112  			[]string{"--output-dir", "."},
   113  		},
   114  		{
   115  			"only one of these options is supported at a time: 'in', 'inputDir'",
   116  			[]string{"--input-dir", ".", "--in", "param"},
   117  		},
   118  		{
   119  			"only one of these options is supported at a time: 'inputFiles', 'inputDir'",
   120  			[]string{"--input-dir", ".", "--file", "input.txt"},
   121  		},
   122  		{
   123  			"only one of these options is supported at a time: 'outputFiles', 'outputDir'",
   124  			[]string{"--output-dir", ".", "--out", "param"},
   125  		},
   126  		{
   127  			"only one of these options is supported at a time: 'outputFiles', 'outputMap'",
   128  			[]string{"--output-map", ".", "--out", "param"},
   129  		},
   130  	}
   131  
   132  	for _, d := range testdata {
   133  		_, _, err := cmd(t, d.args...).run()
   134  		assert.ErrorContains(t, err, d.errmsg)
   135  	}
   136  }
   137  
   138  func TestBasic_DelimsChangedThroughOpts(t *testing.T) {
   139  	o, e, err := cmd(t,
   140  		"--left-delim", "((",
   141  		"--right-delim", "))",
   142  		"-i", `foo((print "hi"))`,
   143  	).run()
   144  	assertSuccess(t, o, e, err, "foohi")
   145  }
   146  
   147  func TestBasic_DelimsChangedThroughEnvVars(t *testing.T) {
   148  	o, e, err := cmd(t, "-i", `foo<<print "hi">>`).
   149  		withEnv("GOMPLATE_LEFT_DELIM", "<<").
   150  		withEnv("GOMPLATE_RIGHT_DELIM", ">>").
   151  		run()
   152  	assertSuccess(t, o, e, err, "foohi")
   153  }
   154  
   155  func TestBasic_UnknownArgErrors(t *testing.T) {
   156  	_, _, err := cmd(t, "-in", "flibbit").run()
   157  	assert.ErrorContains(t, err, `unknown command "flibbit" for "gomplate"`)
   158  }
   159  
   160  func TestBasic_ExecCommand(t *testing.T) {
   161  	tmpDir := setupBasicTest(t)
   162  	out := tmpDir.Join("out")
   163  	o, e, err := cmd(t, "-i", `{{print "hello world"}}`,
   164  		"-o", out,
   165  		"--", "cat", out).run()
   166  	assertSuccess(t, o, e, err, "hello world")
   167  }
   168  
   169  func TestBasic_PostRunExecPipe(t *testing.T) {
   170  	o, e, err := cmd(t,
   171  		"-i", `{{print "hello world"}}`,
   172  		"--exec-pipe",
   173  		"--", "tr", "a-z", "A-Z").run()
   174  	assertSuccess(t, o, e, err, "HELLO WORLD")
   175  }
   176  
   177  func TestBasic_EmptyOutputSuppression(t *testing.T) {
   178  	tmpDir := setupBasicTest(t)
   179  	out := tmpDir.Join("out")
   180  	o, e, err := cmd(t, "-i", `{{print "\t  \n\n\r\n\t\t     \v\n"}}`,
   181  		"-o", out).run()
   182  	assertSuccess(t, o, e, err, "")
   183  
   184  	_, err = os.Stat(out)
   185  	require.ErrorIs(t, err, fs.ErrNotExist)
   186  }
   187  
   188  func TestBasic_RoutesInputsToProperOutputsWithChmod(t *testing.T) {
   189  	tmpDir := setupBasicTest(t)
   190  	oneOut := tmpDir.Join("one.out")
   191  	twoOut := tmpDir.Join("two.out")
   192  
   193  	o, e, err := cmd(t,
   194  		"-f", tmpDir.Join("one"),
   195  		"-f", tmpDir.Join("two"),
   196  		"-o", oneOut,
   197  		"-o", twoOut,
   198  		"--chmod", "0600").
   199  		withStdin("hello world").run()
   200  	assertSuccess(t, o, e, err, "")
   201  
   202  	testdata := []struct {
   203  		path    string
   204  		content string
   205  		mode    os.FileMode
   206  	}{
   207  		{oneOut, "hi\n", 0o600},
   208  		{twoOut, "hello\n", 0o600},
   209  	}
   210  	for _, v := range testdata {
   211  		info, err := os.Stat(v.path)
   212  		require.NoError(t, err)
   213  		assert.Equal(t, iohelpers.NormalizeFileMode(v.mode), info.Mode())
   214  		content, err := os.ReadFile(v.path)
   215  		require.NoError(t, err)
   216  		assert.Equal(t, v.content, string(content))
   217  	}
   218  }
   219  
   220  func TestBasic_OverridesOutputModeWithChmod(t *testing.T) {
   221  	tmpDir := setupBasicTest(t)
   222  	out := tmpDir.Join("two")
   223  
   224  	o, e, err := cmd(t,
   225  		"-f", tmpDir.Join("one"),
   226  		"-o", out,
   227  		"--chmod", "0600").
   228  		withStdin("hello world").run()
   229  	assertSuccess(t, o, e, err, "")
   230  
   231  	testdata := []struct {
   232  		path    string
   233  		content string
   234  		mode    os.FileMode
   235  	}{
   236  		{out, "hi\n", 0o600},
   237  	}
   238  	for _, v := range testdata {
   239  		info, err := os.Stat(v.path)
   240  		require.NoError(t, err)
   241  		assert.Equal(t, iohelpers.NormalizeFileMode(v.mode), info.Mode())
   242  		content, err := os.ReadFile(v.path)
   243  		require.NoError(t, err)
   244  		assert.Equal(t, v.content, string(content))
   245  	}
   246  }
   247  
   248  func TestBasic_AppliesChmodBeforeWrite(t *testing.T) {
   249  	tmpDir := setupBasicTest(t)
   250  
   251  	// 'broken' was created with mode 0000
   252  	out := tmpDir.Join("broken")
   253  	_, _, err := cmd(t,
   254  		"-f", tmpDir.Join("one"),
   255  		"-o", out,
   256  		"--chmod", "0644").run()
   257  	require.NoError(t, err)
   258  
   259  	info, err := os.Stat(out)
   260  	require.NoError(t, err)
   261  	assert.Equal(t, iohelpers.NormalizeFileMode(0o644), info.Mode())
   262  	content, err := os.ReadFile(out)
   263  	require.NoError(t, err)
   264  	assert.Equal(t, "hi\n", string(content))
   265  }
   266  
   267  func TestBasic_CreatesMissingDirectory(t *testing.T) {
   268  	tmpDir := setupBasicTest(t)
   269  	out := tmpDir.Join("foo/bar/baz")
   270  	o, e, err := cmd(t, "-f", tmpDir.Join("one"), "-o", out).run()
   271  	assertSuccess(t, o, e, err, "")
   272  
   273  	info, err := os.Stat(out)
   274  	require.NoError(t, err)
   275  	assert.Equal(t, iohelpers.NormalizeFileMode(0o640), info.Mode())
   276  	content, err := os.ReadFile(out)
   277  	require.NoError(t, err)
   278  	assert.Equal(t, "hi\n", string(content))
   279  
   280  	out = tmpDir.Join("outdir")
   281  	o, e, err = cmd(t,
   282  		"--input-dir", tmpDir.Join("subdir"),
   283  		"--output-dir", out,
   284  	).run()
   285  	assertSuccess(t, o, e, err, "")
   286  
   287  	info, err = os.Stat(out)
   288  	require.NoError(t, err)
   289  
   290  	assert.Equal(t, iohelpers.NormalizeFileMode(0o755|fs.ModeDir), info.Mode())
   291  	assert.Equal(t, true, info.IsDir())
   292  }