package llm import ( "testing" "time" "code.chimeric.al/chimerical/odidere/internal/config" ) func TestTool_ParseArguments(t *testing.T) { tool := &Tool{ Arguments: []string{"--name {{.name}}", "{{if .flag}}--enabled{{end}}"}, } tests := []struct { name string args string expected []string }{ { name: "basic", args: `{"name": "test"}`, expected: []string{"--name test"}, }, { name: "with flag", args: `{"name": "test", "flag": true}`, expected: []string{"--name test", "--enabled"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tool.ParseArguments(tt.args) if err != nil { t.Fatalf("ParseArguments failed: %v", err) } if len(got) != len(tt.expected) { t.Errorf("expected len %d, got %d", len(tt.expected), len(got)) } for i := range got { if got[i] != tt.expected[i] { t.Errorf("expected %q, got %q", tt.expected[i], got[i]) } } }) } } func TestNewTool(t *testing.T) { tests := []struct { name string cfg config.ToolConfig wantErr bool }{ { name: "valid", cfg: config.ToolConfig{ Name: "test", Description: "desc", Command: "ls", Timeout: "10s", }, wantErr: false, }, { name: "invalid timeout", cfg: config.ToolConfig{ Name: "test", Description: "desc", Command: "ls", Timeout: "invalid", }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tool, err := NewTool(tt.cfg) if (err != nil) != tt.wantErr { t.Errorf("NewTool() error = %v, wantErr %v", err, tt.wantErr) } if err == nil { if tool.Name != tt.cfg.Name { t.Errorf("expected name %s, got %s", tt.cfg.Name, tool.Name) } if tt.cfg.Timeout == "10s" && tool.timeout != 10*time.Second { t.Errorf("expected timeout 10s, got %v", tool.timeout) } } }) } } func TestRegistry_NewRegistry(t *testing.T) { tests := []struct { name string tools []config.ToolConfig wantErr bool }{ { name: "valid", tools: []config.ToolConfig{ {Name: "t1", Description: "d1", Command: "c1"}, {Name: "t2", Description: "d2", Command: "c2"}, }, wantErr: false, }, { name: "duplicate", tools: []config.ToolConfig{ {Name: "t1", Description: "d1", Command: "c1"}, {Name: "t1", Description: "d2", Command: "c2"}, }, wantErr: true, }, { name: "invalid tool", tools: []config.ToolConfig{ {Name: "t1", Description: "d1", Command: "c1", Timeout: "invalid"}, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := NewRegistry(tt.tools) if (err != nil) != tt.wantErr { t.Errorf("NewRegistry() error = %v, wantErr %v", err, tt.wantErr) } }) } } func TestRegistry_ToAPI_Nil(t *testing.T) { var reg *Registry got := reg.ToAPI() if got != nil { t.Errorf("Registry.ToAPI(nil) = %v, want nil", got) } } func TestRegistry_ToAPI_Empty(t *testing.T) { reg, err := NewRegistry(nil) if err != nil { t.Fatalf("NewRegistry(nil) error = %v", err) } got := reg.ToAPI() if len(got) != 0 { t.Errorf("Registry.ToAPI(empty) len = %d, want 0", len(got)) } } func TestRegistry_ToAPI_Single(t *testing.T) { reg, err := NewRegistry([]config.ToolConfig{ { Name: "weather", Description: "Get the weather", Parameters: map[string]any{"type": "object"}, }, }) if err != nil { t.Fatalf("NewRegistry error = %v", err) } got := reg.ToAPI() if len(got) != 1 { t.Fatalf("len = %d, want 1", len(got)) } if got[0].Type != "function" { t.Errorf("type = %q, want %q", got[0].Type, "function") } if got[0].Function.Name != "weather" { t.Errorf("name = %q, want %q", got[0].Function.Name, "weather") } if got[0].Function.Description != "Get the weather" { t.Errorf("description = %q, want %q", got[0].Function.Description, "Get the weather") } } func TestTool_ToAPI(t *testing.T) { tool := &Tool{ Name: "test", Description: "A test tool", Parameters: map[string]any{"type": "object"}, Command: "echo", Arguments: []string{"hello"}, } api := tool.ToAPI() if api.Type != "function" { t.Errorf("type = %q, want %q", api.Type, "function") } if api.Function.Name != "test" { t.Errorf("name = %q, want %q", api.Function.Name, "test") } if api.Function.Description != "A test tool" { t.Errorf("description = %q, want %q", api.Function.Description, "A test tool") } if api.Function.Parameters == nil { t.Error("parameters is nil") } }