diff --git a/backend/tests/test_docker.py b/backend/tests/test_docker.py index 48b040b..29221be 100644 --- a/backend/tests/test_docker.py +++ b/backend/tests/test_docker.py @@ -117,8 +117,15 @@ def test_compose_declares_required_services(): def test_compose_declares_required_secrets_and_volumes(): compose = yaml.safe_load(COMPOSE_FILE.read_text()) secrets = compose.get("secrets", {}) - for required in ("cyclone_db_key", "cyclone_admin_password"): + for required in ("cyclone_db_key", "cyclone_admin_password", "cyclone_sftp_password"): assert required in secrets, f"compose must declare secret {required!r}" + # SP26: the SFTP secret must point at the same /etc/cyclone/secrets/ tree + # the host operator manages. Catch typos in the file path here so a + # rename breaks the test, not a production deploy. + assert ( + secrets["cyclone_sftp_password"]["file"] + == "/etc/cyclone/secrets/sftp_password" + ) volumes = compose.get("volumes", {}) for required in ( "cyclone_db", @@ -142,6 +149,22 @@ def test_compose_backend_wires_backup_autostart(): assert "CYCLONE_BACKUP_RETENTION_DAYS" in env +def test_compose_backend_wires_sftp_password_file_env_var(): + """SP26 — the backend must wire CYCLONE_SFTP_PASSWORD_FILE to the + mounted Docker secret so the MFT password resolves from + /run/secrets/cyclone_sftp_password without an env-var export.""" + compose = yaml.safe_load(COMPOSE_FILE.read_text()) + backend = compose["services"]["backend"] + env = backend.get("environment", {}) + assert env.get("CYCLONE_SFTP_PASSWORD_FILE") == "/run/secrets/cyclone_sftp_password", ( + "backend must wire CYCLONE_SFTP_PASSWORD_FILE to the Docker secret mount" + ) + backend_secrets = backend.get("secrets", []) + assert any( + _volume_source(s) == "cyclone_sftp_password" for s in backend_secrets + ), "backend must reference the cyclone_sftp_password Docker secret" + + @pytest.mark.skipif( not _has_docker(), reason="docker not on PATH; skipping Dockerfile parse check" )