| """ |
| Regression tests for shared database path and schema helpers. |
| """ |
|
|
| import os |
| import sys |
|
|
| |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
|
|
| import db_utils |
| import runtime_paths |
| import settings |
|
|
|
|
| def test_runtime_db_paths_follow_shared_directory_overrides(tmp_path, monkeypatch): |
| data_root = tmp_path / "data-root" |
| db_root = tmp_path / "db-root" |
| custom_settings = tmp_path / "custom" / "settings.sqlite" |
|
|
| monkeypatch.setenv("SEARCH_UI_DATA_ROOT", str(data_root)) |
| monkeypatch.delenv("SEARCH_UI_DB_DIR", raising=False) |
| monkeypatch.delenv("SEARCH_UI_SEARCH_DB_PATH", raising=False) |
| monkeypatch.delenv("SEARCH_UI_IMAGE_DB_PATH", raising=False) |
| monkeypatch.delenv("SEARCH_UI_FACE_DB_PATH", raising=False) |
| monkeypatch.delenv("SEARCH_UI_SPEAKER_DB_PATH", raising=False) |
| monkeypatch.delenv("SEARCH_UI_SETTINGS_DB_PATH", raising=False) |
|
|
| assert runtime_paths.get_db_dir() == str(data_root) |
| assert runtime_paths.get_search_db_path() == str(data_root / "database.db") |
| assert runtime_paths.get_image_db_path() == str(data_root / "images_database.db") |
| assert runtime_paths.get_face_db_path() == str(data_root / "faces_database.db") |
| assert runtime_paths.get_speaker_db_path() == str(data_root / "speakers_database.db") |
| assert runtime_paths.get_settings_db_path() == str(data_root / "settings.db") |
| assert runtime_paths.get_publications_db_path() == str(data_root / "publications_database.db") |
| assert runtime_paths.get_json_dir() == str(data_root / "json") |
| assert runtime_paths.get_subtitles_dir() == str(data_root / "subtitles") |
| assert runtime_paths.get_videos_dir() == str(data_root / "videos") |
| assert runtime_paths.get_publications_dir() == str(data_root / "publications") |
| assert runtime_paths.get_logs_dir() == str(data_root / "logs") |
| assert runtime_paths.get_checkpoints_dir() == str(data_root / "checkpoints") |
|
|
| monkeypatch.setenv("SEARCH_UI_DB_DIR", str(db_root)) |
| monkeypatch.setenv("SEARCH_UI_SETTINGS_DB_PATH", str(custom_settings)) |
|
|
| assert runtime_paths.get_db_dir() == str(db_root) |
| assert runtime_paths.get_search_db_path() == str(db_root / "database.db") |
| assert runtime_paths.get_settings_db_path() == str(custom_settings) |
|
|
|
|
| def test_connect_sqlite_creates_parent_dir_and_stores_schema_metadata(tmp_path): |
| db_path = tmp_path / "nested" / "app.db" |
|
|
| conn = db_utils.connect_sqlite(str(db_path)) |
| db_utils.register_schema_metadata( |
| conn, |
| db_name="unit_test_db", |
| schema_version=7, |
| embedding_type="binary", |
| embedding_model="test-model-v1", |
| embedding_recipe={"recipe_version": 1, "model_name": "test-model-v1"}, |
| ) |
| conn.commit() |
|
|
| metadata = db_utils.fetch_schema_metadata(conn, "unit_test_db") |
| conn.close() |
|
|
| assert db_path.exists() |
| assert metadata == { |
| "db_name": "unit_test_db", |
| "schema_version": 7, |
| "embedding_type": "binary", |
| "embedding_model": "test-model-v1", |
| "embedding_recipe": {"recipe_version": 1, "model_name": "test-model-v1"}, |
| "created_at": metadata["created_at"], |
| } |
| assert metadata["created_at"] |
|
|
|
|
| def test_register_schema_metadata_migrates_existing_table_for_recipe(tmp_path): |
| db_path = tmp_path / "legacy.db" |
| conn = db_utils.connect_sqlite(str(db_path)) |
| conn.execute( |
| """ |
| CREATE TABLE _schema_metadata ( |
| db_name TEXT PRIMARY KEY, |
| schema_version INTEGER NOT NULL DEFAULT 1, |
| embedding_type TEXT, |
| embedding_model TEXT, |
| created_at TEXT |
| ) |
| """ |
| ) |
| conn.commit() |
|
|
| db_utils.register_schema_metadata( |
| conn, |
| db_name="legacy_db", |
| schema_version=2, |
| embedding_type="binary", |
| embedding_model="legacy-model", |
| embedding_recipe={"recipe_version": 9}, |
| ) |
| conn.commit() |
|
|
| metadata = db_utils.fetch_schema_metadata(conn, "legacy_db") |
| columns = { |
| row[1] |
| for row in conn.execute("PRAGMA table_info(_schema_metadata)").fetchall() |
| } |
| conn.close() |
|
|
| assert "embedding_recipe" in columns |
| assert metadata["embedding_recipe"] == {"recipe_version": 9} |
|
|
|
|
| def test_settings_init_db_tracks_initialized_paths_per_database(tmp_path, monkeypatch): |
| first_path = tmp_path / "settings-a" / "settings.db" |
| second_path = tmp_path / "settings-b" / "settings.db" |
|
|
| monkeypatch.setattr(settings, "_initialized_db_paths", set()) |
| monkeypatch.setenv("SEARCH_UI_SETTINGS_DB_PATH", str(first_path)) |
| settings.init_settings_db() |
|
|
| monkeypatch.setenv("SEARCH_UI_SETTINGS_DB_PATH", str(second_path)) |
| settings.init_settings_db() |
|
|
| assert first_path.exists() |
| assert second_path.exists() |
|
|
| first_conn = db_utils.connect_sqlite(str(first_path)) |
| second_conn = db_utils.connect_sqlite(str(second_path)) |
| try: |
| first_tables = { |
| row[0] |
| for row in first_conn.execute( |
| "SELECT name FROM sqlite_master WHERE type = 'table'" |
| ).fetchall() |
| } |
| second_tables = { |
| row[0] |
| for row in second_conn.execute( |
| "SELECT name FROM sqlite_master WHERE type = 'table'" |
| ).fetchall() |
| } |
| finally: |
| first_conn.close() |
| second_conn.close() |
|
|
| assert "global_settings" in first_tables |
| assert "global_settings" in second_tables |
| assert settings._initialized_db_paths == {str(first_path), str(second_path)} |
|
|