package gott import ( "fmt" "strings" "testing" ) func TestFunctionCalls(t *testing.T) { tests := []struct { name string template string vars map[string]any want string wantErr bool }{ // Option 1: Functions with no arguments { name: "no-arg function returns string", template: "[% myfunc() %]", vars: map[string]any{ "myfunc": func() string { return "hello" }, }, want: "hello", }, { name: "no-arg function returns int", template: "[% counter() %]", vars: map[string]any{ "counter": func() int { return 42 }, }, want: "42", }, { name: "no-arg function returns any (struct)", template: "[% user() %]", vars: map[string]any{ "user": func() any { return TestUser{Handle: "testuser", DisplayName: "Test User"} }, }, want: "{testuser Test User }", }, // Option 2: Functions with string arguments { name: "function with string args", template: "[% greet('world') %]", vars: map[string]any{ "greet": func(name string) string { return "Hello, " + name + "!" }, }, want: "Hello, world!", }, { name: "function with multiple string args", template: "[% concat('hello', ' ', 'world') %]", vars: map[string]any{ "concat": func(a, b, c string) string { return a + b + c }, }, want: "hello world", }, { name: "function with variadic string args", template: "[% join('a', 'b', 'c') %]", vars: map[string]any{ "join": func(parts ...string) string { result := "" for i, p := range parts { if i > 0 { result += "-" } result += p } return result }, }, want: "a-b-c", }, { name: "function with variadic string args (all args converted to strings)", template: "[% greet('hello', 'world') %]", vars: map[string]any{ "greet": func(args ...string) any { return args[0] + " " + args[1] }, }, want: "hello world", }, { name: "variadic function with numbers (converted to strings)", template: "[% concat(1, 2, 3) %]", vars: map[string]any{ "concat": func(args ...string) any { return args[0] + "-" + args[1] + "-" + args[2] }, }, want: "1-2-3", }, { name: "function returns any and needs string conversion", template: "[% getNumber() %]", vars: map[string]any{ "getNumber": func() any { return 123.45 }, }, want: "123.45", }, // Edge cases and more complex scenarios { name: "function with no args in condition", template: "[% IF enabled() %]enabled[% ELSE %]disabled[% END %]", vars: map[string]any{ "enabled": func() bool { return true }, }, want: "enabled", }, { name: "function in foreach loop", template: "[% FOREACH i IN items %][% item() %][% END %]", vars: map[string]any{ "items": []int{1, 2, 3}, "item": func() string { return "x" }, }, want: "xxx", }, { name: "function with expression as argument", template: "[% sum(2 + 3, 4) %]", vars: map[string]any{ "sum": func(args ...string) any { total := 0 for _, s := range args { var n int fmt.Sscanf(s, "%d", &n) total += n } return fmt.Sprintf("%d", total) }, }, want: "9", }, { name: "function with variable as argument", template: "[% greet(name) %]", vars: map[string]any{ "name": "Alice", "greet": func(args ...string) any { return "Hello, " + args[0] + "!" }, }, want: "Hello, Alice!", }, { name: "function with filter chain", template: "[% upper('hello') | upper %]", vars: map[string]any{ "upper": func(args ...string) any { return strings.ToUpper(args[0]) }, }, want: "HELLO", }, { name: "undefined function", template: "[% unknown() %]", vars: map[string]any{}, wantErr: true, }, { name: "variable with same name as function", template: "[% myvar %]", vars: map[string]any{ "myvar": "I am a variable", }, want: "I am a variable", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r, err := New(nil) if err != nil { t.Fatalf("New() error = %v", err) } got, err := r.Process(tt.template, tt.vars) if (err != nil) != tt.wantErr { t.Errorf("Process() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && got != tt.want { t.Errorf("Process() = %q, want %q", got, tt.want) } }) } }