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