use asupersync::{Budget, Cx};
use fastmcp_core::{McpContext, SessionState};
#[derive(Clone)]
pub struct TestContext {
cx: Cx,
budget: Option<Budget>,
session_state: Option<SessionState>,
}
impl std::fmt::Debug for TestContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TestContext")
.field("has_budget", &self.budget.is_some())
.field("has_session_state", &self.session_state.is_some())
.finish()
}
}
impl Default for TestContext {
fn default() -> Self {
Self::new()
}
}
impl TestContext {
#[must_use]
pub fn new() -> Self {
Self {
cx: Cx::for_testing(),
budget: None,
session_state: None,
}
}
#[must_use]
pub fn with_budget_secs(mut self, secs: u64) -> Self {
self.budget = Some(Budget::with_deadline_secs(secs));
self
}
#[must_use]
pub fn with_budget_ms(mut self, ms: u64) -> Self {
let secs = (ms + 999) / 1000;
self.budget = Some(Budget::with_deadline_secs(secs));
self
}
#[must_use]
pub fn with_session_state(mut self, state: SessionState) -> Self {
self.session_state = Some(state);
self
}
#[must_use]
pub fn cx(&self) -> &Cx {
&self.cx
}
#[must_use]
pub fn cx_clone(&self) -> Cx {
self.cx.clone()
}
#[must_use]
pub fn budget(&self) -> Option<&Budget> {
self.budget.as_ref()
}
#[must_use]
pub fn mcp_context(&self, request_id: u64) -> McpContext {
if let Some(state) = &self.session_state {
McpContext::with_state(self.cx.clone(), request_id, state.clone())
} else {
McpContext::new(self.cx.clone(), request_id)
}
}
#[must_use]
pub fn mcp_context_with_state(&self, request_id: u64, state: SessionState) -> McpContext {
McpContext::with_state(self.cx.clone(), request_id, state)
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.cx.is_cancel_requested()
}
pub fn checkpoint(&self) -> fastmcp_core::McpResult<()> {
if self.cx.is_cancel_requested() {
Err(fastmcp_core::McpError::request_cancelled())
} else {
Ok(())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_context_creation() {
let ctx = TestContext::new();
assert!(ctx.budget().is_none());
assert!(!ctx.is_cancelled());
}
#[test]
fn test_context_with_budget() {
let ctx = TestContext::new().with_budget_secs(10);
assert!(ctx.budget().is_some());
}
#[test]
fn test_context_with_session_state() {
let state = SessionState::new();
let ctx = TestContext::new().with_session_state(state);
assert!(ctx.session_state.is_some());
}
#[test]
fn test_mcp_context_creation() {
let ctx = TestContext::new();
let mcp_ctx = ctx.mcp_context(42);
assert_eq!(mcp_ctx.request_id(), 42);
}
#[test]
fn test_mcp_context_with_shared_state() {
let state = SessionState::new();
{
let ctx = TestContext::new().with_session_state(state.clone());
let mcp_ctx = ctx.mcp_context(1);
mcp_ctx.set_state("test_key", "test_value".to_string());
}
{
let ctx = TestContext::new().with_session_state(state.clone());
let mcp_ctx = ctx.mcp_context(2);
let value: Option<String> = mcp_ctx.get_state("test_key");
assert_eq!(value, Some("test_value".to_string()));
}
}
#[test]
fn test_checkpoint_not_cancelled() {
let ctx = TestContext::new();
assert!(ctx.checkpoint().is_ok());
}
#[test]
fn default_matches_new() {
let def = TestContext::default();
let new = TestContext::new();
assert!(def.budget().is_none());
assert!(new.budget().is_none());
assert!(!def.is_cancelled());
}
#[test]
fn debug_output() {
let ctx = TestContext::new();
let debug = format!("{ctx:?}");
assert!(debug.contains("TestContext"));
assert!(debug.contains("has_budget"));
assert!(debug.contains("has_session_state"));
}
#[test]
fn clone_produces_independent_context() {
let ctx = TestContext::new().with_budget_secs(30);
let cloned = ctx.clone();
assert!(cloned.budget().is_some());
}
#[test]
fn with_budget_ms_sets_budget() {
let ctx = TestContext::new().with_budget_ms(5000);
assert!(ctx.budget().is_some());
}
#[test]
fn cx_and_cx_clone_accessors() {
let ctx = TestContext::new();
let _cx_ref = ctx.cx();
let _cx_owned = ctx.cx_clone();
}
#[test]
fn mcp_context_with_state_method() {
let state = SessionState::new();
let ctx = TestContext::new();
let mcp_ctx = ctx.mcp_context_with_state(99, state);
assert_eq!(mcp_ctx.request_id(), 99);
}
}