nika_engine/runtime/builtin/
assert.rs1use super::BuiltinTool;
26use crate::error::NikaError;
27use serde::{Deserialize, Serialize};
28use std::future::Future;
29use std::pin::Pin;
30
31#[derive(Debug, Clone, Deserialize)]
33struct AssertParams {
34 condition: bool,
36 #[serde(default)]
38 message: Option<String>,
39}
40
41#[derive(Debug, Clone, Serialize)]
43struct AssertResponse {
44 passed: bool,
46 condition: bool,
48}
49
50pub struct AssertTool;
55
56impl BuiltinTool for AssertTool {
57 fn name(&self) -> &'static str {
58 "assert"
59 }
60
61 fn description(&self) -> &'static str {
62 "Validate condition, fail workflow if false"
63 }
64
65 fn parameters_schema(&self) -> serde_json::Value {
66 serde_json::json!({
68 "type": "object",
69 "properties": {
70 "condition": {
71 "type": "boolean",
72 "description": "Boolean condition to assert (must be true)"
73 },
74 "message": {
75 "type": "string",
76 "description": "Error message if assertion fails"
77 }
78 },
79 "required": ["condition", "message"],
80 "additionalProperties": false
81 })
82 }
83
84 fn call<'a>(
85 &'a self,
86 args: String,
87 ) -> Pin<Box<dyn Future<Output = Result<String, NikaError>> + Send + 'a>> {
88 Box::pin(async move {
89 let params: AssertParams =
91 serde_json::from_str(&args).map_err(|e| NikaError::BuiltinInvalidParams {
92 tool: "nika:assert".into(),
93 reason: format!("Invalid JSON parameters: {}", e),
94 })?;
95
96 if !params.condition {
98 let message = params
99 .message
100 .unwrap_or_else(|| "Assertion failed".to_string());
101 return Err(NikaError::AssertionFailed {
102 message,
103 condition: "false".to_string(),
104 });
105 }
106
107 let response = AssertResponse {
109 passed: true,
110 condition: params.condition,
111 };
112
113 serde_json::to_string(&response).map_err(|e| NikaError::BuiltinToolError {
114 tool: "nika:assert".into(),
115 reason: format!("Failed to serialize response: {}", e),
116 })
117 })
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_assert_tool_name() {
127 let tool = AssertTool;
128 assert_eq!(tool.name(), "assert");
129 }
130
131 #[test]
132 fn test_assert_tool_description() {
133 let tool = AssertTool;
134 assert!(tool.description().contains("condition"));
135 }
136
137 #[test]
138 fn test_assert_tool_schema() {
139 let tool = AssertTool;
140 let schema = tool.parameters_schema();
141 assert_eq!(schema["type"], "object");
142 assert!(schema["properties"]["condition"].is_object());
143 assert!(schema["properties"]["message"].is_object());
144 assert!(schema["required"]
145 .as_array()
146 .unwrap()
147 .contains(&serde_json::json!("condition")));
148 }
149
150 #[tokio::test]
151 async fn test_assert_true_passes() {
152 let tool = AssertTool;
153 let result = tool.call(r#"{"condition": true}"#.to_string()).await;
154
155 assert!(result.is_ok());
156 let response: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap();
157 assert_eq!(response["passed"], true);
158 assert_eq!(response["condition"], true);
159 }
160
161 #[tokio::test]
162 async fn test_assert_false_fails() {
163 let tool = AssertTool;
164 let result = tool.call(r#"{"condition": false}"#.to_string()).await;
165
166 assert!(result.is_err());
167 let err = result.unwrap_err();
168 assert!(err.to_string().contains("Assertion failed"));
169 }
170
171 #[tokio::test]
172 async fn test_assert_false_with_message() {
173 let tool = AssertTool;
174 let result = tool
175 .call(r#"{"condition": false, "message": "Expected X to equal Y"}"#.to_string())
176 .await;
177
178 assert!(result.is_err());
179 let err = result.unwrap_err();
180 assert!(err.to_string().contains("Expected X to equal Y"));
181 }
182
183 #[tokio::test]
184 async fn test_assert_true_with_message_still_passes() {
185 let tool = AssertTool;
186 let result = tool
187 .call(r#"{"condition": true, "message": "This should not appear"}"#.to_string())
188 .await;
189
190 assert!(result.is_ok());
191 }
192
193 #[tokio::test]
194 async fn test_assert_invalid_json() {
195 let tool = AssertTool;
196 let result = tool.call("not json".to_string()).await;
197
198 assert!(result.is_err());
199 let err = result.unwrap_err();
200 assert!(err.to_string().contains("Invalid JSON parameters"));
201 }
202
203 #[tokio::test]
204 async fn test_assert_missing_condition() {
205 let tool = AssertTool;
206 let result = tool.call(r#"{"message": "test"}"#.to_string()).await;
207
208 assert!(result.is_err());
209 let err = result.unwrap_err();
210 assert!(err.to_string().contains("Invalid JSON parameters"));
211 }
212
213 #[tokio::test]
214 async fn test_assert_wrong_condition_type() {
215 let tool = AssertTool;
216 let result = tool.call(r#"{"condition": "true"}"#.to_string()).await;
217
218 assert!(result.is_err());
219 let err = result.unwrap_err();
220 assert!(err.to_string().contains("Invalid JSON parameters"));
221 }
222
223 #[tokio::test]
224 async fn test_assert_null_condition() {
225 let tool = AssertTool;
226 let result = tool.call(r#"{"condition": null}"#.to_string()).await;
227
228 assert!(result.is_err());
229 let err = result.unwrap_err();
230 assert!(err.to_string().contains("Invalid JSON parameters"));
231 }
232
233 #[tokio::test]
234 async fn test_assert_error_code() {
235 let tool = AssertTool;
236 let result = tool.call(r#"{"condition": false}"#.to_string()).await;
237
238 assert!(result.is_err());
239 let err = result.unwrap_err();
240 assert!(err.to_string().contains("NIKA-213"));
242 }
243}