from __future__ import annotations import hashlib import pytest from sqlalchemy import inspect from sqlalchemy.exc import IntegrityError from sqlmodel import Session, SQLModel, create_engine from backend.app.db.models import RepoPulseAdoptionJournal from backend.app.services import repopulse_adoption_journal as journal from backend.app.services.enrollment_profiles import PROFILE_VERSION from backend.app.services.repopulse_legacy_adoption_protocol import ( APPLICATION_ID, CONTROLLER_RECEIPT_VERSION, LEGACY_VOLUME, PROTOCOL_VERSION, REPOSITORY, advance_intent, create_intent, intent_binding_sha256, ) def _sha(char: str) -> str: return char * 64 def _passing_preflight() -> dict: check_codes = ( "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": [ {"code": code, "status": "passed", "gate": "legacy_compatibility"} for code in check_codes ], }, "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 _target_gates() -> dict: return { "profileId": "repopulse-authenticated", "profileVersion": PROFILE_VERSION, "planSha256": _sha("c"), **{ key: True for key in ( "profileReady", "controllerTrustReady", "controllerBootstrapSecretImported", "typedEnrollmentTrustReady", "targetSecretReady", "exactPolicyReady", "authenticatedRouteReady", "accessListReady", "mutationBoundaryReady", "managedRuntimeReady", "scanSourceMatches", "scanMountReadOnly", "scanPermissionPolicyPersistent", "scanAccessForRuntimeIdentityReady", "ownedDataTargetReady", "managedEnvironmentReady", "legacyEnvironmentRetained", "maintenanceAuthReady", "controllerAdoptionCapabilityReady", "controllerRegistryWriterReady", "controllerSecretWriterReady", ) }, } 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": "itworx.repopulse-legacy-adoption-receipt/v1", "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 _ownership(intent: dict, registration: dict) -> dict: return { "repository": REPOSITORY, "applicationId": APPLICATION_ID, "enrollmentId": registration["enrollmentId"], "profileId": registration["profileId"], "planSha256": intent["evidence"]["targetGates"]["planSha256"], "controllerReceiptSha256": registration["receiptSha256"], "enrollmentPersisted": True, "exactApplyPolicyPersisted": True, "exactRollbackPolicyPersisted": True, "secretReferenceReady": True, } @pytest.fixture def engine(tmp_path): database_engine = create_engine(f"sqlite:///{tmp_path / 'appops.db'}") SQLModel.metadata.create_all(database_engine) yield database_engine database_engine.dispose() def _reserve(engine, key: str = "repopulse-adoption-2026") -> dict: with Session(engine) as session: return journal.reserve_repopulse_adoption(session, _passing_preflight(), key) def test_create_all_registers_singleton_table_and_reservation_survives_restart(engine) -> None: assert inspect(engine).has_table("repopulse_adoption_journal") first = _reserve(engine) assert first["apiVersion"] == PROTOCOL_VERSION assert first["status"] == "in_progress" assert first["phase"] == "reserved" assert first["repository"] == REPOSITORY assert first["applicationId"] == APPLICATION_ID assert first["revision"] == 1 # A new Session models a process restart: the slot and idempotent replay # are read back from SQLite, not held in adapter memory. replay = _reserve(engine) assert replay == first with Session(engine) as session: status = journal.get_repopulse_adoption_status(session) assert status == first def test_changed_preflight_and_different_key_conflict_without_replacing_slot(engine) -> None: first = _reserve(engine) changed = _passing_preflight() changed["evidence"]["live_revision"] = "c" * 40 with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.reserve_repopulse_adoption(session, changed, "repopulse-adoption-2026") assert error.value.code == "idempotency_conflict" with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.reserve_repopulse_adoption( session, _passing_preflight(), "repopulse-adoption-2027" ) assert error.value.code == "one_time_adoption_already_reserved" assert _reserve(engine) == first def test_reservation_rejects_another_repository_identity_without_creating_a_row(engine) -> None: other_repository = _passing_preflight() other_repository["repository"] = "someone-else/repopulse" with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.reserve_repopulse_adoption( session, other_repository, "repopulse-adoption-2026" ) assert error.value.code == "legacy_preflight_not_compatible" with Session(engine) as session: assert journal.get_repopulse_adoption_status(session)["status"] == "unreserved" intent = create_intent(_passing_preflight(), "repopulse-adoption-2026") with Session(engine) as session, pytest.raises(IntegrityError): session.add( RepoPulseAdoptionJournal( slot="other", repository="someone-else/repopulse", application_id="other-app", intent_id=intent["intentId"], idempotency_sha256=intent["idempotencySha256"], preflight_sha256=intent["preflightSha256"], phase=intent["phase"], status=intent["status"], revision=1, intent_json=intent, ) ) session.commit() def test_controller_intent_confirmation_requires_exact_binding_and_replays(engine) -> None: _reserve(engine) with Session(engine) as session: after_gates = journal.advance_repopulse_adoption( session, "target_gates", _target_gates(), expected_revision=1 ) assert after_gates["phase"] == "target_gates_ready" assert after_gates["revision"] == 2 current = create_intent(_passing_preflight(), "repopulse-adoption-2026") valid_confirmation = _quiescence(current) invalid_confirmation = {**valid_confirmation, "controllerIntentBindingSha256": _sha("0")} with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.confirm_repopulse_controller_intent( session, invalid_confirmation, expected_revision=2 ) assert error.value.code == "controller_intent_binding_mismatch" with Session(engine) as session: confirmed = journal.confirm_repopulse_controller_intent( session, valid_confirmation, expected_revision=2 ) assert confirmed["phase"] == "quiesced" assert confirmed["revision"] == 3 with Session(engine) as session: replay = journal.confirm_repopulse_controller_intent( session, valid_confirmation, expected_revision=2 ) assert replay == confirmed def test_compare_and_swap_rejects_stale_checkpoint_without_overwriting_state(engine) -> None: first = _reserve(engine) with Session(engine) as session: updated = journal.advance_repopulse_adoption( session, "target_gates", _target_gates(), expected_revision=1, ) assert updated["status"] == "in_progress" assert updated["revision"] == 2 candidate = create_intent(_passing_preflight(), "repopulse-adoption-2026") valid_confirmation = _quiescence(candidate) with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.confirm_repopulse_controller_intent( session, valid_confirmation, expected_revision=1, ) assert error.value.code == "journal_revision_conflict" with Session(engine) as session: assert journal.get_repopulse_adoption_status(session) == updated assert updated["intentId"] == first["intentId"] def test_recovery_resume_checks_controller_binding_and_persists_across_sessions(engine) -> None: _reserve(engine) with Session(engine) as session: recovery = journal.mark_repopulse_adoption_recovery( session, "operator_interruption", {"operatorNote": "resume review", "path": "private/path"}, expected_revision=1, ) assert "private/path" not in str(recovery) receipt = { "apiVersion": CONTROLLER_RECEIPT_VERSION, "intentId": recovery["intentId"], "phase": "reserved", "receiptSha256": hashlib.sha256(b"durable controller journal row").hexdigest(), "repository": REPOSITORY, "applicationId": APPLICATION_ID, "legacyVolumeRetained": True, "intentDurable": True, "intentBindingSha256": recovery["intentBindingSha256"], "registrationEvidence": None, } invalid_receipt = {**receipt, "intentBindingSha256": _sha("0")} with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.resume_repopulse_adoption(session, invalid_receipt, expected_revision=2) assert error.value.code == "controller_recovery_receipt_unbound" with Session(engine) as session: resumed = journal.resume_repopulse_adoption( session, receipt, expected_revision=2 ) assert resumed["status"] == "in_progress" assert resumed["phase"] == "reserved" assert resumed["revision"] == 3 with Session(engine) as session: assert journal.get_repopulse_adoption_status(session) == resumed def test_controller_registration_reconciles_before_appops_ownership_can_advance(engine) -> None: _reserve(engine) candidate = create_intent(_passing_preflight(), "repopulse-adoption-2026") with Session(engine) as session: current = journal.advance_repopulse_adoption( session, "target_gates", _target_gates(), expected_revision=1 ) candidate = advance_intent(candidate, "target_gates", _target_gates()) with Session(engine) as session: current = journal.confirm_repopulse_controller_intent( session, _quiescence(candidate), expected_revision=2 ) candidate = advance_intent(candidate, "quiescence", _quiescence(candidate)) with Session(engine) as session: current = journal.advance_repopulse_adoption( session, "snapshot", _snapshot(candidate), expected_revision=3 ) candidate = advance_intent(candidate, "snapshot", _snapshot(candidate)) early_ownership = { "repository": REPOSITORY, "applicationId": APPLICATION_ID, "enrollmentId": "enr-0123456789abcdefabcd", "profileId": "repopulse-authenticated", "planSha256": _sha("c"), "controllerReceiptSha256": _sha("2"), "enrollmentPersisted": True, "exactApplyPolicyPersisted": True, "exactRollbackPolicyPersisted": True, "secretReferenceReady": True, } with Session(engine) as session, pytest.raises(journal.RepoPulseAdoptionJournalError) as error: journal.advance_repopulse_adoption( session, "appops_ownership", early_ownership, expected_revision=4 ) assert error.value.code == "controller_registration_missing" assert current["phase"] == "snapshot_verified" registration = _registration(candidate) with Session(engine) as session: registered = journal.advance_repopulse_adoption( session, "controller_registration", registration, expected_revision=4, ) candidate = advance_intent(candidate, "controller_registration", registration) assert registered["phase"] == "target_registered" assert registered["revision"] == 5 with Session(engine) as session: recovery = journal.mark_repopulse_adoption_recovery( session, "appops_commit_uncertain", {"uncertain": True, "path": "private/path"}, expected_revision=5, ) assert recovery["status"] == "recovery_required" assert recovery["phase"] == "target_registered" assert recovery["legacyVolumePreserved"] is True assert recovery["controllerReconciliationRequired"] is True assert "private/path" not in str(recovery) controller_receipt = { "apiVersion": CONTROLLER_RECEIPT_VERSION, "intentId": candidate["intentId"], "phase": "target_registered", "receiptSha256": registration["receiptSha256"], "repository": REPOSITORY, "applicationId": APPLICATION_ID, "legacyVolumeRetained": True, "intentDurable": True, "intentBindingSha256": intent_binding_sha256(candidate), "registrationEvidence": registration, } with Session(engine) as session: resumed = journal.resume_repopulse_adoption( session, controller_receipt, expected_revision=6 ) assert resumed["status"] == "in_progress" assert resumed["phase"] == "target_registered" assert resumed["revision"] == 7 ownership = _ownership(candidate, registration) with Session(engine) as session: owned = journal.advance_repopulse_adoption( session, "appops_ownership", ownership, expected_revision=7 ) assert owned["phase"] == "appops_owned" assert owned["revision"] == 8 assert owned["status"] != "completed"