"""Tests for the apply-time unresolved-secret gate. `wildpc apply` must refuse to converge a deployment whose ``${secret:NAME}`` env references the active backend can't resolve — otherwise the value silently degrades to a ```` placeholder and the service starts with a bogus credential (the immich-DB-password-to-the-wrong-backend failure). """ from __future__ import annotations from pathlib import Path from types import SimpleNamespace import pytest from wildpc_core.deploy import _unresolved_secrets def _spec(env: dict[str, str], enabled: bool = True) -> SimpleNamespace: return SimpleNamespace(enabled=enabled, defaults=SimpleNamespace(env=env)) @pytest.fixture def file_backend(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: """Force the active backend to a temp file store with a known secret.""" secrets = tmp_path / "secrets" secrets.mkdir() (secrets / "PRESENT").write_text("value\n") monkeypatch.setenv("WILDPC_SECRET_BACKEND", "file") monkeypatch.setattr("wildpc_core.config.SECRETS_DIR", secrets) return secrets def test_all_resolved_returns_empty(file_backend: Path) -> None: items = [("service", "svc", _spec({"TOKEN": "${secret:PRESENT}"}))] assert _unresolved_secrets(items) == [] def test_missing_secret_is_reported(file_backend: Path) -> None: items = [("service", "svc", _spec({"TOKEN": "${secret:ABSENT}"}))] assert _unresolved_secrets(items) == [("svc", ["ABSENT"])] def test_disabled_deployment_skipped(file_backend: Path) -> None: items = [("service", "svc", _spec({"TOKEN": "${secret:ABSENT}"}, enabled=False))] assert _unresolved_secrets(items) == [] def test_non_secret_placeholders_ignored(file_backend: Path) -> None: items = [("service", "svc", _spec({"PORT": "${port}", "DIR": "${data_dir}"}))] assert _unresolved_secrets(items) == [] def test_composite_value_partial_missing(file_backend: Path) -> None: # A URL mixing a present and an absent secret still flags the absent one. env = {"URL": "postgres://u:${secret:PRESENT}@h/db?k=${secret:ABSENT}"} assert _unresolved_secrets([("service", "svc", _spec(env))]) == [("svc", ["ABSENT"])] def test_multiple_deployments_only_broken_flagged(file_backend: Path) -> None: items = [ ("service", "ok", _spec({"T": "${secret:PRESENT}"})), ("service", "bad", _spec({"T": "${secret:ABSENT}"})), ] assert _unresolved_secrets(items) == [("bad", ["ABSENT"])]