Files
Unraid-AppOps-Gateway-Public/backend/tests/test_portfolio_deployment_enrollment.py
T

157 lines
6.6 KiB
Python

from backend.app.services.action_execution import approval_execution_supported
from backend.app.services.action_registry import ACTION_RISK, ACTIONS, action_default_target
from backend.app.services.portfolio_deployment_enrollment import (
_ENROLL_SCRIPT,
_INSPECT_SCRIPT,
_LEGACY_REVISION,
_LEGACY_SINGLE_PROJECT,
_PROJECT,
_RECOVERY_FAILED_REVISION,
_RECOVERY_FAILURE_MESSAGE,
_RECOVERY_LIVE_REVISION,
_REPOSITORY,
PortfolioDeploymentEnrollment,
)
class _Response:
def __init__(self, *, payload=None, text=""):
self._payload = payload
self.text = text
def json(self):
return self._payload
class _InspectPortainer:
def __init__(self):
self.calls = []
def _request(self, method, path, **kwargs):
self.calls.append((method, path, kwargs))
if path.endswith("/exec"):
return _Response(payload={"Id": "exec-safe-inspect"})
if path.endswith("/start"):
return _Response(
text='{"branch":"main","compose_files":["docker-compose.yml"],"containers":["Portfolio","Portfolio-api"],"present":true}'
)
raise AssertionError(f"Unexpected Portainer path: {path}")
def test_portfolio_enrollment_contract_is_fixed_and_paired():
assert _REPOSITORY == "jens/portfolio"
assert _LEGACY_REVISION == "e59bfb3484531883e5486ee9e10b663ed0f6764f"
assert _PROJECT["branch"] == "main"
assert _PROJECT["exact_source"] is True
assert _PROJECT["env_source"] == "/srv/example/appdata/Portfolio/.env"
assert _PROJECT["compose_files"] == ["docker-compose.yml"]
assert _PROJECT["compose_project"] == "portfolio"
assert _PROJECT["containers"] == ["Portfolio", "Portfolio-api"]
assert _PROJECT["verify_oci_revision"] is False
assert _PROJECT["environment"] == {
"GIT_COMMIT": "{revision}",
"BUILD_ID": "build-{short_revision}",
}
def test_portfolio_enrollment_registry_alias_matches_controller_cache_bind_source():
assert 'registry_path = Path("/controller-config/projects.json")' in _ENROLL_SCRIPT
assert (
'host_registry_path = Path("/controller-hub-releases/controller-config/projects.json")'
in _ENROLL_SCRIPT
)
assert "os.path.samefile(registry_path, host_registry_path)" in _ENROLL_SCRIPT
assert '/srv/example/appdata/gitea-deploy-runners/deploy.lock' not in _ENROLL_SCRIPT
assert '/mnt/cache/appdata/gitea-deploy-runners/deploy.lock' in _ENROLL_SCRIPT
def test_portfolio_enrollment_uses_project_then_global_lock_order_without_child_deadlock():
project_lock = "fcntl.flock(project_lock, fcntl.LOCK_EX)"
cache_lock = "fcntl.flock(lock, fcntl.LOCK_EX)"
assert _ENROLL_SCRIPT.index(project_lock) < _ENROLL_SCRIPT.index(cache_lock)
assert "controller.bootstrap_legacy_state(repository, project, legacy_revision)" in _ENROLL_SCRIPT
assert '["python3", "/opt/gitea-deploy/deploy.py", "bootstrap-legacy"' not in _ENROLL_SCRIPT
def test_portfolio_enrollment_only_migrates_known_legacy_single_target():
assert _LEGACY_SINGLE_PROJECT["containers"] == ["portfolio"]
assert _LEGACY_SINGLE_PROJECT["compose_project"] == "portfolio"
assert _LEGACY_SINGLE_PROJECT["compose_files"] == ["docker-compose.yml"]
assert "env_source" not in _LEGACY_SINGLE_PROJECT
def test_portfolio_target_registration_is_a_typed_high_risk_action():
action = "deployment.target.register_portfolio"
assert action in ACTIONS
assert ACTION_RISK[action] == "high"
assert action_default_target(action) == "jens/portfolio"
assert approval_execution_supported(action) is True
def test_portfolio_target_inspection_returns_only_sanitized_json_from_controller():
portainer = _InspectPortainer()
service = PortfolioDeploymentEnrollment(portainer=portainer)
result = service._inspect_current_target(endpoint="1", container_id="controller-id")
assert result == {
"branch": "main",
"compose_files": ["docker-compose.yml"],
"containers": ["Portfolio", "Portfolio-api"],
"present": True,
}
assert portainer.calls[0][2]["json"]["AttachStdout"] is True
assert portainer.calls[0][2]["json"]["Tty"] is True
assert portainer.calls[1][2]["json"] == {"Detach": False, "Tty": True}
def test_portfolio_target_inspection_exposes_only_bounded_state_diagnostics():
assert (
'state_path = Path("/mnt/cache/appdata/gitea-deploy-runners/state/portfolio.json")'
in _INSPECT_SCRIPT
)
for allowed in (
'"schema_version"',
'"live_revision"',
'"live_status"',
'"live_legacy_provenance"',
'"last_attempt"',
'"live_artifact_containers"',
):
assert allowed in _INSPECT_SCRIPT
assert 'safe_attempt["message"]' in _INSPECT_SCRIPT
assert "[:768]" in _INSPECT_SCRIPT
assert 'state.get("live_receipt")' not in _INSPECT_SCRIPT
assert 'state.get("rollback_snapshot")' not in _INSPECT_SCRIPT
def test_portfolio_runtime_reconciliation_is_pinned_to_observed_failure():
assert _RECOVERY_LIVE_REVISION == "52c6f2fc74a1e39ad288f1c5e987a9cd5b072d3e"
assert _RECOVERY_FAILED_REVISION == "b7955d90b63860ffff8e0bd84be123aaba632043"
assert (
_RECOVERY_FAILURE_MESSAGE
== "Existing production containers require a valid healthy live deployment state"
)
assert 'existing_state.get("live_status") == "unknown"' in _ENROLL_SCRIPT
assert 'existing_state.get("live_legacy_provenance") is False' in _ENROLL_SCRIPT
assert 'last_attempt.get("revision") == recovery_failed_revision' in _ENROLL_SCRIPT
assert 'last_attempt.get("message") == recovery_failure_message' in _ENROLL_SCRIPT
assert 'isinstance(live_receipt, dict)' in _ENROLL_SCRIPT
assert 'bool(live_receipt)' in _ENROLL_SCRIPT
assert 'set(artifact_containers) == set(project["containers"])' in _ENROLL_SCRIPT
def test_portfolio_runtime_reconciliation_proves_exact_recorded_artifacts():
assert 'revision_label != recovery_live_revision' in _ENROLL_SCRIPT
assert 'build_label != f"build-{recovery_live_revision[:12]}"' in _ENROLL_SCRIPT
assert 'artifact.get("image_id")' in _ENROLL_SCRIPT
assert 'artifact.get("container_id")' in _ENROLL_SCRIPT
assert 'str(container.get("Image") or "")' in _ENROLL_SCRIPT
assert 'str(container.get("Id") or "")' in _ENROLL_SCRIPT
assert 'reconciled_state = dict(existing_state)' in _ENROLL_SCRIPT
assert 'reconciled_state["live_status"] = "healthy"' in _ENROLL_SCRIPT
assert 'reconciled_state["status"] = "success"' in _ENROLL_SCRIPT
assert 'bool(verify.get("live_receipt"))' in _ENROLL_SCRIPT
assert "runtime-state-reconciled" in _ENROLL_SCRIPT