8d89a762fb
Cada tool ahora vive en su propio subpackage dentro de tools/ (clock, file, http, knowledgetools, matrix, memorytools, ssh, weather) en lugar de archivos planos en el paquete raíz tools/. Esto mejora la organización, permite imports selectivos y reduce acoplamiento entre tools. El paquete tools/ raíz conserva los tipos base (Def, Param, Result, ToolFunc, Tool, Registry). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
183 lines
4.4 KiB
Go
183 lines
4.4 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|