diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-09 20:33:31 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-09 20:33:31 +0300 |
| commit | 914092f4d519aa7107439d48b882d149ddff1118 (patch) | |
| tree | 1b7fc3963d7f80579c4ea39da6e6781e726527ed | |
| parent | cbaa2bf0137790c525c888e4e6363833a1d7cbc7 (diff) | |
t1: remove repository migration shim
| -rw-r--r-- | internal/repository/schema.go (renamed from internal/repository/migrate.go) | 26 | ||||
| -rw-r--r-- | internal/repository/sqlite.go | 6 | ||||
| -rw-r--r-- | internal/repository/sqlite_test.go | 70 |
3 files changed, 74 insertions, 28 deletions
diff --git a/internal/repository/migrate.go b/internal/repository/schema.go index 3c4702f..9ab8284 100644 --- a/internal/repository/migrate.go +++ b/internal/repository/schema.go @@ -5,7 +5,7 @@ import ( "fmt" ) -// tablesSchema defines all CREATE TABLE statements. +// tablesSchema defines all CREATE TABLE statements for a fresh database. const tablesSchema = ` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -196,34 +196,14 @@ func enableForeignKeys(db *sql.DB) error { return nil } -// addPodcastColumn ensures the sets table has the is_podcast column. -// It safely ignores the error if the column already exists. -func addPodcastColumn(db *sql.DB) error { - _, err := db.Exec(`ALTER TABLE sets ADD COLUMN is_podcast INTEGER NOT NULL DEFAULT 0;`) - if err != nil { - // SQLite returns a generic error message for duplicate columns. - if err.Error() == "duplicate column name: is_podcast" || - err.Error() == "SQL logic error: duplicate column name: is_podcast (1)" { - return nil - } - return fmt.Errorf("add is_podcast column: %w", err) - } - return nil -} - -// Migrate creates the database schema if it does not exist. -func Migrate(db *sql.DB) error { +// initializeSchema creates the database schema for a fresh database. +func initializeSchema(db *sql.DB) error { if err := enableForeignKeys(db); err != nil { return err } if err := execSchema(db, "tables", tablesSchema); err != nil { return err } - // Backward-compatibility: older databases may have a sets table - // without the is_podcast column. Add it before creating the index. - if err := addPodcastColumn(db); err != nil { - return err - } if err := execSchema(db, "indexes", indexesSchema); err != nil { return err } diff --git a/internal/repository/sqlite.go b/internal/repository/sqlite.go index d324f8c..08a902c 100644 --- a/internal/repository/sqlite.go +++ b/internal/repository/sqlite.go @@ -14,10 +14,10 @@ type SQLite struct { db *sql.DB } -// New creates a SQLite store from an existing *sql.DB after migrating the schema. +// New creates a SQLite store from an existing *sql.DB after initializing the schema. func New(db *sql.DB) (*SQLite, error) { - if err := Migrate(db); err != nil { - return nil, fmt.Errorf("migrate: %w", err) + if err := initializeSchema(db); err != nil { + return nil, fmt.Errorf("initialize schema: %w", err) } return &SQLite{db: db}, nil } diff --git a/internal/repository/sqlite_test.go b/internal/repository/sqlite_test.go index fa15a80..eaf142f 100644 --- a/internal/repository/sqlite_test.go +++ b/internal/repository/sqlite_test.go @@ -1115,7 +1115,7 @@ func TestSQLite_OpenFailures(t *testing.T) { } }) - t.Run("closed db migrate failure", func(t *testing.T) { + t.Run("closed db schema initialization failure", func(t *testing.T) { db, err := sql.Open("sqlite", ":memory:") if err != nil { t.Fatalf("open: %v", err) @@ -1123,7 +1123,73 @@ func TestSQLite_OpenFailures(t *testing.T) { db.Close() _, err = New(db) if err == nil { - t.Fatal("expected error when migrating closed db") + t.Fatal("expected error when initializing schema on closed db") + } + }) +} + +func TestSQLite_SchemaInitialization(t *testing.T) { + t.Run("fresh database includes podcast column and foreign keys", func(t *testing.T) { + s := newTestStore(t) + defer s.Close() + + var isPodcastColumn int + rows, err := s.db.Query(`PRAGMA table_info(sets)`) + if err != nil { + t.Fatalf("table info: %v", err) + } + defer rows.Close() + for rows.Next() { + var cid int + var name, typ string + var notNull int + var defaultValue sql.NullString + var pk int + if err := rows.Scan(&cid, &name, &typ, ¬Null, &defaultValue, &pk); err != nil { + t.Fatalf("scan column: %v", err) + } + if name == "is_podcast" { + isPodcastColumn++ + } + } + if err := rows.Err(); err != nil { + t.Fatalf("rows: %v", err) + } + if isPodcastColumn != 1 { + t.Fatalf("expected one is_podcast column, got %d", isPodcastColumn) + } + + var foreignKeys int + if err := s.db.QueryRow(`PRAGMA foreign_keys`).Scan(&foreignKeys); err != nil { + t.Fatalf("foreign_keys pragma: %v", err) + } + if foreignKeys != 1 { + t.Fatalf("expected foreign keys enabled, got %d", foreignKeys) + } + }) + + t.Run("stale pre-podcast sets schema is not upgraded", func(t *testing.T) { + db, err := sql.Open("sqlite", ":memory:") + if err != nil { + t.Fatalf("open: %v", err) + } + defer db.Close() + + _, err = db.Exec(` +CREATE TABLE sets ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + root_path TEXT UNIQUE NOT NULL, + cover_thumbnail_path TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +);`) + if err != nil { + t.Fatalf("create stale schema: %v", err) + } + + _, err = New(db) + if err == nil { + t.Fatal("expected stale schema initialization to fail") } }) } |
