package knowledgetools import ( "context" "testing" "github.com/enmanuel/agents/pkg/knowledge" "github.com/enmanuel/agents/tools" ) // mockKnowledgeStore implements KnowledgeStore for testing. type mockKnowledgeStore struct { docs map[string]knowledge.Document } func newMockKnowledgeStore() *mockKnowledgeStore { return &mockKnowledgeStore{docs: make(map[string]knowledge.Document)} } func (m *mockKnowledgeStore) Search(_ context.Context, query string, limit int) ([]knowledge.SearchResult, error) { var results []knowledge.SearchResult for _, d := range m.docs { if len(results) >= limit { break } results = append(results, knowledge.SearchResult{ Slug: d.Slug, Title: d.Title, Snippet: d.Content[:min(len(d.Content), 50)], Rank: 1.0, }) } return results, nil } func (m *mockKnowledgeStore) Get(_ context.Context, slug string) (*knowledge.Document, error) { d, ok := m.docs[slug] if !ok { return nil, ¬FoundError{slug} } return &d, nil } func (m *mockKnowledgeStore) Put(_ context.Context, doc knowledge.Document) error { m.docs[doc.Slug] = doc return nil } func (m *mockKnowledgeStore) List(_ context.Context) ([]knowledge.Document, error) { var docs []knowledge.Document for _, d := range m.docs { docs = append(docs, d) } return docs, nil } type notFoundError struct{ slug string } func (e *notFoundError) Error() string { return "not found: " + e.slug } func TestKnowledgeSearchTool(t *testing.T) { store := newMockKnowledgeStore() store.docs["go-patterns"] = knowledge.Document{ Slug: "go-patterns", Title: "Go Patterns", Content: "Use interfaces", } tool := NewKnowledgeSearch(store) if tool.Def.Name != "knowledge_search" { t.Errorf("name = %q, want knowledge_search", tool.Def.Name) } // Missing query r := tool.Exec(context.Background(), map[string]any{}) if r.Err == nil { t.Error("expected error for missing query") } // Valid search r = tool.Exec(context.Background(), map[string]any{"query": "go"}) if r.Err != nil { t.Errorf("unexpected error: %v", r.Err) } if r.Output == "" { t.Error("expected non-empty output") } } func TestKnowledgeReadTool(t *testing.T) { store := newMockKnowledgeStore() store.docs["test-doc"] = knowledge.Document{ Slug: "test-doc", Title: "Test", Content: "Hello world", } tool := NewKnowledgeRead(store) // Missing slug r := tool.Exec(context.Background(), map[string]any{}) if r.Err == nil { t.Error("expected error for missing slug") } // Valid read r = tool.Exec(context.Background(), map[string]any{"slug": "test-doc"}) if r.Err != nil { t.Errorf("unexpected error: %v", r.Err) } if r.Output != "Hello world" { t.Errorf("output = %q, want %q", r.Output, "Hello world") } // Not found r = tool.Exec(context.Background(), map[string]any{"slug": "nope"}) if r.Err == nil { t.Error("expected error for nonexistent doc") } } func TestKnowledgeWriteTool(t *testing.T) { store := newMockKnowledgeStore() tool := NewKnowledgeWrite(store) // Missing params r := tool.Exec(context.Background(), map[string]any{"slug": "test"}) if r.Err == nil { t.Error("expected error for missing content") } // Valid write r = tool.Exec(context.Background(), map[string]any{ "slug": "new-doc", "content": "# New Doc\nSome content", }) if r.Err != nil { t.Errorf("unexpected error: %v", r.Err) } if _, ok := store.docs["new-doc"]; !ok { t.Error("document was not stored") } } func TestKnowledgeListTool(t *testing.T) { store := newMockKnowledgeStore() tool := NewKnowledgeList(store) // Empty r := tool.Exec(context.Background(), map[string]any{}) if r.Err != nil { t.Errorf("unexpected error: %v", r.Err) } if r.Output != "knowledge base is empty" { t.Errorf("expected empty message, got %q", r.Output) } // With docs store.docs["doc1"] = knowledge.Document{Slug: "doc1", Title: "Doc 1"} r = tool.Exec(context.Background(), map[string]any{}) if r.Err != nil { t.Errorf("unexpected error: %v", r.Err) } if r.Output == "knowledge base is empty" { t.Error("expected non-empty output after adding docs") } } func TestGetInt(t *testing.T) { tests := []struct { args map[string]any key string want int }{ {map[string]any{"n": float64(5)}, "n", 5}, {map[string]any{"n": 3}, "n", 3}, {map[string]any{"n": "str"}, "n", 0}, {map[string]any{}, "n", 0}, } for _, tt := range tests { got := tools.GetInt(tt.args, tt.key) if got != tt.want { t.Errorf("GetInt(%v, %q) = %d, want %d", tt.args, tt.key, got, tt.want) } } }