feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
+30 -20
View File
@@ -8,16 +8,16 @@ use std::{
use async_trait::async_trait;
use crank_core::{
AdapterResponse, ConfirmationPolicy, ExecutionConfig, ExecutionMode, HttpMethod,
IdempotencyMode, IdempotencyPolicy, Operation, OperationId, OperationSafetyClass,
AdapterResponse, ConfirmationPolicy, ExecutionConfig, ExecutionErrorCode, ExecutionMode,
HttpMethod, IdempotencyMode, IdempotencyPolicy, Operation, OperationId, OperationSafetyClass,
OperationSafetyPolicy, OperationSecurityLevel, OperationStatus, Protocol, ProtocolAdapter,
ProtocolAdapterError, ResponseCachePolicy, RestTarget,
RuntimeRequestContext as ProtocolRequestContext, Target, ToolDescription,
};
use crank_mapping::{MappingRule, MappingSet};
use crank_runtime::{
InMemoryCoordinationStateStore, InMemoryResponseCacheStore, RuntimeError,
RuntimeExecutorBuilder, RuntimeRequestContext,
InMemoryCoordinationStateStore, InMemoryResponseCacheStore, RuntimeExecutorBuilder,
RuntimeRequestContext,
};
use crank_schema::{Schema, SchemaKind};
use metrics_util::debugging::DebuggingRecorder;
@@ -110,10 +110,10 @@ async fn exercise_cancelled_idempotency() {
.execute_with_context(&operation, &input, Some(&context))
.await
.expect_err("cancelled external outcome must not be retried automatically");
assert!(matches!(
retry,
RuntimeError::IdempotencyOutcomeUnknown { .. }
));
assert_eq!(
retry.error_code(),
ExecutionErrorCode::IdempotencyOutcomeUnknown
);
assert_eq!(calls.load(Ordering::SeqCst), 1);
}
@@ -180,7 +180,10 @@ async fn exercise_idempotency() {
.await
.expect_err("same idempotency key with a different input must conflict");
assert!(matches!(conflict, RuntimeError::IdempotencyConflict { .. }));
assert_eq!(
conflict.error_code(),
ExecutionErrorCode::IdempotencyConflict
);
assert_eq!(calls.load(Ordering::SeqCst), 1);
}
@@ -206,12 +209,15 @@ async fn exercise_confirmation() {
.execute_with_context(&operation, &input, Some(&context))
.await
.expect_err("destructive operation must require confirmation");
let RuntimeError::ConfirmationRequired {
confirmation_token, ..
} = required
else {
panic!("expected confirmation token");
};
assert_eq!(
required.error_code(),
ExecutionErrorCode::ConfirmationRequired
);
let confirmation_token = required
.confirmation()
.expect("expected confirmation token")
.token()
.to_owned();
let confirmed = context
.clone()
.with_confirmation_token(confirmation_token.clone());
@@ -224,10 +230,10 @@ async fn exercise_confirmation() {
.await
.expect_err("confirmation token must be single-use");
assert!(matches!(
invalid,
RuntimeError::InvalidConfirmationToken { .. }
));
assert_eq!(
invalid.error_code(),
ExecutionErrorCode::ConfirmationInvalid
);
assert_eq!(calls.load(Ordering::SeqCst), 1);
}
@@ -261,9 +267,10 @@ impl ProtocolAdapter for BlockingAdapter {
&self,
_target: &Target,
_prepared: &crank_core::PreparedRequest,
_context: &ProtocolRequestContext,
context: &ProtocolRequestContext,
) -> Result<AdapterResponse, ProtocolAdapterError> {
self.calls.fetch_add(1, Ordering::SeqCst);
context.mark_dispatch_started();
std::future::pending().await
}
}
@@ -412,3 +419,6 @@ async fn wait_for_calls(calls: &AtomicUsize, expected: usize) {
}
panic!("adapter did not receive {expected} call(s)");
}
mod support;
use support::RuntimeExecutorTestExt;