"""TDD red tests for S0 health endpoint and startup guards (APP-30, APP-45, SETTINGS-10, SETTINGS-25).""" import importlib import os import tempfile from unittest.mock import patch import pytest from fastapi.testclient import TestClient import backend.db as db_mod def _reload_app(): import backend.app as app_mod return importlib.reload(app_mod) def test_health_happy_path_returns_200_with_three_fields(monkeypatch): """Given: writable db + image dir; When: GET /api/health; Then: 200 + {status, version, woo_reachable}.""" with tempfile.TemporaryDirectory() as tmp: db_path = os.path.join(tmp, "test.db") img_dir = os.path.join(tmp, "images") os.mkdir(img_dir) monkeypatch.setenv("SLAB_DB_PATH", db_path) monkeypatch.setenv("SLAB_IMAGES_DIR", img_dir) monkeypatch.setenv("WOO_BASE_URL", "https://example.com") monkeypatch.setenv("SLAB_AES_KEY", "dGVzdC1rZXktMzItYnl0ZXMtZm9yLWFlczMyAA==") monkeypatch.setenv("LOG_LEVEL", "INFO") from backend.app import app client = TestClient(app) resp = client.get("/api/health") assert resp.status_code == 200 body = resp.json() assert set(body.keys()) == {"status", "version", "woo_reachable"} assert body["status"] == "ok" db_mod._reset_for_tests() def test_health_fails_non_200_when_image_dir_not_writable(monkeypatch): """Given: image dir not writable; When: GET /api/health; Then: non-200 (probes health file per DATA-MODEL ยง1).""" with tempfile.TemporaryDirectory() as tmp: db_path = os.path.join(tmp, "test.db") img_dir = os.path.join(tmp, "images") os.mkdir(img_dir) monkeypatch.setenv("SLAB_DB_PATH", db_path) monkeypatch.setenv("SLAB_IMAGES_DIR", img_dir) monkeypatch.setenv("WOO_BASE_URL", "https://example.com") monkeypatch.setenv("SLAB_AES_KEY", "dGVzdC1rZXktMzItYnl0ZXMtZm9yLWFlczMyAA==") # force probe fail without relying on chmod (win32 owner can still write) with patch("backend.app.probe_images_writable", return_value=False): from backend.app import app client = TestClient(app) resp = client.get("/api/health") assert resp.status_code != 200 db_mod._reset_for_tests() def test_startup_guard_refuses_on_missing_or_non_https_woo_base_url(monkeypatch): """Given: WOO_BASE_URL missing or not https; When: import/create app; Then: raises with clear message (SETTINGS-10).""" monkeypatch.delenv("WOO_BASE_URL", raising=False) with pytest.raises(RuntimeError, match="WOO_BASE_URL"): _reload_app() monkeypatch.setenv("WOO_BASE_URL", "http://insecure.example.com") with pytest.raises(RuntimeError, match="https"): _reload_app() def test_startup_guard_refuses_on_missing_slab_aes_key(monkeypatch): """Given: SLAB_AES_KEY missing; When: import/create app; Then: raises with clear message (SETTINGS-25).""" monkeypatch.setenv("WOO_BASE_URL", "https://example.com") monkeypatch.delenv("SLAB_AES_KEY", raising=False) with pytest.raises(RuntimeError, match="SLAB_AES_KEY"): _reload_app()