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 }