use std::sync::Arc; use crank_core::{ AgentId, AgentOperationBinding, AgentStatus, ApprovalRequest, ApprovalRequestId, ApprovalRequestStatus, OperationApprovalRiskLevel, OperationId, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyKind, PlatformApiKeyScope, PlatformApiKeyStatus, WorkspaceId, }; use crank_registry::{ CreateAgentRequest, CreateApprovalRequest, CreatePlatformApiKeyRequest, DecideApprovalRequest, FinishApprovalRequest, }; use serde_json::{Value, json}; use time::{OffsetDateTime, format_description::well_known::Rfc3339}; use super::common::{TestDatabase, test_agent, test_agent_version, test_operation}; fn timestamp(value: &str) -> OffsetDateTime { OffsetDateTime::parse(value, &Rfc3339).unwrap() } fn approval_request(id: &str, payload: Value) -> ApprovalRequest { ApprovalRequest { id: ApprovalRequestId::new(id), workspace_id: WorkspaceId::new("ws_default"), agent_id: AgentId::new("agent_approval_atomic"), operation_id: OperationId::new("operation_approval_atomic"), operation_version: 1, status: ApprovalRequestStatus::Pending, risk_level: OperationApprovalRiskLevel::Dangerous, request_id: None, trace_id: None, request_payload: payload, response_payload: None, created_at: timestamp("2026-03-25T12:01:00Z"), expires_at: timestamp("2027-03-25T12:06:00Z"), decided_at: None, decided_by_key_id: None, decision_note: None, } } fn approval_key() -> PlatformApiKey { PlatformApiKey { id: PlatformApiKeyId::new("approval_atomic_key"), workspace_id: WorkspaceId::new("ws_default"), agent_id: Some(AgentId::new("agent_approval_atomic")), key_kind: PlatformApiKeyKind::Approval, name: "approval atomic key".to_owned(), prefix: "crk_appr".to_owned(), scopes: vec![PlatformApiKeyScope::Approve, PlatformApiKeyScope::Deny], status: PlatformApiKeyStatus::Active, created_at: timestamp("2026-03-25T12:00:00Z"), last_used_at: None, expires_at: None, allowed_origins: Vec::new(), } } async fn seed_approval_graph(registry: &crank_registry::PostgresRegistry) { let workspace_id = WorkspaceId::new("ws_default"); let operation = test_operation( "operation_approval_atomic", 1, crank_core::OperationStatus::Published, ); registry .create_operation(&workspace_id, &operation, None) .await .unwrap(); let agent = test_agent("agent_approval_atomic", AgentStatus::Draft); let version = test_agent_version(&agent.id, 1, AgentStatus::Draft); registry .create_agent(CreateAgentRequest { agent: &agent, version: &version, bindings: &[AgentOperationBinding { operation_id: operation.id.clone(), operation_version: 1, tool_name: "dangerous_write".to_owned(), tool_title: "Dangerous write".to_owned(), tool_description_override: None, enabled: true, }], }) .await .unwrap(); let key = approval_key(); registry .create_platform_api_key(CreatePlatformApiKeyRequest { api_key: &key, secret_hash: "approval-key-secret-hash", }) .await .unwrap(); } #[tokio::test] async fn concurrent_equal_scope_creates_one_pending_approval_without_losing_raw_execution_payload() { let database = TestDatabase::new().await; let registry = Arc::new(database.registry().await); seed_approval_graph(®istry).await; let mut tasks = Vec::new(); for index in 0..32 { let registry = Arc::clone(®istry); tasks.push(tokio::spawn(async move { let mut approval = approval_request( &format!("approval_concurrent_{index}"), json!({ "email": "customer@example.com", "token": "SECRET_APPROVAL_CANARY", "_crank_confirmation_token": format!("ct_{index}") }), ); approval.created_at += time::Duration::milliseconds(index); registry .create_approval_request(CreateApprovalRequest { approval: &approval, }) .await .unwrap() })); } let mut ids = std::collections::BTreeSet::new(); for task in tasks { ids.insert(task.await.unwrap().approval.id.as_str().to_owned()); } assert_eq!( ids.len(), 1, "all callers must receive one canonical pending approval" ); let pending = registry .list_pending_approval_requests_for_agent( &WorkspaceId::new("ws_default"), &AgentId::new("agent_approval_atomic"), ) .await .unwrap(); assert_eq!(pending.len(), 1); assert_eq!( pending[0].approval.request_payload["token"], "SECRET_APPROVAL_CANARY", "registry keeps the raw payload for the eventual approved execution" ); assert!( pending[0].approval.request_payload["_crank_confirmation_token"] .as_str() .is_some_and(|value| value.starts_with("ct_")), "only projections may redact control data; execution input remains intact" ); let safe_preview = crank_core::sanitize_invocation_preview(&pending[0].approval.request_payload); let preview = safe_preview.to_string(); assert!(!preview.contains("SECRET_APPROVAL_CANARY")); database.cleanup().await; } #[tokio::test] async fn nested_business_control_like_fields_are_part_of_approval_fingerprint() { let database = TestDatabase::new().await; let registry = database.registry().await; seed_approval_graph(®istry).await; let first = approval_request( "approval_nested_control_a", json!({ "line": { "_crank_approval_id": "business-a" }, "_crank_confirmation_token": "transport-a" }), ); registry .create_approval_request(CreateApprovalRequest { approval: &first }) .await .unwrap(); let second = approval_request( "approval_nested_control_b", json!({ "line": { "_crank_approval_id": "business-b" }, "_crank_confirmation_token": "transport-b" }), ); let created_second = registry .create_approval_request(CreateApprovalRequest { approval: &second }) .await .unwrap(); assert_eq!(created_second.approval.id, second.id); let pending = registry .list_pending_approval_requests_for_agent( &WorkspaceId::new("ws_default"), &AgentId::new("agent_approval_atomic"), ) .await .unwrap(); assert_eq!(pending.len(), 2); database.cleanup().await; } #[tokio::test] async fn decision_claim_finish_and_replay_are_single_winner_transitions() { let database = TestDatabase::new().await; let registry = Arc::new(database.registry().await); seed_approval_graph(®istry).await; let approval = approval_request( "approval_single_winner", json!({"email":"lead@example.com"}), ); let created = registry .create_approval_request(CreateApprovalRequest { approval: &approval, }) .await .unwrap(); assert_eq!(created.approval.status, ApprovalRequestStatus::Pending); let mut decisions = Vec::new(); for index in 0..16 { let registry = Arc::clone(®istry); let operation_id = approval.operation_id.clone(); let operation_version = approval.operation_version; let request_payload = approval.request_payload.clone(); decisions.push(tokio::spawn(async move { let key_id = PlatformApiKeyId::new("approval_atomic_key"); registry .decide_approval_request(DecideApprovalRequest { workspace_id: &WorkspaceId::new("ws_default"), agent_id: &AgentId::new("agent_approval_atomic"), approval_id: &ApprovalRequestId::new("approval_single_winner"), operation_id: &operation_id, operation_version, request_payload: &request_payload, status: if index % 2 == 0 { ApprovalRequestStatus::Approved } else { ApprovalRequestStatus::Denied }, decided_at: timestamp("2026-03-25T12:02:00Z"), decided_by_key_id: Some(&key_id), response_payload: Some(json!({ "decision": index })), decision_note: Some("race decision"), }) .await .unwrap() })); } let mut winners = 0; for decision in decisions { if decision.await.unwrap().is_some() { winners += 1; } } assert_eq!(winners, 1); let mut claims = Vec::new(); for _ in 0..16 { let registry = Arc::clone(®istry); claims.push(tokio::spawn(async move { registry .claim_approval_request( &WorkspaceId::new("ws_default"), &AgentId::new("agent_approval_atomic"), &ApprovalRequestId::new("approval_single_winner"), timestamp("2026-03-25T12:02:01Z"), ) .await .unwrap() })); } let mut claim_winners = 0; for claim in claims { if claim.await.unwrap().is_some() { claim_winners += 1; } } assert_eq!(claim_winners, 1); let first_finish = registry .finish_approval_request(FinishApprovalRequest { workspace_id: &WorkspaceId::new("ws_default"), agent_id: &AgentId::new("agent_approval_atomic"), approval_id: &ApprovalRequestId::new("approval_single_winner"), status: ApprovalRequestStatus::Completed, response_payload: Some(json!({"ok":true})), decision_note: None, }) .await .unwrap(); assert!(first_finish.is_some()); let replay_finish = registry .finish_approval_request(FinishApprovalRequest { workspace_id: &WorkspaceId::new("ws_default"), agent_id: &AgentId::new("agent_approval_atomic"), approval_id: &ApprovalRequestId::new("approval_single_winner"), status: ApprovalRequestStatus::Completed, response_payload: Some(json!({"ok":"replay"})), decision_note: None, }) .await .unwrap(); assert!(replay_finish.is_none()); database.cleanup().await; }