A go template renderer based on Perl's Template Toolkit
1package gott
2
3import (
4 "fmt"
5 "strings"
6 "testing"
7)
8
9func TestFunctionCalls(t *testing.T) {
10 tests := []struct {
11 name string
12 template string
13 vars map[string]any
14 want string
15 wantErr bool
16 }{
17 // Option 1: Functions with no arguments
18 {
19 name: "no-arg function returns string",
20 template: "[% myfunc() %]",
21 vars: map[string]any{
22 "myfunc": func() string {
23 return "hello"
24 },
25 },
26 want: "hello",
27 },
28 {
29 name: "no-arg function returns int",
30 template: "[% counter() %]",
31 vars: map[string]any{
32 "counter": func() int {
33 return 42
34 },
35 },
36 want: "42",
37 },
38 {
39 name: "no-arg function returns any (struct)",
40 template: "[% user() %]",
41 vars: map[string]any{
42 "user": func() any {
43 return TestUser{Handle: "testuser", DisplayName: "Test User"}
44 },
45 },
46 want: "{testuser Test User }",
47 },
48 // Option 2: Functions with string arguments
49 {
50 name: "function with string args",
51 template: "[% greet('world') %]",
52 vars: map[string]any{
53 "greet": func(name string) string {
54 return "Hello, " + name + "!"
55 },
56 },
57 want: "Hello, world!",
58 },
59 {
60 name: "function with multiple string args",
61 template: "[% concat('hello', ' ', 'world') %]",
62 vars: map[string]any{
63 "concat": func(a, b, c string) string {
64 return a + b + c
65 },
66 },
67 want: "hello world",
68 },
69 {
70 name: "function with variadic string args",
71 template: "[% join('a', 'b', 'c') %]",
72 vars: map[string]any{
73 "join": func(parts ...string) string {
74 result := ""
75 for i, p := range parts {
76 if i > 0 {
77 result += "-"
78 }
79 result += p
80 }
81 return result
82 },
83 },
84 want: "a-b-c",
85 },
86 {
87 name: "function with variadic string args (all args converted to strings)",
88 template: "[% greet('hello', 'world') %]",
89 vars: map[string]any{
90 "greet": func(args ...string) any {
91 return args[0] + " " + args[1]
92 },
93 },
94 want: "hello world",
95 },
96 {
97 name: "variadic function with numbers (converted to strings)",
98 template: "[% concat(1, 2, 3) %]",
99 vars: map[string]any{
100 "concat": func(args ...string) any {
101 return args[0] + "-" + args[1] + "-" + args[2]
102 },
103 },
104 want: "1-2-3",
105 },
106 {
107 name: "function returns any and needs string conversion",
108 template: "[% getNumber() %]",
109 vars: map[string]any{
110 "getNumber": func() any {
111 return 123.45
112 },
113 },
114 want: "123.45",
115 },
116 // Edge cases and more complex scenarios
117 {
118 name: "function with no args in condition",
119 template: "[% IF enabled() %]enabled[% ELSE %]disabled[% END %]",
120 vars: map[string]any{
121 "enabled": func() bool {
122 return true
123 },
124 },
125 want: "enabled",
126 },
127 {
128 name: "function in foreach loop",
129 template: "[% FOREACH i IN items %][% item() %][% END %]",
130 vars: map[string]any{
131 "items": []int{1, 2, 3},
132 "item": func() string { return "x" },
133 },
134 want: "xxx",
135 },
136 {
137 name: "function with expression as argument",
138 template: "[% sum(2 + 3, 4) %]",
139 vars: map[string]any{
140 "sum": func(args ...string) any {
141 total := 0
142 for _, s := range args {
143 var n int
144 fmt.Sscanf(s, "%d", &n)
145 total += n
146 }
147 return fmt.Sprintf("%d", total)
148 },
149 },
150 want: "9",
151 },
152 {
153 name: "function with variable as argument",
154 template: "[% greet(name) %]",
155 vars: map[string]any{
156 "name": "Alice",
157 "greet": func(args ...string) any {
158 return "Hello, " + args[0] + "!"
159 },
160 },
161 want: "Hello, Alice!",
162 },
163 {
164 name: "function with filter chain",
165 template: "[% upper('hello') | upper %]",
166 vars: map[string]any{
167 "upper": func(args ...string) any {
168 return strings.ToUpper(args[0])
169 },
170 },
171 want: "HELLO",
172 },
173 {
174 name: "undefined function",
175 template: "[% unknown() %]",
176 vars: map[string]any{},
177 wantErr: true,
178 },
179 {
180 name: "variable with same name as function",
181 template: "[% myvar %]",
182 vars: map[string]any{
183 "myvar": "I am a variable",
184 },
185 want: "I am a variable",
186 },
187 }
188
189 for _, tt := range tests {
190 t.Run(tt.name, func(t *testing.T) {
191 r, err := New(nil)
192 if err != nil {
193 t.Fatalf("New() error = %v", err)
194 }
195
196 got, err := r.Process(tt.template, tt.vars)
197 if (err != nil) != tt.wantErr {
198 t.Errorf("Process() error = %v, wantErr %v", err, tt.wantErr)
199 return
200 }
201 if !tt.wantErr && got != tt.want {
202 t.Errorf("Process() = %q, want %q", got, tt.want)
203 }
204 })
205 }
206}