diff --git a/BUILD_DATE b/BUILD_DATE index ce1b9463..928afe90 100644 --- a/BUILD_DATE +++ b/BUILD_DATE @@ -1 +1 @@ -2026-03-09T23:00:57Z +2026-03-10T09:28:47Z diff --git a/CHANGELOG.md b/CHANGELOG.md index 1568c12d..a9dbc67e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +## v0.114.1 (2026-03-10) + +### Bug Fixes + +- **db**: Add migration to create shared_links table for databases that skipped 025 + ([`289dcc3`](https://github.com/christianlouis/DocuElevate/commit/289dcc375c111c8d71bd04ef31f184a0e6a3f6f2)) + + ## v0.114.0 (2026-03-09) ### Bug Fixes diff --git a/GIT_SHA b/GIT_SHA index 082dbf0f..06d2b585 100644 --- a/GIT_SHA +++ b/GIT_SHA @@ -1 +1 @@ -5fd3f06 +70e5391 diff --git a/RUNTIME_INFO b/RUNTIME_INFO index 0d52e60a..81555b83 100644 --- a/RUNTIME_INFO +++ b/RUNTIME_INFO @@ -1,10 +1,10 @@ DocuElevate Build Information ============================== -Version: 0.114.0 -Build Date: 2026-03-09T23:00:57Z -Git Commit: 5fd3f0661b8d86ba6b3f92481675d820aec0d53c -Git Short SHA: 5fd3f06 +Version: 0.114.1 +Build Date: 2026-03-10T09:28:47Z +Git Commit: 70e539164904cb914e3fa13385e67e94e1cf7ec7 +Git Short SHA: 70e5391 Git Branch: main -Commit Date: 2026-03-10T00:00:39+01:00 -Build Timestamp: 2026-03-09T23:00:57Z +Commit Date: 2026-03-10T10:28:28+01:00 +Build Timestamp: 2026-03-10T09:28:47Z ============================== diff --git a/VERSION b/VERSION index 18455b77..aeb6ab15 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.114.0 +0.114.1 diff --git a/tests/test_database.py b/tests/test_database.py index a49dd0a5..14e803c0 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -124,6 +124,85 @@ class TestInitDb: test_engine.dispose() + def test_init_db_creates_shared_links_for_database_missing_table(self, tmp_path): + """Regression test: databases at revision 026 that skipped 025_add_shared_links. + + Migration 025_add_shared_links was inserted into the chain between + 024_add_api_tokens and 025_add_user_notifications after some databases + had already been migrated past that point. Migration 027 creates the + table idempotently so those databases are repaired. + """ + from sqlalchemy import create_engine, text + from sqlalchemy import inspect as sa_inspect + + db_path = str(tmp_path / "regression_shared_links.db") + test_engine = create_engine(f"sqlite:///{db_path}") + + # Set up a database at revision 026 but WITHOUT the shared_links table. + # This simulates a DB that was migrated before 025_add_shared_links + # was inserted into the chain. + with test_engine.begin() as conn: + conn.execute( + text( + "CREATE TABLE files (" + "id INTEGER PRIMARY KEY, filehash VARCHAR NOT NULL, " + "original_filename VARCHAR, local_filename VARCHAR NOT NULL, " + "original_file_path VARCHAR, processed_file_path VARCHAR, " + "file_size INTEGER NOT NULL, mime_type VARCHAR, " + "is_duplicate BOOLEAN DEFAULT 0 NOT NULL, duplicate_of_id INTEGER, " + "ocr_text TEXT, ai_metadata TEXT, document_title VARCHAR, " + "ocr_quality_score INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)" + ) + ) + conn.execute( + text( + "CREATE TABLE processing_logs (" + "id INTEGER PRIMARY KEY, file_id INTEGER, task_id VARCHAR, " + "step_name VARCHAR, status VARCHAR, message VARCHAR, detail TEXT, " + "timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)" + ) + ) + conn.execute( + text( + "CREATE TABLE file_processing_steps (" + "id INTEGER PRIMARY KEY, file_id INTEGER NOT NULL, " + "step_name VARCHAR NOT NULL, status VARCHAR NOT NULL, " + "started_at DATETIME, completed_at DATETIME, error_message TEXT, " + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)" + ) + ) + conn.execute( + text( + "CREATE TABLE saved_searches (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, user_id VARCHAR NOT NULL, " + "name VARCHAR NOT NULL, filters TEXT NOT NULL, " + "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " + "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, " + "UNIQUE (user_id, name))" + ) + ) + conn.execute(text("CREATE TABLE alembic_version (version_num VARCHAR(32) NOT NULL)")) + conn.execute(text("INSERT INTO alembic_version VALUES ('026_add_scheduled_jobs')")) + + with patch("app.database.engine", test_engine), patch("app.database.DB_URL", f"sqlite:///{db_path}"): + init_db() + + inspector = sa_inspect(test_engine) + table_names = inspector.get_table_names() + assert "shared_links" in table_names + + # Verify the shared_links table has the expected columns. + columns = {col["name"] for col in inspector.get_columns("shared_links")} + assert "id" in columns + assert "token" in columns + assert "file_id" in columns + assert "owner_id" in columns + assert "expires_at" in columns + assert "is_active" in columns + + test_engine.dispose() + @pytest.mark.unit class TestGetDb: