SPB Git

spb/api-ka

Public

API-KA — plateforme centrale : collecte quotidienne des 8 services KA, historisation append-only et API publique sur www.api-ka.com

fix: pg_dump en chemin absolu (cron 03:30) + isolation des logs sous pytest

- backup.py : PG_DUMP = shutil.which ou /opt/homebrew/bin/pg_dump — le cron
  de 03:30 (PATH minimal /usr/bin:/bin) échouait en [Errno 2] depuis le 17/08,
  backups ET purge 90 j jamais exécutés. Testé : 8/8 dumps + purge OK sous
  env cron simulé.
- tests/conftest.py : fixture session isolated_logs_dir — redirige logs_dir
  (alerts.log/apika.log) vers le tmp de test ; pytest écrivait de fausses
  alertes [louka] 2026-08-03 dans le vrai logs/alerts.log de prod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 18, 2026) parent aee9aac Browse files

2 changed files +28 −1

modified src/utils/backup.py +7 −1
@@ -29,6 +29,12 @@ from src.database.db import session_scope
29 from src.database.models import DATA_MODELS 29 from src.database.models import DATA_MODELS
30 from src.utils.logger import alert, get_logger 30 from src.utils.logger import alert, get_logger
31 31
32+# Chemin absolu de pg_dump : le cron de 03:30 tourne avec un PATH minimal
33+# (/usr/bin:/bin) qui ne contient pas /opt/homebrew/bin — un appel nu à
34+# "pg_dump" y échoue en [Errno 2] FileNotFoundError. On résout via le PATH
35+# courant quand c'est possible, sinon on retombe sur l'installation Homebrew.
36+PG_DUMP = shutil.which("pg_dump") or "/opt/homebrew/bin/pg_dump"
37+
32 38
33 def _day_dir(date_key: datetime.date) -> Path: 39 def _day_dir(date_key: datetime.date) -> Path:
34 day_dir = get_settings().backups_dir / date_key.isoformat() 40 day_dir = get_settings().backups_dir / date_key.isoformat()
@@ -69,7 +75,7 @@ def backup_service(service: str, date_key: datetime.date | None = None) -> Path:
69 out_path = day_dir / f"{table}_{timestamp}.sql.gz" 75 out_path = day_dir / f"{table}_{timestamp}.sql.gz"
70 result = subprocess.run( 76 result = subprocess.run(
71 [ 77 [
72− "pg_dump", 78+ PG_DUMP,
73 "--dbname", 79 "--dbname",
74 _libpq_url(settings.database_url), 80 _libpq_url(settings.database_url),
75 "--table", 81 "--table",
modified tests/conftest.py +21 −0
@@ -15,8 +15,10 @@ valeurs dès le premier appel.
15 15
16 from __future__ import annotations 16 from __future__ import annotations
17 17
18+import dataclasses
18 import os 19 import os
19 import tempfile 20 import tempfile
21+from pathlib import Path
20 22
21 _TMP_DIR = tempfile.mkdtemp(prefix="apika-tests-") 23 _TMP_DIR = tempfile.mkdtemp(prefix="apika-tests-")
22 os.environ["APP_ENV"] = "test" 24 os.environ["APP_ENV"] = "test"
@@ -49,3 +51,22 @@ def no_backup(monkeypatch: pytest.MonkeyPatch):
49 monkeypatch.setattr( 51 monkeypatch.setattr(
50 "src.collectors.base_collector.backup_service", lambda *args, **kwargs: None 52 "src.collectors.base_collector.backup_service", lambda *args, **kwargs: None
51 ) 53 )
54+
55+
56+@pytest.fixture(scope="session", autouse=True)
57+def isolated_logs_dir():
58+ """Redirige les logs (alerts.log, apika.log) vers le tmp de test.
59+
60+ ``logs_dir`` est codé en dur sur ``BASE_DIR/logs`` dans ``get_settings()`` :
61+ sans cette fixture, les collectes échouées simulées par les tests
62+ (ex. ``test_run_failure_after_retries``) écriraient de fausses alertes
63+ dans le vrai ``logs/alerts.log`` de production du node.
64+ """
65+ test_logs = Path(_TMP_DIR) / "logs"
66+ test_logs.mkdir(parents=True, exist_ok=True)
67+ test_settings = dataclasses.replace(get_settings(), logs_dir=test_logs)
68+ mp = pytest.MonkeyPatch()
69+ # alert() et get_logger() résolvent get_settings via src.utils.logger.
70+ mp.setattr("src.utils.logger.get_settings", lambda: test_settings)
71+ yield test_logs
72+ mp.undo()
52 73
Menu

Theme