наблюдаемость: завершить базовый контур Community
Добавить структурированные журналы, метрики, трассировку и безопасный канал критических ошибок. Усилить границы рантайма, тесты, проверку зависимостей и сценарии развёртывания.
This commit is contained in:
@@ -16,6 +16,7 @@ use crank_runtime::{
|
||||
InMemoryCoordinationStateStore, RuntimeError, RuntimeExecutorBuilder, RuntimeRequestContext,
|
||||
};
|
||||
use crank_schema::{Schema, SchemaKind};
|
||||
use futures_util::future::join_all;
|
||||
use serde_json::json;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
@@ -89,6 +90,62 @@ async fn destructive_operation_requires_single_use_confirmation() {
|
||||
assert_eq!(call_count.load(Ordering::SeqCst), 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn confirmation_token_allows_only_one_concurrent_execution() {
|
||||
let call_count = Arc::new(AtomicUsize::new(0));
|
||||
let executor = RuntimeExecutorBuilder::new()
|
||||
.register_adapter(Arc::new(CountingAdapter {
|
||||
call_count: Arc::clone(&call_count),
|
||||
}))
|
||||
.with_coordination_store(Arc::new(InMemoryCoordinationStateStore::default()))
|
||||
.build();
|
||||
let operation: crank_runtime::RuntimeOperation = destructive_delete_operation().into();
|
||||
let context = RuntimeRequestContext::from_request_id("req_confirm_concurrent")
|
||||
.with_response_cache_scope("workspace_1", "agent_1");
|
||||
let first = executor
|
||||
.execute_with_context(
|
||||
&operation,
|
||||
&json!({ "order_id": "ord_123" }),
|
||||
Some(&context),
|
||||
)
|
||||
.await
|
||||
.unwrap_err();
|
||||
let RuntimeError::ConfirmationRequired {
|
||||
confirmation_token, ..
|
||||
} = first
|
||||
else {
|
||||
panic!("expected confirmation token")
|
||||
};
|
||||
|
||||
let attempts = (0..16).map(|_| {
|
||||
let executor = executor.clone();
|
||||
let operation = operation.clone();
|
||||
let context = context
|
||||
.clone()
|
||||
.with_confirmation_token(confirmation_token.clone());
|
||||
async move {
|
||||
executor
|
||||
.execute_with_context(
|
||||
&operation,
|
||||
&json!({ "order_id": "ord_123" }),
|
||||
Some(&context),
|
||||
)
|
||||
.await
|
||||
}
|
||||
});
|
||||
let results = join_all(attempts).await;
|
||||
let successful = results.iter().filter(|result| result.is_ok()).count();
|
||||
|
||||
assert_eq!(successful, 1);
|
||||
assert_eq!(call_count.load(Ordering::SeqCst), 1);
|
||||
assert!(
|
||||
results
|
||||
.iter()
|
||||
.filter_map(|result| result.as_ref().err())
|
||||
.all(|error| matches!(error, RuntimeError::InvalidConfirmationToken { .. }))
|
||||
);
|
||||
}
|
||||
|
||||
struct CountingAdapter {
|
||||
call_count: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user