from __future__ import annotations import pytest from backend.app.services.enrollment_profiles import PROFILE_VERSION from backend.app.services.repopulse_legacy_adoption_protocol import ( APPLICATION_ID, CONTROLLER_RECEIPT_VERSION, EXECUTION_ENABLED, LEGACY_VOLUME, REPOSITORY, AdoptionProtocolError, advance_intent, complete_intent, create_intent, execution_status, intent_binding_sha256, mark_recovery_required, reserve_or_replay, resume_intent, ) def _sha(value: str) -> str: return value * 64 def _passing_preflight() -> dict: checks = [ { "code": code, "status": "passed", "gate": "legacy_compatibility", "detail": "sanitized fixed check", } for code in ( "legacy_registry", "controller_state", "gitea_ancestry", "runtime_identity_and_health", "legacy_storage_topology", "legacy_appdata", "product_api", "database_health", "queue_quiescence", "database_baseline", ) ] return { "apiVersion": "itworx.repopulse-legacy-adoption-preflight/v1", "repository": REPOSITORY, "status": "preflight_passed", "legacyCompatibility": {"status": "passed", "checks": checks}, "targetAcceptance": {"status": "blocked", "checks": []}, "readOnly": True, "adoptionAuthorized": False, "evidence": { "main_revision": "a" * 40, "live_revision": "b" * 40, "container_id_sha256": _sha("d"), "container_image_sha256": _sha("1"), "container_state": "running", "container_health": "healthy", "queue": {"queued": 0, "active": 0, "completed": 569, "failed": 0}, "database": { "database_bytes": 44_576_768, "journal_bytes": 0, "scan_count": 572, "compacted_scan_count": 96, }, }, } def _intent() -> dict: return create_intent(_passing_preflight(), "repopulse-adoption-2026") def _target_gates() -> dict: return { "profileId": "repopulse-authenticated", "profileVersion": PROFILE_VERSION, "planSha256": _sha("c"), "profileReady": True, "controllerTrustReady": True, "controllerBootstrapSecretImported": True, "typedEnrollmentTrustReady": True, "targetSecretReady": True, "exactPolicyReady": True, "authenticatedRouteReady": True, "accessListReady": True, "mutationBoundaryReady": True, "managedRuntimeReady": True, "scanSourceMatches": True, "scanMountReadOnly": True, "scanPermissionPolicyPersistent": True, "scanAccessForRuntimeIdentityReady": True, "ownedDataTargetReady": True, "managedEnvironmentReady": True, "legacyEnvironmentRetained": True, "maintenanceAuthReady": True, "controllerAdoptionCapabilityReady": True, "controllerRegistryWriterReady": True, "controllerSecretWriterReady": True, } def _quiescence(intent: dict) -> dict: return { "intentId": intent["intentId"], "globalDeployLockHeld": True, "projectLockHeld": True, "maintenanceModeActive": True, "queueFrozen": True, "queuedJobs": 0, "activeJobs": 0, "maintenanceModeAuthenticated": True, "containerName": "repopulse", "containerIdSha256": _sha("d"), "containerImageSha256": _sha("1"), "containerState": "exited", "legacyVolume": LEGACY_VOLUME, "controllerIntentDurable": True, "controllerIntentBindingSha256": intent_binding_sha256(intent), } def _snapshot(intent: dict) -> dict: return { "intentId": intent["intentId"], "globalDeployLockHeld": True, "projectLockHeld": True, "legacyContainerStopped": True, "legacyVolume": LEGACY_VOLUME, "legacyVolumeRetained": True, "sqliteBackupMethod": "sqlite_online_backup", "quickCheck": "ok", "integrityCheck": "ok", "sourceLogicalSha256": _sha("e"), "snapshotLogicalSha256": _sha("e"), "snapshotFileSha256": _sha("f"), "sourceScanCount": 572, "snapshotScanCount": 572, "sourceCompactedScanCount": 96, "snapshotCompactedScanCount": 96, "sourceQueueSha256": _sha("1"), "snapshotQueueSha256": _sha("1"), "sourceAuxiliaryTreeSha256": _sha("3"), "sourceAuxiliaryFileCount": 42, "targetDataPathWasAbsent": True, "snapshotReceiptDurable": True, "legacyEnvironmentRetained": True, "managedEnvironmentFilePrivate": True, "controllerIntentBindingSha256": intent_binding_sha256(intent), } def _registration(intent: dict) -> dict: gates = intent["evidence"]["targetGates"] snapshot = intent["evidence"]["snapshot"] return { "receiptVersion": CONTROLLER_RECEIPT_VERSION, "intentId": intent["intentId"], "receiptSha256": _sha("2"), "repository": REPOSITORY, "applicationId": APPLICATION_ID, "enrollmentId": "enr-0123456789abcdefabcd", "profileId": gates["profileId"], "planSha256": gates["planSha256"], "snapshotFileSha256": snapshot["snapshotFileSha256"], "targetLogicalSha256": snapshot["snapshotLogicalSha256"], "targetScanCount": snapshot["snapshotScanCount"], "targetCompactedScanCount": snapshot["snapshotCompactedScanCount"], "targetQueueSha256": snapshot["snapshotQueueSha256"], "targetAuxiliaryTreeSha256": snapshot["sourceAuxiliaryTreeSha256"], "targetAuxiliaryFileCount": snapshot["sourceAuxiliaryFileCount"], "targetQueuedJobs": 0, "targetActiveJobs": 0, "targetQuickCheck": "ok", "targetIntegrityCheck": "ok", "targetRecoveryStatus": "healthy", "targetDataUid": 10001, "targetDataGid": 10001, "managedDataMountMatches": True, "copySourceIsVerifiedSnapshot": True, "registryTargetMatches": True, "controllerStateReceiptVerified": True, "deploymentReceiptSchema": "gitea-deploy-receipt.v1", "deploymentReceiptSha256": _sha("4"), "deploymentReceiptVerified": True, "runtimeHealthy": True, "publishedHostPorts": [], "globalDeployLockHeld": True, "projectLockHeld": True, "legacyVolume": LEGACY_VOLUME, "legacyVolumeRetained": True, "legacyEnvironmentRetained": True, "managedEnvironmentFilePrivate": True, "scanSourceMatches": True, "scanMountReadOnly": True, "scanPermissionPolicyPersistent": True, "scanAccessForRuntimeIdentityReady": True, "controllerIntentDurable": True, "controllerIntentBindingSha256": intent_binding_sha256(intent), } def _to_snapshot_verified() -> dict: intent = advance_intent(_intent(), "target_gates", _target_gates()) intent = advance_intent(intent, "quiescence", _quiescence(intent)) return advance_intent(intent, "snapshot", _snapshot(intent)) def _to_target_registered() -> dict: intent = _to_snapshot_verified() return advance_intent(intent, "controller_registration", _registration(intent)) def test_passing_legacy_preflight_can_reserve_but_never_authorizes_adoption() -> None: intent = _intent() assert intent["repository"] == REPOSITORY assert intent["applicationId"] == APPLICATION_ID assert intent["legacyVolume"] == LEGACY_VOLUME assert intent["phase"] == "reserved" assert intent["status"] == "in_progress" assert "adoptionAuthorized" not in intent assert intent["evidence"]["preflight"]["queue"]["queued"] == 0 def test_reservation_is_idempotent_and_binds_the_preflight_facts() -> None: first = create_intent(_passing_preflight(), "repopulse-adoption-2026") replay = reserve_or_replay(first, _passing_preflight(), "repopulse-adoption-2026") changed = _passing_preflight() changed["evidence"]["main_revision"] = "c" * 40 assert replay == first with pytest.raises(AdoptionProtocolError, match="idempotency_conflict"): reserve_or_replay(first, changed, "repopulse-adoption-2026") with pytest.raises(AdoptionProtocolError, match="one_time_adoption_already_reserved"): reserve_or_replay(first, _passing_preflight(), "repopulse-adoption-2027") assert intent_binding_sha256(first) == intent_binding_sha256(replay) def test_modified_persisted_intent_evidence_fails_integrity_check() -> None: intent = _intent() intent["evidence"]["preflight"]["repository"] = "jens/other" with pytest.raises(AdoptionProtocolError, match="intent_evidence_invalid"): advance_intent(intent, "target_gates", _target_gates()) @pytest.mark.parametrize( "mutate,code", [ (lambda report: report.update(repository="jens/other"), "legacy_preflight_not_compatible"), (lambda report: report.update(adoptionAuthorized=True), "legacy_preflight_not_compatible"), (lambda report: report.update(status="blocked"), "legacy_preflight_not_compatible"), (lambda report: report["evidence"]["queue"].update(active=1), "legacy_preflight_queue_not_quiescent"), (lambda report: report["legacyCompatibility"]["checks"].pop(), "legacy_preflight_check_incomplete"), ], ) def test_invalid_or_tampered_preflight_cannot_reserve(mutate, code: str) -> None: report = _passing_preflight() mutate(report) with pytest.raises(AdoptionProtocolError) as error: create_intent(report, "repopulse-adoption-2026") assert error.value.code == code def test_changed_or_unready_profile_trust_secret_acl_or_runtime_gate_blocks() -> None: intent = _intent() gates = _target_gates() gates["accessListReady"] = False with pytest.raises(AdoptionProtocolError, match="target_gates_not_ready"): advance_intent(intent, "target_gates", gates) with pytest.raises(AdoptionProtocolError, match="adoption_step_out_of_order"): advance_intent(intent, "quiescence", _quiescence(intent)) @pytest.mark.parametrize( ("field", "value"), [("profileId", "internal-webapp"), ("profileVersion", "unreviewed/v9")], ) def test_target_gates_require_the_exact_managed_profile(field: str, value: str) -> None: gates = _target_gates() gates[field] = value with pytest.raises(AdoptionProtocolError, match="target_gates_not_ready"): advance_intent(_intent(), "target_gates", gates) def test_quiescence_requires_locks_maintenance_drain_stop_and_the_named_volume() -> None: intent = advance_intent(_intent(), "target_gates", _target_gates()) evidence = _quiescence(intent) evidence["globalDeployLockHeld"] = False with pytest.raises(AdoptionProtocolError, match="quiescence_not_proven"): advance_intent(intent, "quiescence", evidence) evidence = _quiescence(intent) evidence["activeJobs"] = 1 with pytest.raises(AdoptionProtocolError, match="quiescence_not_proven"): advance_intent(intent, "quiescence", evidence) evidence = _quiescence(intent) evidence["controllerIntentBindingSha256"] = _sha("9") with pytest.raises(AdoptionProtocolError, match="controller_intent_binding_mismatch"): advance_intent(intent, "quiescence", evidence) evidence = _quiescence(intent) evidence["containerIdSha256"] = _sha("9") with pytest.raises(AdoptionProtocolError, match="quiescence_not_proven"): advance_intent(intent, "quiescence", evidence) def test_quiesced_snapshot_requires_sqlite_online_backup_and_matching_logical_data() -> None: intent = advance_intent(_intent(), "target_gates", _target_gates()) intent = advance_intent(intent, "quiescence", _quiescence(intent)) evidence = _snapshot(intent) evidence["sqliteBackupMethod"] = "raw_file_copy" with pytest.raises(AdoptionProtocolError, match="sqlite_snapshot_not_verified"): advance_intent(intent, "snapshot", evidence) evidence = _snapshot(intent) evidence["sourceLogicalSha256"] = _sha("a") with pytest.raises(AdoptionProtocolError, match="sqlite_snapshot_not_verified"): advance_intent(intent, "snapshot", evidence) def test_controller_receipt_must_bind_exact_target_keep_legacy_volume_and_have_no_ports() -> None: intent = _to_snapshot_verified() evidence = _registration(intent) evidence["publishedHostPorts"] = [{"ip": "0.0.0.0", "port": 1254}] with pytest.raises(AdoptionProtocolError, match="controller_adoption_receipt_not_verified"): advance_intent(intent, "controller_registration", evidence) evidence = _registration(intent) evidence["legacyVolumeRetained"] = False with pytest.raises(AdoptionProtocolError, match="controller_adoption_receipt_not_verified"): advance_intent(intent, "controller_registration", evidence) evidence = _registration(intent) evidence["targetDataUid"] = 0 with pytest.raises(AdoptionProtocolError, match="controller_adoption_receipt_not_verified"): advance_intent(intent, "controller_registration", evidence) evidence = _registration(intent) evidence["targetActiveJobs"] = 1 with pytest.raises(AdoptionProtocolError, match="controller_adoption_receipt_not_verified"): advance_intent(intent, "controller_registration", evidence) for field, invalid in ( ("targetAuxiliaryTreeSha256", _sha("5")), ("targetAuxiliaryFileCount", 41), ("deploymentReceiptSchema", "unknown"), ("deploymentReceiptSha256", "missing"), ("deploymentReceiptVerified", False), ): evidence = _registration(intent) evidence[field] = invalid with pytest.raises(AdoptionProtocolError, match="controller_adoption_receipt_not_verified"): advance_intent(intent, "controller_registration", evidence) def test_exact_controller_and_appops_ownership_are_separate_checkpoints() -> None: intent = _to_target_registered() registration = intent["evidence"]["controllerRegistration"] gates = intent["evidence"]["targetGates"] evidence = { "repository": REPOSITORY, "applicationId": APPLICATION_ID, "enrollmentId": registration["enrollmentId"], "profileId": registration["profileId"], "planSha256": gates["planSha256"], "controllerReceiptSha256": registration["receiptSha256"], "enrollmentPersisted": True, "exactApplyPolicyPersisted": True, "exactRollbackPolicyPersisted": True, "secretReferenceReady": True, } appops_owned = advance_intent(intent, "appops_ownership", evidence) complete = complete_intent( appops_owned, { "intentId": intent["intentId"], "repository": REPOSITORY, "applicationId": APPLICATION_ID, "enrollmentId": registration["enrollmentId"], "controllerReceiptSha256": registration["receiptSha256"], "enrollmentStillPersisted": True, "exactApplyPolicyStillPersisted": True, "exactRollbackPolicyStillPersisted": True, "controllerReceiptStillMatches": True, "legacyVolumeRetained": True, }, ) assert complete["phase"] == "completed" assert complete["status"] == "completed" assert "appopsOwnership" in complete["evidence"] assert "completion" in complete["evidence"] def test_step_replay_is_idempotent_but_changed_evidence_is_a_conflict() -> None: intent = advance_intent(_intent(), "target_gates", _target_gates()) replay = advance_intent(intent, "target_gates", _target_gates()) changed = _target_gates() changed["planSha256"] = _sha("9") assert replay == intent with pytest.raises(AdoptionProtocolError, match="idempotency_conflict"): advance_intent(intent, "target_gates", changed) def test_recovery_marker_keeps_last_good_phase_and_never_deletes_legacy_data() -> None: intent = _to_snapshot_verified() failed = mark_recovery_required( intent, "registry_receipt_uncertain", {"rawError": "SECRET_OR_PATH_MUST_NOT_BE_STORED"}, ) assert failed["status"] == "recovery_required" assert failed["phase"] == "snapshot_verified" assert failed["legacyVolumePreserved"] is True assert "legacyVolumePreserved" not in failed["recovery"] or failed["recovery"]["legacyVolumePreserved"] is True assert "SECRET_OR_PATH_MUST_NOT_BE_STORED" not in str(failed) with pytest.raises(AdoptionProtocolError, match="intent_requires_recovery"): advance_intent(failed, "controller_registration", _registration(intent)) def test_recovery_reconciles_only_exact_controller_committed_registration() -> None: intent = _to_snapshot_verified() failed = mark_recovery_required(intent, "appops_commit_uncertain", {"timeout": True}) registration = _registration(intent) receipt = { "apiVersion": CONTROLLER_RECEIPT_VERSION, "intentId": intent["intentId"], "phase": "target_registered", "receiptSha256": registration["receiptSha256"], "repository": REPOSITORY, "applicationId": APPLICATION_ID, "legacyVolumeRetained": True, "intentDurable": True, "intentBindingSha256": intent_binding_sha256(intent), "registrationEvidence": registration, } resumed = resume_intent(failed, receipt) assert resumed["status"] == "in_progress" assert resumed["phase"] == "target_registered" assert resumed["evidence"]["controllerRegistration"]["receiptSha256"] == registration["receiptSha256"] def test_recovery_replays_registered_target_without_repeating_cutover() -> None: intent = _to_target_registered() failed = mark_recovery_required(intent, "appops_commit_uncertain", {"commit": "timeout"}) registration = intent["evidence"]["controllerRegistration"] receipt = { "apiVersion": CONTROLLER_RECEIPT_VERSION, "intentId": intent["intentId"], "phase": "target_registered", "receiptSha256": registration["receiptSha256"], "repository": REPOSITORY, "applicationId": APPLICATION_ID, "legacyVolumeRetained": True, "intentDurable": True, "intentBindingSha256": intent_binding_sha256(intent), "registrationEvidence": registration, } resumed = resume_intent(failed, receipt) assert resumed["phase"] == "target_registered" assert resumed["status"] == "in_progress" assert resumed["receipts"]["controller_registration"] == intent["receipts"]["controller_registration"] def test_recovery_receipt_for_another_repository_or_intent_is_rejected() -> None: intent = mark_recovery_required(_to_snapshot_verified(), "registry_receipt_uncertain", {}) receipt = { "apiVersion": CONTROLLER_RECEIPT_VERSION, "intentId": "rpa-00000000000000000000", "phase": "target_registered", "receiptSha256": _sha("2"), "repository": "jens/other", "applicationId": APPLICATION_ID, "legacyVolumeRetained": True, "intentDurable": True, "intentBindingSha256": intent_binding_sha256(intent), "registrationEvidence": {}, } with pytest.raises(AdoptionProtocolError, match="controller_recovery_receipt_unbound"): resume_intent(intent, receipt) def test_protocol_activation_reports_enabled_without_claiming_readiness() -> None: assert EXECUTION_ENABLED is True assert execution_status() == { "apiVersion": "itworx.repopulse-legacy-adoption/v1", "repository": REPOSITORY, "status": "enabled", "reason": None, "executionEnabled": True, }