diff --git a/src/db.py b/src/db.py index 7fca3de..9e3f24f 100644 --- a/src/db.py +++ b/src/db.py @@ -118,13 +118,24 @@ def _ensure_database() -> Path: db_path = Path(os.getenv("DUCKDB_PATH", "./decp.duckdb")) parquet_path = os.getenv("DATA_FILE_PARQUET_PATH", "") lock_path = db_path.with_suffix(".duckdb.lock") + db_exists = db_path.exists() with open(lock_path, "w") as lock_fd: fcntl.flock(lock_fd, fcntl.LOCK_EX) - if should_rebuild(db_path, parquet_path): - build_database(db_path) - else: - logger.debug("Base de données déjà disponible et à jour.") + try: + if should_rebuild(db_path, parquet_path): + build_database(db_path) + else: + logger.debug("Base de données déjà disponible et à jour.") + except Exception as e: + if db_exists: + logger.error( + f"Bootstrap données KO ({e}). " + f"Réutilisation du DuckDB existant : {db_path}" + ) + else: + logger.critical("Aucune base DuckDB et reconstruction impossible.") + raise return db_path diff --git a/tests/test_db.py b/tests/test_db.py index 10e9725..865da05 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -312,3 +312,59 @@ def test_concurrent_build_serialized(tmp_path, monkeypatch): assert errors == [] assert db_path.exists() assert not tmp_path_artifact.exists() + + +def _raise(*args, **kwargs): + raise RuntimeError("boom") + + +def test_ensure_database_reuses_db_when_should_rebuild_raises(tmp_path, monkeypatch): + import src.db as db + + dbf = tmp_path / "decp.duckdb" + dbf.write_bytes(b"existing") + monkeypatch.setenv("DUCKDB_PATH", str(dbf)) + monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://unreachable") + monkeypatch.setattr(db, "should_rebuild", _raise) + result = db._ensure_database() # ne doit pas lever + assert result == dbf + assert dbf.read_bytes() == b"existing" + + +def test_ensure_database_reuses_db_when_build_raises(tmp_path, monkeypatch): + import src.db as db + + dbf = tmp_path / "decp.duckdb" + dbf.write_bytes(b"existing") + monkeypatch.setenv("DUCKDB_PATH", str(dbf)) + monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://unreachable") + monkeypatch.setattr(db, "should_rebuild", lambda *a, **k: True) + monkeypatch.setattr(db, "build_database", _raise) + db._ensure_database() # ne doit pas lever + assert dbf.read_bytes() == b"existing" + + +def test_ensure_database_raises_on_cold_start(tmp_path, monkeypatch): + import src.db as db + + dbf = tmp_path / "decp.duckdb" # n'existe pas + monkeypatch.setenv("DUCKDB_PATH", str(dbf)) + monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://unreachable") + monkeypatch.setattr(db, "should_rebuild", lambda *a, **k: True) + monkeypatch.setattr(db, "build_database", _raise) + with pytest.raises(RuntimeError): + db._ensure_database() + + +def test_ensure_database_builds_when_needed(tmp_path, monkeypatch): + import src.db as db + + dbf = tmp_path / "decp.duckdb" + dbf.write_bytes(b"old") + monkeypatch.setenv("DUCKDB_PATH", str(dbf)) + monkeypatch.setenv("DATA_FILE_PARQUET_PATH", "http://x") + called = {} + monkeypatch.setattr(db, "should_rebuild", lambda *a, **k: True) + monkeypatch.setattr(db, "build_database", lambda p: called.setdefault("built", p)) + db._ensure_database() + assert called.get("built") == dbf