package fix import ( "os" "path/filepath" "testing" _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" ) // TestFixFunctions_RequireBeadsDir verifies all fix functions properly validate // that a .beads directory exists before attempting fixes. // This replaces 10+ individual "missing .beads directory" subtests. func TestFixFunctions_RequireBeadsDir(t *testing.T) { funcs := []struct { name string fn func(string) error }{ {"GitHooks", GitHooks}, {"MergeDriver", MergeDriver}, {"Daemon", Daemon}, {"DBJSONLSync", DBJSONLSync}, {"DatabaseVersion", DatabaseVersion}, {"SchemaCompatibility", SchemaCompatibility}, {"SyncBranchConfig", SyncBranchConfig}, {"SyncBranchHealth", func(dir string) error { return SyncBranchHealth(dir, "beads-sync") }}, {"UntrackedJSONL", UntrackedJSONL}, {"MigrateTombstones", MigrateTombstones}, {"ChildParentDependencies", ChildParentDependencies}, } for _, tc := range funcs { t.Run(tc.name, func(t *testing.T) { // Use a temp directory without .beads dir := t.TempDir() err := tc.fn(dir) if err == nil { t.Errorf("%s should return error for missing .beads directory", tc.name) } }) } } func TestChildParentDependencies_NoBadDeps(t *testing.T) { // Set up test database with no child→parent deps dir := t.TempDir() beadsDir := filepath.Join(dir, ".beads") if err := os.MkdirAll(beadsDir, 0755); err != nil { t.Fatal(err) } dbPath := filepath.Join(beadsDir, "beads.db") db, err := openDB(dbPath) if err != nil { t.Fatal(err) } // Create minimal schema _, err = db.Exec(` CREATE TABLE issues (id TEXT PRIMARY KEY); CREATE TABLE dependencies (issue_id TEXT, depends_on_id TEXT, type TEXT); CREATE TABLE dirty_issues (issue_id TEXT PRIMARY KEY); INSERT INTO issues (id) VALUES ('bd-abc'), ('bd-abc.1'), ('bd-xyz'); INSERT INTO dependencies (issue_id, depends_on_id, type) VALUES ('bd-abc.1', 'bd-xyz', 'blocks'); `) if err != nil { t.Fatal(err) } db.Close() // Run fix - should find no bad deps err = ChildParentDependencies(dir) if err != nil { t.Errorf("ChildParentDependencies failed: %v", err) } // Verify the good dependency still exists db, _ = openDB(dbPath) defer db.Close() var count int db.QueryRow("SELECT COUNT(*) FROM dependencies").Scan(&count) if count != 1 { t.Errorf("Expected 1 dependency, got %d", count) } } func TestChildParentDependencies_FixesBadDeps(t *testing.T) { // Set up test database with child→parent deps dir := t.TempDir() beadsDir := filepath.Join(dir, ".beads") if err := os.MkdirAll(beadsDir, 0755); err != nil { t.Fatal(err) } dbPath := filepath.Join(beadsDir, "beads.db") db, err := openDB(dbPath) if err != nil { t.Fatal(err) } // Create minimal schema with child→parent dependency _, err = db.Exec(` CREATE TABLE issues (id TEXT PRIMARY KEY); CREATE TABLE dependencies (issue_id TEXT, depends_on_id TEXT, type TEXT); CREATE TABLE dirty_issues (issue_id TEXT PRIMARY KEY); INSERT INTO issues (id) VALUES ('bd-abc'), ('bd-abc.1'), ('bd-abc.1.2'); INSERT INTO dependencies (issue_id, depends_on_id, type) VALUES ('bd-abc.1', 'bd-abc', 'blocks'), ('bd-abc.1.2', 'bd-abc', 'blocks'), ('bd-abc.1.2', 'bd-abc.1', 'blocks'); `) if err != nil { t.Fatal(err) } db.Close() // Run fix err = ChildParentDependencies(dir) if err != nil { t.Errorf("ChildParentDependencies failed: %v", err) } // Verify all bad dependencies were removed db, _ = openDB(dbPath) defer db.Close() var count int db.QueryRow("SELECT COUNT(*) FROM dependencies").Scan(&count) if count != 0 { t.Errorf("Expected 0 dependencies after fix, got %d", count) } // Verify dirty_issues was updated for affected issues // Note: 2 unique issue_ids (bd-abc.1 appears once, bd-abc.1.2 appears twice but INSERT OR IGNORE dedupes) var dirtyCount int db.QueryRow("SELECT COUNT(*) FROM dirty_issues").Scan(&dirtyCount) if dirtyCount != 2 { t.Errorf("Expected 2 dirty issues (unique issue_ids from removed deps), got %d", dirtyCount) } }