исправить: закрыть ревью сквозной корреляции
This commit is contained in:
@@ -11,6 +11,7 @@ use serde_json::{Map, Value, json};
|
||||
use time::OffsetDateTime;
|
||||
use tokio::sync::{OwnedSemaphorePermit, Semaphore};
|
||||
use tracing::{Instrument, Span, debug};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
AdapterResponse, PreparedRequest, ResolvedAuth, RuntimeError, RuntimeLimits, RuntimeOperation,
|
||||
@@ -517,9 +518,9 @@ impl RuntimeExecutor {
|
||||
fn adapter_request_context(
|
||||
request_context: Option<&RuntimeRequestContext>,
|
||||
) -> crank_core::RuntimeRequestContext {
|
||||
request_context
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|| crank_core::RuntimeRequestContext::new(String::new(), String::new()))
|
||||
request_context.map(Into::into).unwrap_or_else(|| {
|
||||
crank_core::RuntimeRequestContext::from_request_id(Uuid::now_v7().to_string())
|
||||
})
|
||||
}
|
||||
|
||||
fn map_protocol_adapter_error(
|
||||
|
||||
@@ -2,6 +2,5 @@ mod integration {
|
||||
mod confirmation;
|
||||
mod idempotency;
|
||||
mod no_input_get;
|
||||
mod stages;
|
||||
mod valkey;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,12 @@ async fn valkey_coordination_and_rate_limit_operations_are_atomic() {
|
||||
.get_host_port_ipv4(6379.tcp())
|
||||
.await
|
||||
.expect("Valkey port must be mapped");
|
||||
let host = container
|
||||
.get_host()
|
||||
.await
|
||||
.expect("Docker host must be resolved");
|
||||
let store = Arc::new(
|
||||
RedisCacheStore::connect(CacheBackend::Valkey, &format!("redis://127.0.0.1:{port}/0"))
|
||||
RedisCacheStore::connect(CacheBackend::Valkey, &format!("redis://{host}:{port}/0"))
|
||||
.await
|
||||
.expect("runtime store must connect to Valkey"),
|
||||
);
|
||||
|
||||
+75
@@ -20,9 +20,13 @@ use serde_json::json;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{Id, Instrument, Subscriber, field::Visit, instrument::WithSubscriber};
|
||||
use tracing_subscriber::{Layer, layer::SubscriberExt, registry::LookupSpan};
|
||||
use uuid::Version;
|
||||
|
||||
static TRACING_TEST_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
|
||||
|
||||
#[tokio::test]
|
||||
async fn successful_execution_has_real_stages_and_omits_inapplicable_ones() {
|
||||
let _tracing_test_guard = TRACING_TEST_LOCK.lock().await;
|
||||
let capture = TraceCapture::default();
|
||||
let subscriber = tracing_subscriber::registry().with(capture.clone());
|
||||
let executor = RuntimeExecutorBuilder::new()
|
||||
@@ -82,6 +86,7 @@ async fn successful_execution_has_real_stages_and_omits_inapplicable_ones() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn failed_mapping_records_closed_category_and_stops_later_stages() {
|
||||
let _tracing_test_guard = TRACING_TEST_LOCK.lock().await;
|
||||
let capture = TraceCapture::default();
|
||||
let subscriber = tracing_subscriber::registry().with(capture.clone());
|
||||
let executor = RuntimeExecutorBuilder::new()
|
||||
@@ -110,8 +115,46 @@ async fn failed_mapping_records_closed_category_and_stops_later_stages() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn execution_without_context_sends_generated_correlation_headers() {
|
||||
let _tracing_test_guard = TRACING_TEST_LOCK.lock().await;
|
||||
let captured_headers = Arc::new(Mutex::new(None));
|
||||
let executor = RuntimeExecutorBuilder::new()
|
||||
.register_adapter(Arc::new(ContextCapturingAdapter {
|
||||
captured_headers: Arc::clone(&captured_headers),
|
||||
}))
|
||||
.build();
|
||||
let operation = operation().into();
|
||||
|
||||
let result = async {
|
||||
executor
|
||||
.execute(&operation, &json!({"name": "without-context"}))
|
||||
.await
|
||||
}
|
||||
.with_subscriber(tracing_subscriber::registry())
|
||||
.await;
|
||||
|
||||
assert_eq!(result.unwrap(), json!({"accepted": true}));
|
||||
let headers = captured_headers
|
||||
.lock()
|
||||
.expect("captured headers lock")
|
||||
.clone()
|
||||
.expect("adapter headers");
|
||||
let request_id = headers.get("x-request-id").expect("x-request-id");
|
||||
let correlation_id = headers.get("x-correlation-id").expect("x-correlation-id");
|
||||
assert!(!request_id.is_empty());
|
||||
assert_eq!(correlation_id, request_id);
|
||||
assert_eq!(
|
||||
uuid::Uuid::parse_str(request_id)
|
||||
.expect("generated UUID")
|
||||
.get_version(),
|
||||
Some(Version::SortRand)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn approval_stage_is_present_only_when_confirmation_is_required() {
|
||||
let _tracing_test_guard = TRACING_TEST_LOCK.lock().await;
|
||||
let capture = TraceCapture::default();
|
||||
let subscriber = tracing_subscriber::registry().with(capture.clone());
|
||||
let executor = RuntimeExecutorBuilder::new()
|
||||
@@ -151,6 +194,7 @@ async fn approval_stage_is_present_only_when_confirmation_is_required() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn idempotency_stage_distinguishes_execution_from_replay() {
|
||||
let _tracing_test_guard = TRACING_TEST_LOCK.lock().await;
|
||||
let capture = TraceCapture::default();
|
||||
let subscriber = tracing_subscriber::registry().with(capture.clone());
|
||||
let executor = RuntimeExecutorBuilder::new()
|
||||
@@ -238,6 +282,37 @@ impl ProtocolAdapter for SuccessAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
struct ContextCapturingAdapter {
|
||||
captured_headers: Arc<Mutex<Option<BTreeMap<String, String>>>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ProtocolAdapter for ContextCapturingAdapter {
|
||||
fn protocol(&self) -> Protocol {
|
||||
Protocol::Rest
|
||||
}
|
||||
|
||||
fn supports_mode(&self, mode: ExecutionMode) -> bool {
|
||||
mode == ExecutionMode::Unary
|
||||
}
|
||||
|
||||
async fn invoke_unary(
|
||||
&self,
|
||||
_target: &Target,
|
||||
_prepared: &crank_core::PreparedRequest,
|
||||
context: &crank_core::RuntimeRequestContext,
|
||||
) -> Result<AdapterResponse, ProtocolAdapterError> {
|
||||
*self.captured_headers.lock().expect("captured headers lock") =
|
||||
Some(context.outbound_headers());
|
||||
Ok(AdapterResponse {
|
||||
status_code: 200,
|
||||
headers: BTreeMap::new(),
|
||||
body: json!({"accepted": true}),
|
||||
data: json!({"accepted": true}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn operation() -> Operation<Schema, MappingSet> {
|
||||
Operation {
|
||||
id: OperationId::new("op_stage_test"),
|
||||
Reference in New Issue
Block a user