Files
fn_registry/functions/infra/vault_search_test.go
T
egutierrez a802f59f55 chore: auto-commit (95 archivos)
- cmd/fn/doctor.go
- cmd/fn/main.go
- cpp/apps/primitives_gallery/playground/tables/CMakeLists.txt
- cpp/apps/primitives_gallery/playground/tables/data_table.cpp
- cpp/apps/primitives_gallery/playground/tables/data_table_logic.cpp
- cpp/apps/primitives_gallery/playground/tables/data_table_logic.h
- cpp/apps/primitives_gallery/playground/tables/self_test.cpp
- cpp/apps/primitives_gallery/playground/tables/tql.cpp
- cpp/apps/primitives_gallery/playground/tables/viz.cpp
- cpp/apps/primitives_gallery/playground/tables/viz.h
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:50:34 +02:00

148 lines
4.3 KiB
Go

package infra
import (
"testing"
"time"
)
// openTestVaultDB creates a fresh vault_index.db in a temp dir and returns the path.
func openTestVaultDir(t *testing.T) string {
t.Helper()
dir := t.TempDir()
db, err := VaultIndexOpen(dir)
if err != nil {
t.Fatalf("VaultIndexOpen: %v", err)
}
db.Close()
return dir
}
// seedVaultFile inserts a row into files + files_fts.
func seedVaultFile(t *testing.T, dir, relPath, mime, bucket, subBucket, contentText string, size int64) {
t.Helper()
db, err := VaultIndexOpen(dir)
if err != nil {
t.Fatalf("VaultIndexOpen seed: %v", err)
}
defer db.Close()
now := time.Now().Unix()
_, err = db.Exec(`
INSERT INTO files (rel_path, size, mtime, sha256, mime, ext, bucket, sub_bucket, indexed_at)
VALUES (?, ?, ?, 'aabbccdd', ?, '', ?, ?, ?)`,
relPath, size, now, mime, bucket, subBucket, now,
)
if err != nil {
t.Fatalf("seed files: %v", err)
}
_, err = db.Exec(`INSERT INTO files_fts(rel_path, content_text) VALUES (?, ?)`, relPath, contentText)
if err != nil {
t.Fatalf("seed files_fts: %v", err)
}
}
// --- Tests ---
func TestVaultSearch_FTSMatch(t *testing.T) {
t.Run("FTS match devuelve hit con snippet", func(t *testing.T) {
dir := openTestVaultDir(t)
seedVaultFile(t, dir, "data/raw/informe.csv", "text/csv", "data", "raw",
"ventas trimestrales empresa iberica", 1024)
seedVaultFile(t, dir, "data/raw/other.csv", "text/csv", "data", "raw",
"productos inventario almacen", 512)
hits, err := VaultSearch(dir, "ventas", 10)
if err != nil {
t.Fatalf("VaultSearch: %v", err)
}
if len(hits) != 1 {
t.Fatalf("got %d hits, want 1", len(hits))
}
if hits[0].RelPath != "data/raw/informe.csv" {
t.Errorf("RelPath = %q, want data/raw/informe.csv", hits[0].RelPath)
}
if hits[0].VaultName == "" {
t.Errorf("VaultName should not be empty")
}
})
}
func TestVaultSearch_NoMatch(t *testing.T) {
t.Run("query sin resultados retorna slice vacio", func(t *testing.T) {
dir := openTestVaultDir(t)
seedVaultFile(t, dir, "data/raw/file.csv", "text/csv", "data", "raw", "some content", 100)
hits, err := VaultSearch(dir, "zzznomatch", 10)
if err != nil {
t.Fatalf("VaultSearch: %v", err)
}
if len(hits) != 0 {
t.Errorf("got %d hits, want 0", len(hits))
}
})
}
func TestVaultSearch_LimitRespected(t *testing.T) {
t.Run("limit se respeta", func(t *testing.T) {
dir := openTestVaultDir(t)
for i := 0; i < 10; i++ {
path := "data/raw/file" + string(rune('a'+i)) + ".csv"
seedVaultFile(t, dir, path, "text/csv", "data", "raw", "common keyword everywhere", 100)
}
hits, err := VaultSearch(dir, "common", 3)
if err != nil {
t.Fatalf("VaultSearch: %v", err)
}
if len(hits) != 3 {
t.Errorf("got %d hits, want 3", len(hits))
}
})
}
func TestVaultSearch_BadFTSQuery_FallbackLike(t *testing.T) {
t.Run("query FTS invalida activa fallback LIKE", func(t *testing.T) {
dir := openTestVaultDir(t)
// Insert a file whose rel_path contains "foobar" so LIKE can find it.
seedVaultFile(t, dir, "data/raw/foobar_report.csv", "text/csv", "data", "raw", "", 200)
// "foo:bar:" — colon after a non-column name triggers FTS5 parser error.
// safeFTSQuery passes it through unchanged because it contains ":"
// → FTS5 "no such column: bar" → fallback LIKE on rel_path.
hits, err := VaultSearch(dir, "foo:bar:", 10)
if err != nil {
t.Fatalf("VaultSearch: %v", err)
}
if len(hits) == 0 {
t.Errorf("expected fallback LIKE to find foobar_report.csv, got 0 hits")
}
for _, h := range hits {
if h.Snippet != "" {
t.Errorf("fallback hits should have empty Snippet, got %q", h.Snippet)
}
}
})
}
func TestVaultSearch_LimitZeroDefaults(t *testing.T) {
t.Run("limit cero usa 50 por defecto", func(t *testing.T) {
dir := openTestVaultDir(t)
// Insert 55 files with the same keyword.
for i := 0; i < 55; i++ {
path := "data/raw/doc" + string(rune('a')) + string(rune(int('0')+i%10)) + ".csv"
if i >= 10 {
path = "data/raw/doc" + string(rune('b'+i/10-1)) + string(rune(int('0')+i%10)) + ".csv"
}
seedVaultFile(t, dir, path, "text/csv", "data", "raw", "keyword alpha beta", 100)
}
hits, err := VaultSearch(dir, "keyword", 0)
if err != nil {
t.Fatalf("VaultSearch: %v", err)
}
if len(hits) != 50 {
t.Errorf("got %d hits, want 50 (default limit)", len(hits))
}
})
}