from sqlmodel import Session, SQLModel, create_engine, select from backend.app.api import routes_jobs from backend.app.db.models import AppSetting, AuditEvent, JobRun from backend.app.services import action_execution, mcp_operations from backend.app.services import privatemuse_deployment_recovery as recovery from backend.app.services.action_execution import approval_execution_supported from backend.app.services.action_registry import ( ACTION_RISK, ACTIONS, action_default_target, ) def test_privatemuse_target_registration_is_a_typed_high_risk_action(): action = "deployment.target.register_privatemuse" assert action in ACTIONS assert ACTION_RISK[action] == "high" assert action_default_target(action) == "jens/privatemuse" assert approval_execution_supported(action) is True def test_privatemuse_target_registration_rejects_any_other_target(): result = action_execution.execute_governed_action( None, # type: ignore[arg-type] action="deployment.target.register_privatemuse", target="jens/not-privatemuse", source="chatgpt-mcp", risk="high", reason="Regression test for exact-target enforcement.", original_policy_decision="allowed", ) assert result["success"] is False assert result["result"] == "blocked" assert result["policyDecision"] == "blocked" assert result["message"] == "PrivateMuse target registration accepts only jens/privatemuse." def test_privatemuse_target_registration_dispatches_only_fixed_executor(monkeypatch): observed = {} def fake_executor(session, **kwargs): observed["session"] = session observed.update(kwargs) return { "success": True, "status": "success", "result": "success", "message": "enrolled", "jobId": "job-private-muse", "auditId": "audit-private-muse", "policyDecision": "allowed", "originalPolicyDecision": kwargs["original_policy_decision"], } monkeypatch.setattr( action_execution, "_execute_privatemuse_target_enrollment", fake_executor, ) sentinel_session = object() result = action_execution.execute_governed_action( sentinel_session, # type: ignore[arg-type] action="deployment.target.register_privatemuse", target="JENS/PrivateMuse", source="chatgpt-mcp", risk="high", reason="Enroll the fixed PrivateMuse deployment target.", original_policy_decision="allowed", ) assert result["success"] is True assert observed == { "session": sentinel_session, "source": "chatgpt-mcp", "risk": "high", "reason": "Enroll the fixed PrivateMuse deployment target.", "original_policy_decision": "allowed", } def test_privatemuse_unknown_outcome_keeps_job_running_and_retains_exec_evidence( tmp_path, monkeypatch ): engine = create_engine(f"sqlite:///{tmp_path / 'unknown-action.db'}") SQLModel.metadata.create_all(engine) def uncertain_run(self, *, session, job_id): return { "status": "outcome_unknown", "reason": "Detached recovery outcome is unknown; do not retry.", "execution_id": "exec-action-unknown", "original_job_id": job_id, } monkeypatch.setattr(recovery.PrivateMuseDeploymentRecovery, "run", uncertain_run) with Session(engine) as session: result = action_execution.execute_governed_action( session, action="deployment.target.register_privatemuse", target="jens/privatemuse", source="chatgpt-mcp", risk="high", reason="Exercise durable unknown-outcome reporting.", original_policy_decision="allowed", ) job = session.get(JobRun, result["jobId"]) audit = session.get(AuditEvent, result["auditId"]) assert result["success"] is False assert result["status"] == "outcome_unknown" assert result["result"] == "outcome_unknown" assert result["executionId"] == "exec-action-unknown" assert job is not None assert job.status == "running" assert "exec-action-unknown" in str(job.result_summary) assert audit is not None assert audit.result == "outcome_unknown" assert audit.after_state["executionId"] == "exec-action-unknown" assert audit.after_state["originalJobId"] == result["jobId"] def test_unknown_privatemuse_recovery_job_cannot_be_cancelled(tmp_path): engine = create_engine(f"sqlite:///{tmp_path / 'cancel-guard.db'}") SQLModel.metadata.create_all(engine) with Session(engine) as session: session.add( JobRun( id="job-unknown-recovery", type="deployment.target.register_privatemuse", status="running", source="chatgpt-mcp", action_id="deployment.target.register_privatemuse", target="jens/privatemuse", risk="high", policy_decision="allowed", result_summary="outcome_unknown; executionId=exec-cancel-guard", ) ) session.add( AppSetting( key=recovery._RECOVERY_STATE_SETTING, value_json={ "state": "outcome_unknown", "executionId": "exec-cancel-guard", "controllerId": "controller-id", "jobId": "job-unknown-recovery", }, is_secret=False, ) ) session.commit() result = mcp_operations.cancel_operation_job( session, "job-unknown-recovery" ) route_result = routes_jobs.cancel("job-unknown-recovery", session=session) persisted = session.exec( select(JobRun).where(JobRun.id == "job-unknown-recovery") ).one() assert result == { "jobId": "job-unknown-recovery", "status": "running", "cancelled": False, "changed": False, "reason": ( "This job owns an unresolved detached PrivateMuse recovery execution; " "reconcile that execution before changing the job record." ), } assert route_result == { "success": False, "message": ( "This job owns an unresolved detached PrivateMuse recovery execution; " "reconcile that execution before changing the job record." ), "id": "job-unknown-recovery", "status": "running", } assert persisted.status == "running" def test_privatemuse_job_is_cancellation_guarded_at_admission(tmp_path): engine = create_engine(f"sqlite:///{tmp_path / 'admission-cancel.db'}") SQLModel.metadata.create_all(engine) with Session(engine) as session: _state, admitted = recovery.PrivateMuseDeploymentRecovery.admit_job( session, JobRun( id="job-admitted-recovery", type="deployment.target.register_privatemuse", status="queued", source="chatgpt-mcp", action_id="deployment.target.register_privatemuse", target="jens/privatemuse", risk="high", policy_decision="allowed", ), ) result = mcp_operations.cancel_operation_job( session, "job-admitted-recovery" ) assert admitted is True assert result["cancelled"] is False assert result["changed"] is False assert result["status"] == "queued" assert "unresolved detached PrivateMuse recovery" in result["reason"] def test_privatemuse_cancellation_guard_applies_only_to_the_owner(tmp_path): engine = create_engine(f"sqlite:///{tmp_path / 'owner-cancel.db'}") SQLModel.metadata.create_all(engine) with Session(engine) as session: recovery.PrivateMuseDeploymentRecovery.admit_job( session, JobRun( id="job-owner", type="deployment.target.register_privatemuse", status="queued", source="chatgpt-mcp", action_id="deployment.target.register_privatemuse", target="jens/privatemuse", risk="high", policy_decision="allowed", ), ) unrelated = JobRun( id="job-unrelated", type="deployment.target.register_privatemuse", status="running", source="chatgpt-mcp", action_id="deployment.target.register_privatemuse", target="jens/privatemuse", risk="high", policy_decision="allowed", ) session.add(unrelated) session.commit() result = mcp_operations.cancel_operation_job(session, unrelated.id) assert result["cancelled"] is True assert result["changed"] is True assert result["status"] == "cancelled"