use std::sync::Arc;
use a2a_protocol_types::error::{A2aError, A2aResult};
use a2a_protocol_types::message::Message;
use a2a_protocol_types::task::TaskState;
use super::{Outcome, check, check_with_context, outcome_of};
use crate::executor::AgentExecutor;
use crate::executor_helpers::EventEmitter;
use crate::request_context::RequestContext;
use crate::streaming::EventQueueWriter;
executor!(PanicsOnCancel, |ctx, queue| {
let emit = EventEmitter::new(ctx, queue);
assert!(
!emit.is_cancelled(),
"conformance harness: deliberate panic on the cancel path"
);
emit.status(TaskState::Working).await?;
assert!(
!emit.is_cancelled(),
"conformance harness: deliberate panic on the cancel path"
);
emit.status(TaskState::Completed).await
});
#[tokio::test]
async fn a_panic_on_the_cancel_path_is_caught() {
let report = check(Arc::new(PanicsOnCancel)).await;
assert_eq!(
outcome_of(&report, "does_not_panic_when_cancelled"),
Outcome::Fail,
"{report}"
);
assert!(!report.is_pass(), "{report}");
}
executor!(ErrorsOnCancel, |ctx, queue| {
let emit = EventEmitter::new(ctx, queue);
if emit.is_cancelled() {
return Err(A2aError::internal("cancelled"));
}
emit.status(TaskState::Working).await?;
if emit.is_cancelled() {
return Err(A2aError::internal("cancelled"));
}
emit.status(TaskState::Completed).await
});
#[tokio::test]
async fn reporting_cancellation_as_an_error_is_caught() {
let report = check(Arc::new(ErrorsOnCancel)).await;
assert_eq!(
outcome_of(&report, "cancellation_is_not_an_error"),
Outcome::Fail,
"{report}"
);
assert!(!report.is_pass(), "{report}");
}
executor!(AlwaysErrors, |_ctx, _queue| {
Err(A2aError::internal("not implemented"))
});
#[tokio::test]
async fn an_executor_that_only_ever_errors_is_not_a_pass() {
let report = check(Arc::new(AlwaysErrors)).await;
assert!(
!report.is_pass(),
"an executor that emits nothing and fails everything must not pass:\n{report}"
);
assert_eq!(
outcome_of(&report, "ends_in_terminal_or_interrupt"),
Outcome::Fail,
"{report}"
);
}
executor!(NeverReturns, |_ctx, _queue| {
std::future::pending::<()>().await;
Ok(())
});
#[tokio::test(start_paused = true)]
async fn an_executor_that_never_returns_is_graded_rather_than_hanging() {
let report = check(Arc::new(NeverReturns)).await;
assert_eq!(
outcome_of(&report, "returns_within_the_time_limit"),
Outcome::Fail,
"{report}"
);
assert!(!report.is_pass(), "{report}");
}
struct NeedsTenant;
impl crate::executor::AgentExecutor for NeedsTenant {
fn execute<'a>(
&'a self,
ctx: &'a crate::request_context::RequestContext,
queue: &'a dyn crate::streaming::EventQueueWriter,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = A2aResult<()>> + Send + 'a>> {
Box::pin(async move {
let emit = EventEmitter::new(ctx, queue);
if ctx.tenant() != Some("acme") {
return Err(A2aError::internal("this executor serves one tenant only"));
}
if emit.is_cancelled() {
return emit.status(TaskState::Canceled).await;
}
emit.status(TaskState::Working).await?;
if emit.is_cancelled() {
return emit.status(TaskState::Canceled).await;
}
emit.status(TaskState::Completed).await
})
}
}
#[tokio::test]
async fn an_executor_that_reads_its_call_context_can_be_graded() {
let without = check(Arc::new(NeedsTenant)).await;
assert!(
!without.is_pass(),
"without a tenant this executor refuses, and that must show:\n{without}"
);
let with = check_with_context(
Arc::new(NeedsTenant),
Message::user_text("conformance-1", "ping"),
crate::call_context::CallContext::new("SendMessage").with_tenant("acme"),
)
.await;
with.assert_pass();
}