diff --git a/tests/test_credential_crypto.py b/tests/test_credential_crypto.py index 6274bb5..2ca31b0 100644 --- a/tests/test_credential_crypto.py +++ b/tests/test_credential_crypto.py @@ -69,3 +69,69 @@ def test_missing_key_with_env_var_bootstraps(tmp_path, monkeypatch): key_path = tmp_path / "bootstrap.b64" CredentialCrypto(key_path) # no bootstrap_ok kwarg — relies on env assert key_path.exists() + + +# --- #3422: the install docs quote this module, so they must keep agreeing -- +# +# README.md and .env.example both print the literal failure a new installer +# hits, and the literal path and env var they must act on. That is the right +# call — a stranger greps for the string their terminal showed them — but it +# means those two files now DEPEND on this module's wording, with nothing +# connecting them. The characteristic defect of the install surface is exactly +# this: the documented behaviour and the code drift apart, and the code is the +# one that is right. +# +# Presence checks on both sides, deliberately: an absence check against prose +# would pass for the wrong reason the moment a sentence were reworded +# (snippet #3352). + +_REPO_ROOT = Path(__file__).resolve().parents[1] +_INSTALL_DOCS = ("README.md", ".env.example") + + +def test_the_bootstrap_refusal_still_reads_the_way_the_docs_quote_it(tmp_path, monkeypatch): + """The three things a reader is told to look for, in the raised message.""" + from backend.app.services.credential_crypto import ( + _BOOTSTRAP_ENV_VAR, + MissingCredentialKey, + ) + monkeypatch.delenv(_BOOTSTRAP_ENV_VAR, raising=False) + with pytest.raises(MissingCredentialKey) as exc: + CredentialCrypto(tmp_path / "absent.b64") + message = str(exc.value) + # The sentence README.md reproduces verbatim. + assert "Fernet key file not found at" in message + # The variable both docs tell the operator to set. + assert _BOOTSTRAP_ENV_VAR in message + # The alternative the docs lean on — that a restored instance restores the + # key rather than minting one. Losing this line loses the whole point. + assert "restore the key file" in message + + +def test_the_install_docs_name_the_real_key_path_and_variable(): + """Pins the two literals, read from the code rather than retyped here. + + Changing `_CREDENTIAL_KEY_PATH` or the env var name without updating the + docs fails this — which is the only thing standing between a rename and a + README that sends strangers to a path that does not exist. + """ + from backend.app import _CREDENTIAL_KEY_PATH + from backend.app.services.credential_crypto import _BOOTSTRAP_ENV_VAR + + for name in _INSTALL_DOCS: + text = (_REPO_ROOT / name).read_text() + assert str(_CREDENTIAL_KEY_PATH) in text, f"{name} does not name the key path" + assert _BOOTSTRAP_ENV_VAR in text, f"{name} does not name the bootstrap variable" + + +def test_compose_passes_the_bootstrap_variable_through(): + """The docs say "set it in .env"; that only works if compose forwards it. + + Without this line the instruction is silently inert — the operator sets the + variable, the container never sees it, and the failure is identical to not + having set it at all. + """ + from backend.app.services.credential_crypto import _BOOTSTRAP_ENV_VAR + + compose = (_REPO_ROOT / "docker-compose.yml").read_text() + assert f"{_BOOTSTRAP_ENV_VAR}: ${{{_BOOTSTRAP_ENV_VAR}" in compose