Changeset 0.7.2 (#40)
This commit is contained in:
194
server/tools/tools_test.go
Normal file
194
server/tools/tools_test.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ── Registry Tests ──────────────────────────
|
||||
|
||||
func TestRegistryHasTools(t *testing.T) {
|
||||
// init() in notes.go registers 4 tools
|
||||
if !HasTools() {
|
||||
t.Fatal("Expected HasTools() == true after init registration")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryAllDefinitions(t *testing.T) {
|
||||
defs := AllDefinitions()
|
||||
if len(defs) < 4 {
|
||||
t.Errorf("Expected at least 4 tool definitions, got %d", len(defs))
|
||||
}
|
||||
|
||||
expected := map[string]bool{
|
||||
"note_create": false,
|
||||
"note_search": false,
|
||||
"note_update": false,
|
||||
"note_list": false,
|
||||
}
|
||||
for _, d := range defs {
|
||||
if _, ok := expected[d.Name]; ok {
|
||||
expected[d.Name] = true
|
||||
}
|
||||
// Every tool must have a description and parameters
|
||||
if d.Description == "" {
|
||||
t.Errorf("Tool %q has empty description", d.Name)
|
||||
}
|
||||
if len(d.Parameters) == 0 {
|
||||
t.Errorf("Tool %q has empty parameters", d.Name)
|
||||
}
|
||||
}
|
||||
for name, found := range expected {
|
||||
if !found {
|
||||
t.Errorf("Expected tool %q not found in registry", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryGetKnown(t *testing.T) {
|
||||
tool := Get("note_create")
|
||||
if tool == nil {
|
||||
t.Fatal("Get(note_create) returned nil")
|
||||
}
|
||||
def := tool.Definition()
|
||||
if def.Name != "note_create" {
|
||||
t.Errorf("Expected name 'note_create', got %q", def.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegistryGetUnknown(t *testing.T) {
|
||||
tool := Get("nonexistent_tool")
|
||||
if tool != nil {
|
||||
t.Error("Get(nonexistent_tool) should return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteCallUnknownTool(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
execCtx := ExecutionContext{UserID: "test", ChannelID: "test"}
|
||||
call := ToolCall{ID: "call_1", Name: "nonexistent", Arguments: "{}"}
|
||||
|
||||
result := ExecuteCall(ctx, execCtx, call)
|
||||
|
||||
if !result.IsError {
|
||||
t.Error("Expected is_error=true for unknown tool")
|
||||
}
|
||||
if result.ToolCallID != "call_1" {
|
||||
t.Errorf("Expected tool_call_id='call_1', got %q", result.ToolCallID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteAll(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
execCtx := ExecutionContext{UserID: "test", ChannelID: "test"}
|
||||
calls := []ToolCall{
|
||||
{ID: "call_1", Name: "nonexistent_a", Arguments: "{}"},
|
||||
{ID: "call_2", Name: "nonexistent_b", Arguments: "{}"},
|
||||
}
|
||||
|
||||
results := ExecuteAll(ctx, execCtx, calls)
|
||||
|
||||
if len(results) != 2 {
|
||||
t.Fatalf("Expected 2 results, got %d", len(results))
|
||||
}
|
||||
for i, r := range results {
|
||||
if !r.IsError {
|
||||
t.Errorf("Result %d: expected is_error=true", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tool Definition Schema Tests ────────────
|
||||
|
||||
func TestToolDefinitionsHaveValidJSON(t *testing.T) {
|
||||
for _, def := range AllDefinitions() {
|
||||
var schema map[string]interface{}
|
||||
if err := json.Unmarshal(def.Parameters, &schema); err != nil {
|
||||
t.Errorf("Tool %q has invalid JSON schema: %v", def.Name, err)
|
||||
}
|
||||
// Should be a JSON Schema object with "type" and "properties"
|
||||
if schema["type"] != "object" {
|
||||
t.Errorf("Tool %q schema type should be 'object', got %v", def.Name, schema["type"])
|
||||
}
|
||||
if _, ok := schema["properties"]; !ok {
|
||||
t.Errorf("Tool %q schema missing 'properties'", def.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── JSON Schema Helper Tests ────────────────
|
||||
|
||||
func TestPropString(t *testing.T) {
|
||||
p := Prop("string", "a description")
|
||||
|
||||
if p["type"] != "string" {
|
||||
t.Errorf("Expected type=string, got %v", p["type"])
|
||||
}
|
||||
if p["description"] != "a description" {
|
||||
t.Errorf("Expected description, got %v", p["description"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPropEnum(t *testing.T) {
|
||||
p := PropEnum("content mode", "replace", "append", "prepend")
|
||||
|
||||
if p["type"] != "string" {
|
||||
t.Error("Expected type=string")
|
||||
}
|
||||
enums := p["enum"].([]string)
|
||||
if len(enums) != 3 {
|
||||
t.Errorf("Expected 3 enum values, got %d", len(enums))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPropArray(t *testing.T) {
|
||||
p := PropArray("list of tags")
|
||||
|
||||
if p["type"] != "array" {
|
||||
t.Error("Expected type=array")
|
||||
}
|
||||
items := p["items"].(map[string]string)
|
||||
if items["type"] != "string" {
|
||||
t.Error("Expected items.type=string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONSchema(t *testing.T) {
|
||||
props := map[string]interface{}{
|
||||
"title": Prop("string", "note title"),
|
||||
"content": Prop("string", "note body"),
|
||||
}
|
||||
schema := JSONSchema(props, []string{"title"})
|
||||
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(schema, &m); err != nil {
|
||||
t.Fatalf("Invalid JSON: %v", err)
|
||||
}
|
||||
|
||||
if m["type"] != "object" {
|
||||
t.Error("Expected type=object")
|
||||
}
|
||||
required := m["required"].([]interface{})
|
||||
if len(required) != 1 || required[0] != "title" {
|
||||
t.Errorf("Expected required=[title], got %v", required)
|
||||
}
|
||||
mProps := m["properties"].(map[string]interface{})
|
||||
if _, ok := mProps["title"]; !ok {
|
||||
t.Error("Missing property 'title'")
|
||||
}
|
||||
if _, ok := mProps["content"]; !ok {
|
||||
t.Error("Missing property 'content'")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustJSON(t *testing.T) {
|
||||
result := MustJSON(map[string]string{"key": "value"})
|
||||
var m map[string]string
|
||||
if err := json.Unmarshal(result, &m); err != nil {
|
||||
t.Fatalf("MustJSON produced invalid JSON: %v", err)
|
||||
}
|
||||
if m["key"] != "value" {
|
||||
t.Errorf("Expected value, got %q", m["key"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user