Files
unibots/shell/knowledge/store_test.go
T
agent fc644ecd6e feat: import agents_and_robots platform as unibots (Matrix-out, unibus transport)
Reemplaza el scaffold del echobot por la plataforma completa de bots traida
desde ~/DataProyects/Github/agents_and_robots tras la operacion Matrix-out:
los bots ya no hablan por Matrix sino por el bus unibus (modelo todo-rooms +
E2E via shell/transportunibus sobre github.com/enmanuel/unibus/pkg/client).

- go.mod: replace de unibus -> ../unibus y de fn-registry -> ../../../.. (paths
  relativos reajustados a la nueva ubicacion dentro de fn_registry).
- app.md: bump a 0.2.0, descripcion + arquitectura + comandos + gotchas reales.
- modulo Go conservado como github.com/enmanuel/agents (sin reescribir imports).

agents_and_robots queda archivado como museo de la era Matrix.
2026-06-07 11:50:13 +02:00

209 lines
4.9 KiB
Go

package shellknowledge
import (
"context"
"os"
"path/filepath"
"testing"
"log/slog"
"github.com/enmanuel/agents/pkg/knowledge"
)
func testStore(t *testing.T) (*FileStore, string) {
t.Helper()
dir := t.TempDir()
knowledgeDir := filepath.Join(dir, "knowledge")
dbPath := filepath.Join(dir, "data", "knowledge.db")
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
store, err := New(knowledgeDir, dbPath, logger)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { store.Close() })
return store, knowledgeDir
}
func TestValidSlug(t *testing.T) {
tests := []struct {
slug string
want bool
}{
{"go-patterns", true},
{"ab", true},
{"a-b", true},
{"abc123", true},
{"a", false}, // too short
{"A-B", false}, // uppercase
{"-bad", false}, // starts with hyphen
{"bad-", false}, // ends with hyphen
{"has space", false}, // space
{"has_underscore", false}, // underscore
{"", false},
}
for _, tt := range tests {
if got := ValidSlug(tt.slug); got != tt.want {
t.Errorf("ValidSlug(%q) = %v, want %v", tt.slug, got, tt.want)
}
}
}
func TestPutAndGet(t *testing.T) {
store, _ := testStore(t)
ctx := context.Background()
doc := knowledge.Document{
Slug: "test-doc",
Content: "# Test Document\n\nThis is a test.",
}
if err := store.Put(ctx, doc); err != nil {
t.Fatal(err)
}
got, err := store.Get(ctx, "test-doc")
if err != nil {
t.Fatal(err)
}
if got.Content != doc.Content {
t.Errorf("content mismatch: got %q, want %q", got.Content, doc.Content)
}
if got.Title != "Test Document" {
t.Errorf("title = %q, want %q", got.Title, "Test Document")
}
}
func TestPutInvalidSlug(t *testing.T) {
store, _ := testStore(t)
ctx := context.Background()
err := store.Put(ctx, knowledge.Document{Slug: "BAD", Content: "test"})
if err == nil {
t.Error("expected error for invalid slug")
}
}
func TestPutTooLarge(t *testing.T) {
store, _ := testStore(t)
ctx := context.Background()
bigContent := make([]byte, 65*1024)
for i := range bigContent {
bigContent[i] = 'x'
}
err := store.Put(ctx, knowledge.Document{Slug: "too-big", Content: string(bigContent)})
if err == nil {
t.Error("expected error for oversized document")
}
}
func TestSyncAndSearch(t *testing.T) {
store, knowledgeDir := testStore(t)
ctx := context.Background()
// Write files directly to disk
os.WriteFile(filepath.Join(knowledgeDir, "go-patterns.md"),
[]byte("# Go Patterns\n\nUse interfaces for dependency injection."), 0o644)
os.WriteFile(filepath.Join(knowledgeDir, "matrix-tips.md"),
[]byte("# Matrix Tips\n\nUse mautrix-go for Matrix bots."), 0o644)
if err := store.Sync(ctx); err != nil {
t.Fatal(err)
}
// Search for "interfaces"
results, err := store.Search(ctx, "interfaces", 5)
if err != nil {
t.Fatal(err)
}
if len(results) == 0 {
t.Fatal("expected at least 1 search result")
}
if results[0].Slug != "go-patterns" {
t.Errorf("expected slug go-patterns, got %q", results[0].Slug)
}
}
func TestList(t *testing.T) {
store, _ := testStore(t)
ctx := context.Background()
// Empty initially
docs, err := store.List(ctx)
if err != nil {
t.Fatal(err)
}
if len(docs) != 0 {
t.Errorf("expected 0 docs, got %d", len(docs))
}
// Add two docs
store.Put(ctx, knowledge.Document{Slug: "alpha", Content: "# Alpha\nContent A"})
store.Put(ctx, knowledge.Document{Slug: "beta", Content: "# Beta\nContent B"})
docs, err = store.List(ctx)
if err != nil {
t.Fatal(err)
}
if len(docs) != 2 {
t.Fatalf("expected 2 docs, got %d", len(docs))
}
}
func TestDelete(t *testing.T) {
store, knowledgeDir := testStore(t)
ctx := context.Background()
store.Put(ctx, knowledge.Document{Slug: "to-delete", Content: "# Delete Me\nGoodbye"})
// Verify file exists
if _, err := os.Stat(filepath.Join(knowledgeDir, "to-delete.md")); err != nil {
t.Fatal("file should exist after Put")
}
if err := store.Delete(ctx, "to-delete"); err != nil {
t.Fatal(err)
}
// File removed
if _, err := os.Stat(filepath.Join(knowledgeDir, "to-delete.md")); !os.IsNotExist(err) {
t.Error("file should be removed after Delete")
}
// Not in index
_, err := store.Get(ctx, "to-delete")
if err == nil {
t.Error("expected error for deleted document")
}
}
func TestGetNotFound(t *testing.T) {
store, _ := testStore(t)
ctx := context.Background()
_, err := store.Get(ctx, "nonexistent")
if err == nil {
t.Error("expected error for nonexistent document")
}
}
func TestExtractTitle(t *testing.T) {
tests := []struct {
content string
slug string
want string
}{
{"# My Title\nBody", "slug", "My Title"},
{"No heading here", "my-doc", "My doc"},
{"", "empty-doc", "Empty doc"},
{"\n\n# Late Title\n", "slug", "Late Title"},
}
for _, tt := range tests {
got := extractTitle(tt.content, tt.slug)
if got != tt.want {
t.Errorf("extractTitle(%q, %q) = %q, want %q", tt.content, tt.slug, got, tt.want)
}
}
}