package database import ( "context" "database/sql" "testing" _ "modernc.org/sqlite" // SQLite driver registration ) func TestParseMigrationVersion(t *testing.T) { tests := []struct { name string filename string want string wantErr bool }{ { name: "version only", filename: "001.sql", want: "001", }, { name: "version with description", filename: "001_initial_schema.sql", want: "001", }, { name: "multi-digit version", filename: "042_add_indexes.sql", want: "042", }, { name: "long version number", filename: "00001_long_prefix.sql", want: "00001", }, { name: "description with multiple underscores", filename: "003_add_user_auth_tables.sql", want: "003", }, { name: "empty filename", filename: ".sql", wantErr: true, }, { name: "leading underscore", filename: "_description.sql", wantErr: true, }, { name: "non-numeric version", filename: "abc_migration.sql", wantErr: true, }, { name: "mixed alphanumeric version", filename: "001a_migration.sql", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseMigrationVersion(tt.filename) if tt.wantErr { if err == nil { t.Errorf("ParseMigrationVersion(%q) expected error, got %q", tt.filename, got) } return } if err != nil { t.Errorf("ParseMigrationVersion(%q) unexpected error: %v", tt.filename, err) return } if got != tt.want { t.Errorf("ParseMigrationVersion(%q) = %q, want %q", tt.filename, got, tt.want) } }) } } func TestApplyMigrations(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") if err != nil { t.Fatalf("failed to open in-memory database: %v", err) } defer db.Close() // Apply migrations should succeed. if err := ApplyMigrations(db); err != nil { t.Fatalf("ApplyMigrations failed: %v", err) } // Verify the schema_migrations table recorded the version. var version string err = db.QueryRowContext(context.Background(), "SELECT version FROM schema_migrations LIMIT 1", ).Scan(&version) if err != nil { t.Fatalf("failed to query schema_migrations: %v", err) } if version != "001" { t.Errorf("expected version %q, got %q", "001", version) } // Verify a table from the migration exists (source_content). var tableName string err = db.QueryRowContext(context.Background(), "SELECT name FROM sqlite_master WHERE type='table' AND name='source_content'", ).Scan(&tableName) if err != nil { t.Fatalf("expected source_content table to exist: %v", err) } } func TestApplyMigrationsIdempotent(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") if err != nil { t.Fatalf("failed to open in-memory database: %v", err) } defer db.Close() // Apply twice should succeed (idempotent). if err := ApplyMigrations(db); err != nil { t.Fatalf("first ApplyMigrations failed: %v", err) } if err := ApplyMigrations(db); err != nil { t.Fatalf("second ApplyMigrations failed: %v", err) } // Should still have exactly one migration recorded. var count int err = db.QueryRowContext(context.Background(), "SELECT COUNT(*) FROM schema_migrations", ).Scan(&count) if err != nil { t.Fatalf("failed to count schema_migrations: %v", err) } if count != 1 { t.Errorf("expected 1 migration record, got %d", count) } }