e3c8979e8d
- 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>
148 lines
4.3 KiB
Go
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))
|
|
}
|
|
})
|
|
}
|