fix: add password_hash column migration and unit tests
CI Pipeline / backend-test (push) Successful in 4m5s
CI Pipeline / frontend-test (push) Successful in 27s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 11s
CI Pipeline / backend-test (pull_request) Successful in 4m11s
CI Pipeline / frontend-test (pull_request) Successful in 26s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 8s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s

- Add users.password_hash to _migrate_add_missing_columns pending list
- Skip tables that don't exist yet (prevents crash on fresh DB)
- Handle nullable columns (no DEFAULT clause) in ALTER TABLE
- Fix SQLite column type (use sql_type instead of hardcoded TEXT)
- Update migrate_roles.py to create schema columns/tables before seeding roles
- Add unit tests for migration: adds column, nullable, idempotent, no-op
This commit is contained in:
2026-07-31 08:10:05 +00:00
parent 210da72e03
commit 85b3a4adc4
3 changed files with 213 additions and 8 deletions
+23 -6
View File
@@ -45,6 +45,7 @@ def _migrate_add_missing_columns(sync_conn):
import sqlalchemy as sa
# Column definitions: (table, name, type, default)
# default=None → nullable column (no DEFAULT clause)
pending = [
("station_config", "stream_url", sa.String(500), ""),
("station_config", "stream_metadata_url", sa.String(500), ""),
@@ -75,6 +76,8 @@ def _migrate_add_missing_columns(sync_conn):
("station_config", "color_light", sa.String(7), "#f0ebe3"),
("station_config", "color_medium", sa.String(7), "#8a8580"),
("station_config", "color_black", sa.String(7), "#1a1816"),
# Multi-admin support: password_hash (nullable — existing rows stay NULL)
("users", "password_hash", sa.String(255), None),
]
# Determine dialect
@@ -83,6 +86,10 @@ def _migrate_add_missing_columns(sync_conn):
inspector = sa.inspect(sync_conn)
for table_name, col_name, col_type, default in pending:
# Skip tables that don't exist yet (e.g., station_config on fresh DB)
if not inspector.has_table(table_name):
continue
# Check if column already exists
existing = inspector.get_columns(table_name)
if any(c["name"] == col_name for c in existing):
@@ -99,13 +106,23 @@ def _migrate_add_missing_columns(sync_conn):
sql_type = "TEXT"
if dialect == "sqlite":
sync_conn.execute(
sa.text(f"ALTER TABLE {table_name} ADD COLUMN \"{col_name}\" TEXT NOT NULL DEFAULT '{default}'")
)
if default is None:
sync_conn.execute(
sa.text(f"ALTER TABLE {table_name} ADD COLUMN \"{col_name}\" {sql_type}")
)
else:
sync_conn.execute(
sa.text(f"ALTER TABLE {table_name} ADD COLUMN \"{col_name}\" {sql_type} NOT NULL DEFAULT '{default}'")
)
else:
sync_conn.execute(
sa.text(f"ALTER TABLE {table_name} ADD COLUMN \"{col_name}\" {sql_type} NOT NULL DEFAULT '{default}'")
)
if default is None:
sync_conn.execute(
sa.text(f"ALTER TABLE {table_name} ADD COLUMN \"{col_name}\" {sql_type}")
)
else:
sync_conn.execute(
sa.text(f"ALTER TABLE {table_name} ADD COLUMN \"{col_name}\" {sql_type} NOT NULL DEFAULT '{default}'")
)
print(f" ✓ Added {table_name}.{col_name}")