mcpkit_testing/
assertions.rs

1//! Custom assertions for MCP testing.
2//!
3//! This module provides assertion helpers that make it easier to test
4//! MCP responses and results.
5
6use mcpkit_core::types::{CallToolResult, Content, ToolOutput};
7
8/// Assert that a tool result is successful and contains expected text.
9///
10/// # Panics
11///
12/// Panics if the result is an error or doesn't contain the expected text.
13pub fn assert_tool_success(result: &CallToolResult, expected_text: &str) {
14    assert!(
15        !result.is_error(),
16        "Expected successful tool result, but got error"
17    );
18    assert!(!result.content.is_empty(), "Tool result has no content");
19
20    let text = result
21        .content
22        .iter()
23        .filter_map(|c| c.as_text())
24        .collect::<Vec<_>>()
25        .join("");
26
27    assert!(
28        text.contains(expected_text),
29        "Expected tool result to contain '{expected_text}', but got '{text}'"
30    );
31}
32
33/// Assert that a tool result is an error with expected message.
34///
35/// # Panics
36///
37/// Panics if the result is successful or doesn't contain the expected message.
38pub fn assert_tool_error(result: &CallToolResult, expected_message: &str) {
39    assert!(
40        result.is_error(),
41        "Expected error tool result, but got success"
42    );
43
44    let text = result
45        .content
46        .iter()
47        .filter_map(|c| c.as_text())
48        .collect::<Vec<_>>()
49        .join("");
50
51    assert!(
52        text.contains(expected_message),
53        "Expected error message to contain '{expected_message}', but got '{text}'"
54    );
55}
56
57/// Assert that a `ToolOutput` is successful with expected text.
58///
59/// # Panics
60///
61/// Panics if the output is an error or doesn't contain the expected text.
62pub fn assert_output_success(output: &ToolOutput, expected_text: &str) {
63    match output {
64        ToolOutput::Success(result) => assert_tool_success(result, expected_text),
65        ToolOutput::RecoverableError { message, .. } => {
66            panic!("Expected successful output, but got error: {message}");
67        }
68    }
69}
70
71/// Assert that a `ToolOutput` is an error.
72///
73/// # Panics
74///
75/// Panics if the output is successful.
76pub fn assert_output_error(output: &ToolOutput, expected_message: &str) {
77    match output {
78        ToolOutput::Success(_) => {
79            panic!("Expected error output, but got success");
80        }
81        ToolOutput::RecoverableError { message, .. } => {
82            assert!(
83                message.contains(expected_message),
84                "Expected error message to contain '{expected_message}', but got '{message}'"
85            );
86        }
87    }
88}
89
90/// Assert that content is text with expected value.
91///
92/// # Panics
93///
94/// Panics if the content is not text or doesn't match.
95pub fn assert_content_text(content: &Content, expected: &str) {
96    match content {
97        Content::Text(tc) => {
98            assert_eq!(
99                tc.text, expected,
100                "Expected content text '{}', but got '{}'",
101                expected, tc.text
102            );
103        }
104        _ => panic!("Expected text content, but got other type"),
105    }
106}
107
108/// Assert that content contains expected text.
109///
110/// # Panics
111///
112/// Panics if the content doesn't contain the expected text.
113pub fn assert_content_contains(content: &Content, expected: &str) {
114    match content {
115        Content::Text(tc) => {
116            assert!(
117                tc.text.contains(expected),
118                "Expected content to contain '{}', but got '{}'",
119                expected,
120                tc.text
121            );
122        }
123        _ => panic!("Expected text content, but got other type"),
124    }
125}
126
127/// Macro for asserting tool result success.
128///
129/// # Example
130///
131/// ```rust
132/// use mcpkit_testing::assert_tool_result;
133/// use mcpkit_core::types::CallToolResult;
134///
135/// let result = CallToolResult::text("expected text");
136/// assert_tool_result!(result, "expected text");
137/// ```
138#[macro_export]
139macro_rules! assert_tool_result {
140    ($result:expr, $expected:expr) => {
141        $crate::assertions::assert_tool_success(&$result, $expected)
142    };
143}
144
145/// Macro for asserting tool result error.
146///
147/// # Example
148///
149/// ```rust
150/// use mcpkit_testing::assert_tool_error_msg;
151/// use mcpkit_core::types::CallToolResult;
152///
153/// let result = CallToolResult::error("error message");
154/// assert_tool_error_msg!(result, "error message");
155/// ```
156#[macro_export]
157macro_rules! assert_tool_error_msg {
158    ($result:expr, $expected:expr) => {
159        $crate::assertions::assert_tool_error(&$result, $expected)
160    };
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn test_assert_tool_success_passes() {
169        let result = CallToolResult::text("Hello, world!");
170        assert_tool_success(&result, "Hello");
171    }
172
173    #[test]
174    #[should_panic(expected = "Expected successful tool result")]
175    fn test_assert_tool_success_fails_on_error() {
176        let result = CallToolResult::error("Something went wrong");
177        assert_tool_success(&result, "Hello");
178    }
179
180    #[test]
181    #[should_panic(expected = "Expected tool result to contain")]
182    fn test_assert_tool_success_fails_on_wrong_text() {
183        let result = CallToolResult::text("Goodbye!");
184        assert_tool_success(&result, "Hello");
185    }
186
187    #[test]
188    fn test_assert_tool_error_passes() {
189        let result = CallToolResult::error("Something went wrong");
190        assert_tool_error(&result, "went wrong");
191    }
192
193    #[test]
194    #[should_panic(expected = "Expected error tool result")]
195    fn test_assert_tool_error_fails_on_success() {
196        let result = CallToolResult::text("Success!");
197        assert_tool_error(&result, "error");
198    }
199
200    #[test]
201    fn test_assert_output_success() {
202        let output = ToolOutput::text("Result");
203        assert_output_success(&output, "Result");
204    }
205
206    #[test]
207    fn test_assert_output_error() {
208        let output = ToolOutput::error("Failed");
209        assert_output_error(&output, "Failed");
210    }
211
212    #[test]
213    fn test_assert_content_text() {
214        let content = Content::text("Hello");
215        assert_content_text(&content, "Hello");
216    }
217
218    #[test]
219    fn test_assert_content_contains() {
220        let content = Content::text("Hello, world!");
221        assert_content_contains(&content, "world");
222    }
223}