use serde::de::DeserializeOwned;
pub fn assert_roundtrips_through_client<T>(real_dispatch_output: serde_json::Value)
where
T: DeserializeOwned,
{
let pretty = serde_json::to_string_pretty(&real_dispatch_output)
.unwrap_or_else(|_| real_dispatch_output.to_string());
if let Err(error) = serde_json::from_value::<T>(real_dispatch_output) {
panic!(
"dispatch output does not deserialize into `{}`: {error}\noffending output was:\n{pretty}",
std::any::type_name::<T>(),
);
}
}
#[cfg(test)]
mod tests {
use super::assert_roundtrips_through_client;
use crate::types::tasks::{CreateTaskResult, GetTaskResult, Task, TaskStatus};
fn sample_task() -> Task {
Task::new("t-positive", TaskStatus::Working)
.with_timestamps("2026-06-21T00:00:00Z", "2026-06-21T00:00:00Z")
}
#[test]
fn passes_on_valid_get_task_result() {
let value = serde_json::to_value(GetTaskResult::new(sample_task())).unwrap();
assert_roundtrips_through_client::<GetTaskResult>(value);
}
#[test]
fn passes_on_valid_create_task_result() {
let mut value = serde_json::to_value(CreateTaskResult::new(sample_task())).unwrap();
value.as_object_mut().unwrap().insert(
"_meta".to_string(),
serde_json::json!({ "io.modelcontextprotocol/related-task": { "taskId": "t-positive" } }),
);
assert_roundtrips_through_client::<CreateTaskResult>(value);
}
#[test]
#[should_panic(expected = "does not deserialize into")]
fn panics_on_exact_historical_flat_task_shape() {
let flat_task = serde_json::to_value(Task::new("t-1", TaskStatus::Working)).unwrap();
assert!(flat_task.get("taskId").is_some());
assert!(flat_task.get("task").is_none());
assert_roundtrips_through_client::<GetTaskResult>(flat_task);
}
}