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>
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// mkVaultDir creates a temporary directory tree for tests.
|
||||
// entries is a list of relative paths to create.
|
||||
// Paths ending in "/" are directories; others are files with placeholder content.
|
||||
func mkVaultDir(t *testing.T, entries []string) string {
|
||||
t.Helper()
|
||||
root := t.TempDir()
|
||||
for _, e := range entries {
|
||||
full := filepath.Join(root, filepath.FromSlash(e))
|
||||
if e[len(e)-1] == '/' {
|
||||
if err := os.MkdirAll(full, 0o755); err != nil {
|
||||
t.Fatalf("mkVaultDir: mkdir %q: %v", full, err)
|
||||
}
|
||||
} else {
|
||||
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
|
||||
t.Fatalf("mkVaultDir: mkdir parent %q: %v", full, err)
|
||||
}
|
||||
if err := os.WriteFile(full, []byte("test\n"), 0o644); err != nil {
|
||||
t.Fatalf("mkVaultDir: write %q: %v", full, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_DryRun_NoChange(t *testing.T) {
|
||||
root := mkVaultDir(t, []string{
|
||||
"raw/",
|
||||
"raw/file1.csv",
|
||||
"processed/",
|
||||
})
|
||||
|
||||
before := snapshotDir(t, root)
|
||||
report, err := VaultLayoutEnsure(root, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !report.DryRun {
|
||||
t.Error("DryRun flag not set in report")
|
||||
}
|
||||
after := snapshotDir(t, root)
|
||||
if !mapEqual(before, after) {
|
||||
t.Errorf("dry-run modified disk: before=%v after=%v", before, after)
|
||||
}
|
||||
// Should have planned a migration for raw and processed.
|
||||
if len(report.Migrated) == 0 {
|
||||
t.Error("expected Migrated to be non-empty in dry-run plan")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_FreshDir_CreatesLayout(t *testing.T) {
|
||||
root := mkVaultDir(t, []string{}) // empty vault
|
||||
|
||||
report, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// All standard dirs should be created.
|
||||
wantCreated := []string{
|
||||
"data", "knowledge",
|
||||
filepath.Join("data", "raw"),
|
||||
filepath.Join("data", "processed"),
|
||||
filepath.Join("data", "exports"),
|
||||
filepath.Join("knowledge", "decisions"),
|
||||
filepath.Join("knowledge", "domains"),
|
||||
filepath.Join("knowledge", "models"),
|
||||
filepath.Join("knowledge", "benchmarks"),
|
||||
filepath.Join("knowledge", "test_documents"),
|
||||
}
|
||||
createdSet := toSet(report.Created)
|
||||
for _, w := range wantCreated {
|
||||
if _, ok := createdSet[w]; !ok {
|
||||
t.Errorf("expected Created to contain %q, got %v", w, report.Created)
|
||||
}
|
||||
}
|
||||
|
||||
// All directories must actually exist on disk.
|
||||
for _, w := range wantCreated {
|
||||
full := filepath.Join(root, w)
|
||||
info, err := os.Stat(full)
|
||||
if err != nil {
|
||||
t.Errorf("expected %q to exist: %v", full, err)
|
||||
continue
|
||||
}
|
||||
if !info.IsDir() {
|
||||
t.Errorf("%q should be a directory", full)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_LegacyDataLayout_Migrates(t *testing.T) {
|
||||
root := mkVaultDir(t, []string{
|
||||
"raw/",
|
||||
"raw/file1.parquet",
|
||||
"raw/file2.parquet",
|
||||
"processed/",
|
||||
"processed/clean.csv",
|
||||
"exports/",
|
||||
})
|
||||
|
||||
report, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// raw and processed should appear in Migrated (as dirs, top-level rename).
|
||||
migratedSet := toSet(report.Migrated)
|
||||
for _, pair := range []string{
|
||||
"raw -> " + filepath.Join("data", "raw"),
|
||||
"processed -> " + filepath.Join("data", "processed"),
|
||||
} {
|
||||
if _, ok := migratedSet[pair]; !ok {
|
||||
t.Errorf("expected Migrated to contain %q, got %v", pair, report.Migrated)
|
||||
}
|
||||
}
|
||||
|
||||
// Files must have moved.
|
||||
for _, f := range []string{
|
||||
filepath.Join("data", "raw", "file1.parquet"),
|
||||
filepath.Join("data", "raw", "file2.parquet"),
|
||||
filepath.Join("data", "processed", "clean.csv"),
|
||||
} {
|
||||
if _, err := os.Stat(filepath.Join(root, f)); err != nil {
|
||||
t.Errorf("expected %q to exist after migration: %v", f, err)
|
||||
}
|
||||
}
|
||||
// Old dirs must be gone.
|
||||
for _, d := range []string{"raw", "processed"} {
|
||||
if pathExists(filepath.Join(root, d)) {
|
||||
t.Errorf("expected legacy dir %q to be removed", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_LegacyKnowledgeLayout_Migrates(t *testing.T) {
|
||||
root := mkVaultDir(t, []string{
|
||||
"decisions/",
|
||||
"decisions/2024-01.md",
|
||||
"models/",
|
||||
"models/ner_v1.pkl",
|
||||
"README.md",
|
||||
})
|
||||
|
||||
report, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// decisions and models should appear in Migrated.
|
||||
migratedSet := toSet(report.Migrated)
|
||||
for _, pair := range []string{
|
||||
"decisions -> " + filepath.Join("knowledge", "decisions"),
|
||||
"models -> " + filepath.Join("knowledge", "models"),
|
||||
"README.md -> " + filepath.Join("knowledge", "README.md"),
|
||||
} {
|
||||
if _, ok := migratedSet[pair]; !ok {
|
||||
t.Errorf("expected Migrated to contain %q, got %v", pair, report.Migrated)
|
||||
}
|
||||
}
|
||||
|
||||
// Files must be at new location.
|
||||
for _, f := range []string{
|
||||
filepath.Join("knowledge", "decisions", "2024-01.md"),
|
||||
filepath.Join("knowledge", "models", "ner_v1.pkl"),
|
||||
filepath.Join("knowledge", "README.md"),
|
||||
} {
|
||||
if _, err := os.Stat(filepath.Join(root, f)); err != nil {
|
||||
t.Errorf("expected %q to exist after migration: %v", f, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_AlreadyMigrated_Idempotent(t *testing.T) {
|
||||
root := mkVaultDir(t, []string{
|
||||
"data/",
|
||||
"data/raw/",
|
||||
"data/raw/file.csv",
|
||||
"data/processed/",
|
||||
"data/exports/",
|
||||
"knowledge/",
|
||||
"knowledge/decisions/",
|
||||
"knowledge/domains/",
|
||||
"knowledge/models/",
|
||||
"knowledge/benchmarks/",
|
||||
"knowledge/test_documents/",
|
||||
})
|
||||
|
||||
report1, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("first run error: %v", err)
|
||||
}
|
||||
if len(report1.Migrated) != 0 {
|
||||
t.Errorf("first run on fully-migrated vault should have no migrations, got %v", report1.Migrated)
|
||||
}
|
||||
|
||||
before := snapshotDir(t, root)
|
||||
report2, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("second run error: %v", err)
|
||||
}
|
||||
after := snapshotDir(t, root)
|
||||
|
||||
if !mapEqual(before, after) {
|
||||
t.Error("second run modified disk (not idempotent)")
|
||||
}
|
||||
if len(report2.Migrated) != 0 {
|
||||
t.Errorf("second run should produce no migrations, got %v", report2.Migrated)
|
||||
}
|
||||
if len(report2.AlreadyOK) == 0 {
|
||||
t.Error("second run should report existing dirs as AlreadyOK")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_Mixed_PartialMigration(t *testing.T) {
|
||||
// data/raw already migrated; exports still at root; knowledge dirs in legacy positions.
|
||||
root := mkVaultDir(t, []string{
|
||||
"data/",
|
||||
"data/raw/",
|
||||
"data/raw/already_here.csv",
|
||||
"exports/",
|
||||
"exports/report.pdf",
|
||||
"decisions/",
|
||||
"decisions/2023-note.md",
|
||||
})
|
||||
|
||||
report, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// data/raw should be AlreadyOK.
|
||||
if !sliceContains(report.AlreadyOK, filepath.Join("data", "raw")) {
|
||||
t.Errorf("data/raw should be AlreadyOK, got AlreadyOK=%v", report.AlreadyOK)
|
||||
}
|
||||
// exports should be migrated.
|
||||
exportsMigrated := false
|
||||
for _, m := range report.Migrated {
|
||||
if m == "exports -> "+filepath.Join("data", "exports") {
|
||||
exportsMigrated = true
|
||||
}
|
||||
}
|
||||
if !exportsMigrated {
|
||||
t.Errorf("exports should be migrated, Migrated=%v", report.Migrated)
|
||||
}
|
||||
// decisions should be migrated.
|
||||
decisionsMigrated := false
|
||||
for _, m := range report.Migrated {
|
||||
if m == "decisions -> "+filepath.Join("knowledge", "decisions") {
|
||||
decisionsMigrated = true
|
||||
}
|
||||
}
|
||||
if !decisionsMigrated {
|
||||
t.Errorf("decisions should be migrated, Migrated=%v", report.Migrated)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_MergeConflict_Errors(t *testing.T) {
|
||||
// Both src (raw/) and dst (data/raw/) exist and have a file with the same name.
|
||||
root := mkVaultDir(t, []string{
|
||||
"raw/",
|
||||
"raw/collision.csv",
|
||||
"data/",
|
||||
"data/raw/",
|
||||
"data/raw/collision.csv", // same name -> conflict
|
||||
})
|
||||
|
||||
_, err := VaultLayoutEnsure(root, false)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for merge conflict, got nil")
|
||||
}
|
||||
if !contains(err.Error(), "conflict") && !contains(err.Error(), "collision.csv") {
|
||||
t.Errorf("error should mention conflict or the file name, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_UnknownFiles_Skipped(t *testing.T) {
|
||||
root := mkVaultDir(t, []string{
|
||||
".git/",
|
||||
"vault_index.db",
|
||||
"my_custom_notes.txt",
|
||||
"raw/",
|
||||
})
|
||||
|
||||
report, err := VaultLayoutEnsure(root, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
skippedSet := toSet(report.Skipped)
|
||||
for _, name := range []string{".git", "vault_index.db", "my_custom_notes.txt"} {
|
||||
if _, ok := skippedSet[name]; !ok {
|
||||
t.Errorf("expected %q in Skipped, got %v", name, report.Skipped)
|
||||
}
|
||||
}
|
||||
// raw should NOT be in Skipped (it's a known bucket).
|
||||
if _, ok := skippedSet["raw"]; ok {
|
||||
t.Error("raw should not appear in Skipped — it is a known bucket")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVaultLayoutEnsure_NotADir_Errors(t *testing.T) {
|
||||
t.Run("non-existent path", func(t *testing.T) {
|
||||
_, err := VaultLayoutEnsure("/tmp/does_not_exist_fn_registry_test_xyz", false)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for non-existent path")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("path is a file", func(t *testing.T) {
|
||||
f, err := os.CreateTemp("", "vault_layout_*.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
_, err = VaultLayoutEnsure(f.Name(), false)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when vaultPath is a file, not a dir")
|
||||
}
|
||||
if !contains(err.Error(), "not a directory") {
|
||||
t.Errorf("error should mention 'not a directory', got: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
|
||||
// snapshotDir returns a map of relative path -> exists for all entries under root.
|
||||
func snapshotDir(t *testing.T, root string) map[string]bool {
|
||||
t.Helper()
|
||||
snap := make(map[string]bool)
|
||||
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, _ := filepath.Rel(root, path)
|
||||
snap[rel] = true
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("snapshotDir: %v", err)
|
||||
}
|
||||
return snap
|
||||
}
|
||||
|
||||
func mapEqual(a, b map[string]bool) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for k := range a {
|
||||
if !b[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func toSet(ss []string) map[string]struct{} {
|
||||
m := make(map[string]struct{}, len(ss))
|
||||
for _, s := range ss {
|
||||
m[s] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func sliceContains(ss []string, target string) bool {
|
||||
for _, s := range ss {
|
||||
if s == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func contains(s, sub string) bool {
|
||||
return len(s) >= len(sub) && (s == sub || len(sub) == 0 ||
|
||||
func() bool {
|
||||
for i := 0; i <= len(s)-len(sub); i++ {
|
||||
if s[i:i+len(sub)] == sub {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}())
|
||||
}
|
||||
Reference in New Issue
Block a user