package infra import ( "os" "path/filepath" "testing" "time" ) // setupDoctorRepo creates a repo layout with one vault in a project manifest. // vaultPath must be an absolute path that already exists (or not, for missing tests). func setupDoctorRepo(t *testing.T, vaultName, projectID, vaultPath string) string { t.Helper() root := t.TempDir() projVaultsDir := filepath.Join(root, "projects", projectID, "vaults") if err := os.MkdirAll(projVaultsDir, 0755); err != nil { t.Fatalf("mkdir projects: %v", err) } manifest := "vaults:\n - name: " + vaultName + "\n description: test vault\n path: " + vaultPath + "\n tags: []\n" if err := os.WriteFile(filepath.Join(projVaultsDir, "vault.yaml"), []byte(manifest), 0644); err != nil { t.Fatalf("write vault.yaml: %v", err) } return root } func TestVaultDoctor_OK(t *testing.T) { vaultDir := t.TempDir() // Proper layout if err := os.MkdirAll(filepath.Join(vaultDir, "data", "raw"), 0755); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(vaultDir, "knowledge"), 0755); err != nil { t.Fatal(err) } // Create a file with a past mtime so the index is not stale samplePath := filepath.Join(vaultDir, "data", "raw", "sample.csv") if err := os.WriteFile(samplePath, []byte("a,b\n1,2\n"), 0644); err != nil { t.Fatal(err) } pastTime := time.Now().Add(-1 * time.Hour) if err := os.Chtimes(samplePath, pastTime, pastTime); err != nil { t.Fatal(err) } // Create vault_index.db with the file indexed after its mtime vdb, err := VaultIndexOpen(vaultDir) if err != nil { t.Fatalf("VaultIndexOpen: %v", err) } futureIndexed := time.Now().Unix() // indexed_at is now — after file mtime _, err = vdb.Exec(`INSERT INTO files (rel_path, size, mtime, sha256, mime, ext, bucket, sub_bucket, indexed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, "data/raw/sample.csv", 8, pastTime.Unix(), "deadbeef", "text/csv", ".csv", "data", "raw", futureIndexed) if err != nil { t.Fatalf("insert: %v", err) } vdb.Close() root := setupDoctorRepo(t, "my_vault", "my_proj", vaultDir) entries, err := VaultDoctor(root) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(entries) != 1 { t.Fatalf("expected 1 entry, got %d", len(entries)) } e := entries[0] if e.Status != "ok" { t.Errorf("Status: want ok, got %s (issues: %v)", e.Status, e.Issues) } if len(e.Issues) != 0 { t.Errorf("Issues: want empty, got %v", e.Issues) } if e.DiskFiles != 1 { t.Errorf("DiskFiles: want 1, got %d", e.DiskFiles) } if e.IndexedFiles != 1 { t.Errorf("IndexedFiles: want 1, got %d", e.IndexedFiles) } } func TestVaultDoctor_MissingDir(t *testing.T) { missingPath := filepath.Join(t.TempDir(), "does_not_exist") root := setupDoctorRepo(t, "missing_vault", "my_proj", missingPath) entries, err := VaultDoctor(root) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(entries) != 1 { t.Fatalf("expected 1 entry, got %d", len(entries)) } e := entries[0] if e.Status != "error" { t.Errorf("Status: want error, got %s", e.Status) } found := false for _, issue := range e.Issues { if issue == "directory_missing" { found = true } } if !found { t.Errorf("Expected directory_missing issue, got %v", e.Issues) } } func TestVaultDoctor_NoIndex(t *testing.T) { vaultDir := t.TempDir() // Proper layout but no vault_index.db if err := os.MkdirAll(filepath.Join(vaultDir, "data", "raw"), 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(vaultDir, "data", "raw", "a.csv"), []byte("x"), 0644); err != nil { t.Fatal(err) } root := setupDoctorRepo(t, "no_index_vault", "my_proj", vaultDir) entries, err := VaultDoctor(root) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(entries) != 1 { t.Fatalf("expected 1 entry, got %d", len(entries)) } e := entries[0] if e.Status != "warning" { t.Errorf("Status: want warning, got %s", e.Status) } found := false for _, issue := range e.Issues { if issue == "index_missing" { found = true } } if !found { t.Errorf("Expected index_missing issue, got %v", e.Issues) } } func TestVaultDoctor_LayoutDrift(t *testing.T) { vaultDir := t.TempDir() // No data/ or knowledge/ — just a random file at root if err := os.WriteFile(filepath.Join(vaultDir, "something.txt"), []byte("hi"), 0644); err != nil { t.Fatal(err) } root := setupDoctorRepo(t, "layout_vault", "my_proj", vaultDir) entries, err := VaultDoctor(root) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(entries) != 1 { t.Fatalf("expected 1 entry, got %d", len(entries)) } e := entries[0] if e.Status != "warning" { t.Errorf("Status: want warning, got %s", e.Status) } foundLayout := false for _, issue := range e.Issues { if issue == "layout_missing" || issue == "non_standard_layout" { foundLayout = true } } if !foundLayout { t.Errorf("Expected layout_missing or non_standard_layout, got %v", e.Issues) } } func TestVaultDoctor_EmptyVault(t *testing.T) { vaultDir := t.TempDir() // data/ and knowledge/ exist but are empty if err := os.MkdirAll(filepath.Join(vaultDir, "data"), 0755); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(vaultDir, "knowledge"), 0755); err != nil { t.Fatal(err) } // Create vault_index.db (empty) vdb, err := VaultIndexOpen(vaultDir) if err != nil { t.Fatalf("VaultIndexOpen: %v", err) } vdb.Close() root := setupDoctorRepo(t, "empty_vault", "my_proj", vaultDir) entries, err := VaultDoctor(root) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(entries) != 1 { t.Fatalf("expected 1 entry, got %d", len(entries)) } e := entries[0] if e.Status != "warning" { t.Errorf("Status: want warning, got %s (issues: %v)", e.Status, e.Issues) } found := false for _, issue := range e.Issues { if issue == "empty_vault" { found = true } } if !found { t.Errorf("Expected empty_vault issue, got %v", e.Issues) } }