use adk_code::{CollaborationEventKind, Workspace};
use proptest::prelude::*;
use std::time::Duration;
fn arb_identifier() -> impl Strategy<Value = String> {
"[a-zA-Z0-9_-]{1,30}"
}
fn arb_event_kind() -> impl Strategy<Value = CollaborationEventKind> {
prop_oneof![
Just(CollaborationEventKind::NeedWork),
Just(CollaborationEventKind::WorkClaimed),
Just(CollaborationEventKind::WorkPublished),
Just(CollaborationEventKind::FeedbackRequested),
Just(CollaborationEventKind::FeedbackProvided),
Just(CollaborationEventKind::Blocked),
Just(CollaborationEventKind::Completed),
]
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_collaboration_correlation_is_preserved(
correlation_id in arb_identifier(),
topic in arb_identifier(),
requester in arb_identifier(),
publisher in arb_identifier(),
) {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap();
rt.block_on(async {
let ws = Workspace::new("/tmp/prop-test").build();
let ws_pub = ws.clone();
let corr = correlation_id.clone();
let t = topic.clone();
let p = publisher.clone();
ws.request_work(&correlation_id, &topic, &requester);
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(5)).await;
ws_pub.publish_work(&corr, &t, &p, serde_json::json!({ "result": "ok" }));
});
let result = ws.wait_for_work(&correlation_id, Duration::from_secs(2)).await;
let event = result.expect("should receive matching WorkPublished event");
prop_assert_eq!(&event.correlation_id, &correlation_id);
prop_assert_eq!(event.kind, CollaborationEventKind::WorkPublished);
prop_assert_eq!(&event.topic, &topic);
prop_assert_eq!(&event.producer, &publisher);
prop_assert_eq!(event.payload, serde_json::json!({ "result": "ok" }));
Ok(())
})?;
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_blocked_agents_resume_only_on_matching_work(
target_corr in arb_identifier(),
wrong_corr in arb_identifier(),
target_kind in arb_event_kind(),
wrong_kind in arb_event_kind(),
topic in arb_identifier(),
producer in arb_identifier(),
) {
prop_assume!(target_corr != wrong_corr);
prop_assume!(target_kind != wrong_kind);
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap();
rt.block_on(async {
let ws = Workspace::new("/tmp/prop-test-2").build();
let ws_pub = ws.clone();
let tc = target_corr.clone();
let wc = wrong_corr.clone();
let tk = target_kind;
let wk = wrong_kind;
let t = topic.clone();
let p = producer.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(5)).await;
ws_pub.publish(adk_code::CollaborationEvent::new(
&wc, &t, &p, tk,
));
ws_pub.publish(adk_code::CollaborationEvent::new(
&tc, &t, &p, wk,
));
ws_pub.publish(adk_code::CollaborationEvent::new(
&wc, &t, &p, wk,
));
tokio::time::sleep(Duration::from_millis(5)).await;
ws_pub.publish(adk_code::CollaborationEvent::new(
&tc, &t, &p, tk,
));
});
let result = ws
.wait_for_kind(&target_corr, target_kind, Duration::from_secs(2))
.await;
let event = result.expect("should resume only on exact match");
prop_assert_eq!(&event.correlation_id, &target_corr);
prop_assert_eq!(event.kind, target_kind);
prop_assert_eq!(&event.topic, &topic);
prop_assert_eq!(&event.producer, &producer);
Ok(())
})?;
}
}