package infra import ( "database/sql" "os" "path/filepath" "testing" _ "github.com/mattn/go-sqlite3" ) // setupTestRegistry creates a minimal registry.db with pc_locations + apps tables // in a temp directory and returns the root path + a cleanup func. func setupTestRegistry(t *testing.T) (string, func()) { t.Helper() root := t.TempDir() dbPath := filepath.Join(root, "registry.db") db, err := sql.Open("sqlite3", dbPath) if err != nil { t.Fatalf("open test db: %v", err) } defer db.Close() _, err = db.Exec(` CREATE TABLE pc_locations ( id TEXT PRIMARY KEY, entity_type TEXT NOT NULL, entity_id TEXT NOT NULL, pc_id TEXT NOT NULL, dir_path TEXT NOT NULL, status TEXT NOT NULL, notes TEXT, created_at TEXT, updated_at TEXT ); CREATE TABLE apps ( id TEXT PRIMARY KEY, name TEXT, dir_path TEXT NOT NULL DEFAULT '' ); CREATE TABLE analysis ( id TEXT PRIMARY KEY, name TEXT, dir_path TEXT NOT NULL DEFAULT '' ); `) if err != nil { t.Fatalf("create schema: %v", err) } return root, func() {} } func insertLocation(t *testing.T, root, entityType, entityID, dirPath, status, pcID string) { t.Helper() db, _ := sql.Open("sqlite3", filepath.Join(root, "registry.db")) defer db.Close() id := entityType + "_" + entityID + "_" + pcID _, err := db.Exec( `INSERT INTO pc_locations(id, entity_type, entity_id, pc_id, dir_path, status) VALUES (?,?,?,?,?,?)`, id, entityType, entityID, pcID, dirPath, status, ) if err != nil { t.Fatalf("insert location: %v", err) } } func insertApp(t *testing.T, root, id, dirPath string) { t.Helper() db, _ := sql.Open("sqlite3", filepath.Join(root, "registry.db")) defer db.Close() _, err := db.Exec(`INSERT INTO apps(id, dir_path) VALUES (?,?)`, id, dirPath) if err != nil { t.Fatalf("insert app: %v", err) } } func TestPcLocationsDrift_DetectsMissingFolder(t *testing.T) { root, cleanup := setupTestRegistry(t) defer cleanup() // An active entry whose folder does NOT exist on disk insertLocation(t, root, "app", "my_app", "apps/my_app", "active", "test-pc") drifts, err := PcLocationsDrift(root, "test-pc") if err != nil { t.Fatalf("unexpected error: %v", err) } found := false for _, d := range drifts { if d.EntityID == "my_app" && d.Issue == "missing_on_disk" { found = true } } if !found { t.Errorf("expected missing_on_disk drift for my_app, got: %+v", drifts) } } func TestPcLocationsDrift_ActiveFolderExistsNoDrift(t *testing.T) { root, cleanup := setupTestRegistry(t) defer cleanup() // Create the folder appDir := filepath.Join(root, "apps", "good_app") if err := os.MkdirAll(appDir, 0755); err != nil { t.Fatal(err) } insertLocation(t, root, "app", "good_app", "apps/good_app", "active", "test-pc") drifts, err := PcLocationsDrift(root, "test-pc") if err != nil { t.Fatalf("unexpected error: %v", err) } for _, d := range drifts { if d.EntityID == "good_app" { t.Errorf("unexpected drift for good_app: %+v", d) } } } func TestPcLocationsDrift_StatusShouldBeActive(t *testing.T) { root, cleanup := setupTestRegistry(t) defer cleanup() // Folder exists but status is "missing" appDir := filepath.Join(root, "apps", "came_back") if err := os.MkdirAll(appDir, 0755); err != nil { t.Fatal(err) } insertLocation(t, root, "app", "came_back", "apps/came_back", "missing", "test-pc") drifts, err := PcLocationsDrift(root, "test-pc") if err != nil { t.Fatalf("unexpected error: %v", err) } found := false for _, d := range drifts { if d.EntityID == "came_back" && d.Issue == "status_should_be_active" { found = true } } if !found { t.Errorf("expected status_should_be_active for came_back, got: %+v", drifts) } } func TestPcLocationsDrift_UntrackedOnDisk(t *testing.T) { root, cleanup := setupTestRegistry(t) defer cleanup() // App indexed in registry with folder on disk but no pc_locations entry appDir := filepath.Join(root, "apps", "orphan_app") if err := os.MkdirAll(appDir, 0755); err != nil { t.Fatal(err) } insertApp(t, root, "orphan_app", "apps/orphan_app") // No pc_locations entry for test-pc drifts, err := PcLocationsDrift(root, "test-pc") if err != nil { t.Fatalf("unexpected error: %v", err) } found := false for _, d := range drifts { if d.EntityID == "orphan_app" && d.Issue == "untracked_on_disk" { found = true } } if !found { t.Errorf("expected untracked_on_disk for orphan_app, got: %+v", drifts) } }