Skip to content

Commit 821d909

Browse files
committed
feat(codegen): add file.SetMode
Adds a new `file.SetMode` function for setting the mode of the currently rendered file. Also added a new `RenderTemplate` helper for simple "run command, check modifications" tests. This will be expanded on later. Converted some tests to use this helper and added some new basic tests using it.
1 parent 6ef6651 commit 821d909

File tree

5 files changed

+165
-96
lines changed

5 files changed

+165
-96
lines changed

.mise.lock

Lines changed: 9 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/codegen/template_test.go

Lines changed: 42 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -38,94 +38,68 @@ var generatedBlockIndentTemplate string
3838
var fakeGeneratedBlockIndentFile string
3939

4040
func TestSingleFileRender(t *testing.T) {
41-
log := slogext.NewTestLogger(t)
42-
fs, err := testmemfs.WithManifest("name: testing\n")
43-
assert.NilError(t, err, "failed to testmemfs.WithManifest")
44-
m, err := modulestest.NewWithFS(t.Context(), "testing", fs)
45-
assert.NilError(t, err, "failed to NewWithFS")
46-
47-
tpl, err := NewTemplate(m, "virtual-file.tpl", 0o644, time.Now(), []byte("hello world!"), log, nil)
48-
assert.NilError(t, err, "failed to create basic template")
49-
50-
sm := &configuration.Manifest{Name: "testing"}
51-
52-
st := NewStencil(sm, nil, []*modules.Module{m}, log, false)
53-
err = tpl.Render(st, NewValues(t.Context(), sm, nil))
54-
assert.NilError(t, err, "expected Render() to not fail")
41+
tpl := RenderTemplate(t, nil, nil, "hello world!")
42+
assert.Equal(t, len(tpl.Files), 1, "expected exactly one file to be rendered")
5543
assert.Equal(t, tpl.Files[0].String(), "hello world!", "expected Render() to modify first created file")
5644
}
5745

5846
func TestMultiFileRender(t *testing.T) {
59-
log := slogext.NewTestLogger(t)
60-
fs, err := testmemfs.WithManifest("name: testing\narguments:\n commands:\n type: list")
61-
assert.NilError(t, err, "failed to testmemfs.WithManifest")
62-
m, err := modulestest.NewWithFS(t.Context(), "testing", fs)
63-
assert.NilError(t, err, "failed to NewWithFS")
64-
65-
tpl, err := NewTemplate(m, "multi-file.tpl", 0o644,
66-
time.Now(), []byte(multiFileTemplate), log, nil)
67-
assert.NilError(t, err, "failed to create template")
68-
69-
sm := &configuration.Manifest{Name: "testing", Arguments: map[string]any{
70-
"commands": []string{"hello", "world", "command"},
71-
}}
72-
73-
st := NewStencil(sm, nil, []*modules.Module{m}, log, false)
74-
err = tpl.Render(st, NewValues(t.Context(), sm, nil))
75-
assert.NilError(t, err, "expected Render() to not fail")
47+
tpl := RenderTemplate(t,
48+
&configuration.Manifest{
49+
Arguments: map[string]any{
50+
"commands": []string{"hello", "world", "command"},
51+
},
52+
},
53+
&configuration.TemplateRepositoryManifest{
54+
Arguments: map[string]configuration.Argument{
55+
"commands": {},
56+
},
57+
}, multiFileTemplate,
58+
)
7659
assert.Equal(t, len(tpl.Files), 3, "expected Render() to create 3 files")
7760

7861
for i, f := range tpl.Files {
79-
assert.Equal(t, f.String(), "command", "rendered template %d contents differred", i)
62+
assert.Equal(t, f.String(), "command", "rendered template %d contents differed", i)
8063
}
8164
}
8265

8366
func TestMultiFileWithInputRender(t *testing.T) {
84-
log := slogext.NewTestLogger(t)
85-
fs, err := testmemfs.WithManifest("name: testing\narguments:\n commands:\n type: list")
86-
assert.NilError(t, err, "failed to testmemfs.WithManifest")
87-
m, err := modulestest.NewWithFS(t.Context(), "testing", fs)
88-
assert.NilError(t, err, "failed to NewWithFS")
89-
90-
tpl, err := NewTemplate(m, "multi-file-input.tpl", 0o644,
91-
time.Now(), []byte(multiFileInputTemplate), log, nil)
92-
assert.NilError(t, err, "failed to create template")
93-
94-
sm := &configuration.Manifest{Name: "testing", Arguments: map[string]any{
95-
"commands": []string{"hello", "world", "command"},
96-
}}
67+
mf := &configuration.Manifest{
68+
Arguments: map[string]any{
69+
"commands": []string{"hello", "world", "command"},
70+
},
71+
}
9772

98-
st := NewStencil(sm, nil, []*modules.Module{m}, log, false)
99-
err = tpl.Render(st, NewValues(t.Context(), sm, nil))
100-
assert.NilError(t, err, "expected Render() to not fail")
73+
tpl := RenderTemplate(t,
74+
mf,
75+
&configuration.TemplateRepositoryManifest{
76+
Arguments: map[string]configuration.Argument{
77+
"commands": {},
78+
},
79+
}, multiFileInputTemplate,
80+
)
10181
assert.Equal(t, len(tpl.Files), 3, "expected Render() to create 3 files")
10282

10383
for i, f := range tpl.Files {
104-
assert.Equal(t, (sm.Arguments["commands"].([]string))[i], f.String(), "rendered template %d contents differred", i)
84+
assert.Equal(t, f.String(), mf.Arguments["commands"].([]string)[i], "rendered template %d contents differed", i)
10585
}
10686
}
10787

10888
func TestIncludeArgumentPassthrough(t *testing.T) {
109-
log := slogext.NewTestLogger(t)
110-
fs, err := testmemfs.WithManifest("name: testing\narguments:\n commands:\n type: list")
111-
assert.NilError(t, err, "failed to testmemfs.WithManifest")
112-
m, err := modulestest.NewWithFS(t.Context(), "testing", fs)
113-
assert.NilError(t, err, "failed to NewWithFS")
114-
115-
tpl, err := NewTemplate(m, "apply-template-passthrough.tpl", 0o644,
116-
time.Now(), []byte(includePassthroughTemplate), log, nil)
117-
assert.NilError(t, err, "failed to create template")
118-
119-
sm := &configuration.Manifest{Name: "testing", Arguments: map[string]any{
120-
"commands": []string{"hello", "world", "command"},
121-
}}
122-
123-
st := NewStencil(sm, nil, []*modules.Module{m}, log, false)
124-
err = tpl.Render(st, NewValues(t.Context(), sm, nil))
125-
assert.NilError(t, err, "expected Render() to not fail")
89+
tpl := RenderTemplate(t,
90+
&configuration.Manifest{
91+
Arguments: map[string]any{
92+
"commands": []string{"hello", "world", "command"},
93+
},
94+
},
95+
&configuration.TemplateRepositoryManifest{
96+
Arguments: map[string]configuration.Argument{
97+
"commands": {},
98+
},
99+
}, includePassthroughTemplate,
100+
)
126101
assert.Equal(t, len(tpl.Files), 1, "expected Render() to create 1 files")
127-
128-
assert.Equal(t, "testing", tpl.Files[0].String(), "rendered template contents differed")
102+
assert.Equal(t, t.Name(), tpl.Files[0].String(), "rendered template contents differed")
129103
}
130104

131105
func TestGeneratedBlock(t *testing.T) {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (C) 2025 stencil contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// SPDX-License-Identifier: Apache-2.0
16+
17+
// Description: Contains high-level abstractions for testing template
18+
// rendering.
19+
20+
package codegen
21+
22+
import (
23+
"testing"
24+
"time"
25+
26+
"go.rgst.io/stencil/v2/internal/modules"
27+
"go.rgst.io/stencil/v2/internal/modules/modulestest"
28+
"go.rgst.io/stencil/v2/pkg/configuration"
29+
"go.rgst.io/stencil/v2/pkg/slogext"
30+
"gotest.tools/v3/assert"
31+
)
32+
33+
// RenderTemplate creates a template with the provided contents,
34+
// executes it, and returns the post-rendered [Template].
35+
//
36+
// TODO(jaredallard): This shouldn't be exposed outside of tests, but
37+
// right now we'd need a decently large test refactor so here it is.
38+
func RenderTemplate(
39+
t *testing.T,
40+
mf *configuration.Manifest,
41+
trmf *configuration.TemplateRepositoryManifest,
42+
contents string,
43+
) *Template {
44+
log := slogext.NewTestLogger(t)
45+
46+
if mf == nil {
47+
mf = &configuration.Manifest{}
48+
}
49+
if mf.Name == "" {
50+
mf.Name = t.Name()
51+
}
52+
53+
if trmf == nil {
54+
trmf = &configuration.TemplateRepositoryManifest{}
55+
}
56+
if trmf.Name == "" {
57+
trmf.Name = t.Name() + "Module"
58+
}
59+
60+
m, err := modulestest.NewModuleFromTemplates(trmf)
61+
assert.NilError(t, err, "expected module creation to not fail")
62+
63+
tpl, err := NewTemplate(m, "test.tpl", 0o755, time.Now(), []byte(contents), log, &NewTemplateOpts{})
64+
assert.NilError(t, err, "expected template creation to not fail")
65+
66+
st := NewStencil(mf, nil, []*modules.Module{m}, log, false)
67+
assert.NilError(t, tpl.Render(st, NewValues(t.Context(), mf, []*modules.Module{m})), "expected render to not fail")
68+
69+
return tpl
70+
}

internal/codegen/tpl_file.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
// Copyright 2022 Outreach Corporation. All Rights Reserved.
1+
// Copyright (C) 2025 stencil contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// SPDX-License-Identifier: Apache-2.0
216

317
// Description: This file implements the file struct passed to
418
// templates in Stencil.
@@ -245,3 +259,11 @@ func (f *TplFile) MigrateTo(path string) (out string, err error) {
245259

246260
return "", nil
247261
}
262+
263+
// SetMode sets the provided mode on the current output file.
264+
//
265+
// {{- file.SetMode 0o755 }}
266+
func (f *TplFile) SetMode(mode os.FileMode) (string, error) {
267+
f.f.mode = mode
268+
return "", nil
269+
}

internal/codegen/tpl_file_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package codegen
22

33
import (
4+
"bytes"
45
"os"
56
"path"
67
"testing"
@@ -269,3 +270,23 @@ func TestTplFile_RemoveAll(t *testing.T) {
269270
_, err = os.Stat("test/test2.go")
270271
assert.ErrorContains(t, err, "no such file")
271272
}
273+
274+
func TestTplFile_SetMode(t *testing.T) {
275+
tpl := RenderTemplate(t, nil, nil, `{{- file.SetMode 0o777 }}`)
276+
assert.Equal(t, tpl.Files[0].Mode(), os.FileMode(0o777), "expected file mode to be 0o777")
277+
}
278+
279+
func TestTplFile_SetPath(t *testing.T) {
280+
tpl := RenderTemplate(t, nil, nil, `{{- file.SetPath "xyz" }}`)
281+
assert.Equal(t, tpl.Files[0].path, "xyz", "expected file path to be xyz")
282+
}
283+
284+
func TestTplFile_SetContents(t *testing.T) {
285+
tpl := RenderTemplate(t, nil, nil, `{{- file.SetContents "xyz" }}`)
286+
assert.Assert(t, bytes.Equal(tpl.Files[0].contents, []byte("xyz")), "expected file contents to be xyz")
287+
}
288+
289+
func TestTplFile_Static(t *testing.T) {
290+
tpl := RenderTemplate(t, nil, nil, `{{- file.Static }}`)
291+
assert.Equal(t, tpl.Files[0].Skipped, false, "expected file to not be skipped when doesn't exist")
292+
}

0 commit comments

Comments
 (0)