63 lines
2.9 KiB
Python
63 lines
2.9 KiB
Python
from pathlib import Path
|
|
|
|
from backend.app.services.portfolio_deployment_recovery import (
|
|
_FAILED_REVISION,
|
|
_FAILURE_MESSAGE,
|
|
_LIVE_REVISION,
|
|
_REATTEST_SCRIPT,
|
|
_REPOSITORY,
|
|
)
|
|
|
|
|
|
def test_portfolio_recovery_is_pinned_to_exact_observed_incident():
|
|
assert _REPOSITORY == "jens/portfolio"
|
|
assert _LIVE_REVISION == "52c6f2fc74a1e39ad288f1c5e987a9cd5b072d3e"
|
|
assert _FAILED_REVISION == "b7955d90b63860ffff8e0bd84be123aaba632043"
|
|
assert _FAILURE_MESSAGE == "Existing production containers require a valid healthy live deployment state"
|
|
assert 'main.stdout.strip() != failed_revision' in _REATTEST_SCRIPT
|
|
assert 'state.get("live_status") == "unknown"' in _REATTEST_SCRIPT
|
|
assert 'attempt.get("revision") == failed_revision' in _REATTEST_SCRIPT
|
|
assert 'attempt.get("message") == failure_message' in _REATTEST_SCRIPT
|
|
assert '/mnt/cache/appdata/gitea-deploy-runners/state' in _REATTEST_SCRIPT
|
|
assert '/srv/example/appdata/gitea-deploy-runners/deploy.lock' not in _REATTEST_SCRIPT
|
|
assert '/mnt/cache/appdata/gitea-deploy-runners/deploy.lock' in _REATTEST_SCRIPT
|
|
|
|
|
|
def test_portfolio_recovery_reuses_controller_receipt_verifiers():
|
|
assert "controller.validate_recorded_receipt" in _REATTEST_SCRIPT
|
|
assert "controller.verify_deployment" in _REATTEST_SCRIPT
|
|
assert "expected_image_ids=expected_image_ids" in _REATTEST_SCRIPT
|
|
assert "controller.deployment_artifacts" in _REATTEST_SCRIPT
|
|
assert "controller.verified_current_receipt" in _REATTEST_SCRIPT
|
|
|
|
|
|
def test_portfolio_recovery_allows_only_ephemeral_container_identity_change():
|
|
assert 'if key != "container_id"' in _REATTEST_SCRIPT
|
|
assert "container_id_changed = False" in _REATTEST_SCRIPT
|
|
assert "if not container_id_changed" in _REATTEST_SCRIPT
|
|
assert 'artifacts.get("compose_sha256") != observed_artifacts.get("compose_sha256")' in _REATTEST_SCRIPT
|
|
assert 'repaired["live_receipt"] = observed_receipt' in _REATTEST_SCRIPT
|
|
assert 'repaired["live_artifacts"] = observed_artifacts' in _REATTEST_SCRIPT
|
|
|
|
|
|
def test_portfolio_recovery_has_no_application_runtime_mutation_commands():
|
|
forbidden = (
|
|
"docker restart",
|
|
"docker stop",
|
|
"docker rm",
|
|
"docker compose up",
|
|
"docker compose down",
|
|
"subprocess.run([\"docker\", \"restart\"",
|
|
"subprocess.run([\"docker\", \"rm\"",
|
|
)
|
|
assert all(value not in _REATTEST_SCRIPT for value in forbidden)
|
|
assert "shutil.copy2(state_path, backup)" in _REATTEST_SCRIPT
|
|
assert "os.fsync" in _REATTEST_SCRIPT
|
|
|
|
|
|
def test_portfolio_action_execution_accepts_reconciled_status():
|
|
source = Path("backend/app/services/action_execution.py").read_text(encoding="utf-8")
|
|
assert "from .portfolio_deployment_recovery import PortfolioDeploymentRecovery" in source
|
|
assert "PortfolioDeploymentRecovery().run()" in source
|
|
assert 'status in {"enrolled", "already-enrolled", "reconciled"}' in source
|