pulseengine_mcp_protocol/
validation.rs1use crate::{Error, Result};
4use jsonschema::{JSONSchema, ValidationError};
5use serde_json::Value;
6use std::collections::HashMap;
7use uuid::Uuid;
8use validator::Validate;
9
10pub struct Validator;
12
13impl Validator {
14 pub fn validate_uuid(uuid_str: &str) -> Result<Uuid> {
20 uuid_str
21 .parse::<Uuid>()
22 .map_err(|e| Error::validation_error(format!("Invalid UUID: {e}")))
23 }
24
25 pub fn validate_non_empty(value: &str, field_name: &str) -> Result<()> {
31 if value.trim().is_empty() {
32 Err(Error::validation_error(format!(
33 "{field_name} cannot be empty"
34 )))
35 } else {
36 Ok(())
37 }
38 }
39
40 pub fn validate_tool_name(name: &str) -> Result<()> {
46 Self::validate_non_empty(name, "Tool name")?;
47
48 if !name
49 .chars()
50 .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
51 {
52 return Err(Error::validation_error(
53 "Tool name must contain only alphanumeric characters, underscores, and hyphens",
54 ));
55 }
56
57 Ok(())
58 }
59
60 pub fn validate_resource_uri(uri: &str) -> Result<()> {
66 Self::validate_non_empty(uri, "Resource URI")?;
67
68 if uri.chars().any(char::is_control) {
70 return Err(Error::validation_error(
71 "Resource URI cannot contain control characters",
72 ));
73 }
74
75 Ok(())
76 }
77
78 pub fn validate_json_schema(schema: &Value) -> Result<()> {
84 if let Some(obj) = schema.as_object() {
86 if !obj.contains_key("type") {
87 return Err(Error::validation_error(
88 "JSON schema must have a 'type' field",
89 ));
90 }
91 } else {
92 return Err(Error::validation_error("JSON schema must be an object"));
93 }
94
95 Ok(())
96 }
97
98 pub fn validate_tool_arguments(args: &HashMap<String, Value>, schema: &Value) -> Result<()> {
104 if let Some(schema_obj) = schema.as_object() {
106 if let Some(_properties) = schema_obj.get("properties").and_then(|p| p.as_object()) {
107 if let Some(required) = schema_obj.get("required").and_then(|r| r.as_array()) {
108 for req_field in required {
109 if let Some(field_name) = req_field.as_str() {
110 if !args.contains_key(field_name) {
111 return Err(Error::validation_error(format!(
112 "Required argument '{field_name}' is missing"
113 )));
114 }
115 }
116 }
117 }
118 }
119 }
120
121 Ok(())
122 }
123
124 pub fn validate_pagination(cursor: Option<&str>, limit: Option<u32>) -> Result<()> {
130 if let Some(cursor_val) = cursor {
131 Self::validate_non_empty(cursor_val, "Cursor")?;
132 }
133
134 if let Some(limit_val) = limit {
135 if limit_val == 0 {
136 return Err(Error::validation_error("Limit must be greater than 0"));
137 }
138 if limit_val > 1000 {
139 return Err(Error::validation_error("Limit cannot exceed 1000"));
140 }
141 }
142
143 Ok(())
144 }
145
146 pub fn validate_prompt_name(name: &str) -> Result<()> {
152 Self::validate_non_empty(name, "Prompt name")?;
153
154 if !name
155 .chars()
156 .all(|c| c.is_alphanumeric() || c == '_' || c == '-' || c == '.')
157 {
158 return Err(Error::validation_error(
159 "Prompt name must contain only alphanumeric characters, underscores, hyphens, and dots"
160 ));
161 }
162
163 Ok(())
164 }
165
166 pub fn validate_struct<T: Validate>(item: &T) -> Result<()> {
172 item.validate()
173 .map_err(|e| Error::validation_error(e.to_string()))
174 }
175
176 pub fn validate_structured_content(content: &Value, output_schema: &Value) -> Result<()> {
182 Self::validate_json_schema(output_schema)?;
184
185 let schema = JSONSchema::compile(output_schema)
187 .map_err(|e| Error::validation_error(format!("Invalid JSON schema: {e}")))?;
188
189 if let Err(errors) = schema.validate(content) {
191 let error_messages: Vec<String> = errors
192 .map(|e| format!("{}: {}", e.instance_path, e))
193 .collect();
194 return Err(Error::validation_error(format!(
195 "Structured content validation failed: {}",
196 error_messages.join(", ")
197 )));
198 }
199
200 Ok(())
201 }
202
203 pub fn validate_tool_output_schema(output_schema: &Value) -> Result<()> {
209 Self::validate_json_schema(output_schema)?;
211
212 if let Some(obj) = output_schema.as_object() {
214 if let Some(schema_type) = obj.get("type").and_then(|t| t.as_str()) {
216 match schema_type {
217 "object" | "array" => {
218 }
220 "string" | "number" | "integer" | "boolean" | "null" => {
221 return Err(Error::validation_error(
222 "Tool output schema should define structured data (object or array), not primitive types"
223 ));
224 }
225 _ => {
226 return Err(Error::validation_error(
227 "Invalid type specified in tool output schema",
228 ));
229 }
230 }
231 }
232
233 if obj.get("type").and_then(|t| t.as_str()) == Some("object") {
235 if let Some(properties) = obj.get("properties") {
236 if !properties.is_object() {
237 return Err(Error::validation_error(
238 "Object schema properties must be an object",
239 ));
240 }
241 } else {
242 return Err(Error::validation_error(
243 "Object schema must define properties",
244 ));
245 }
246 }
247 }
248
249 Ok(())
250 }
251
252 pub fn format_validation_errors<'a>(
258 errors: impl Iterator<Item = ValidationError<'a>>,
259 ) -> String {
260 let messages: Vec<String> = errors
261 .map(|error| {
262 let path_str = error.instance_path.to_string();
263 if path_str.is_empty() {
264 error.to_string()
265 } else {
266 format!("at '{path_str}': {error}")
267 }
268 })
269 .collect();
270
271 if messages.is_empty() {
272 "Unknown validation error".to_string()
273 } else {
274 messages.join("; ")
275 }
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282 use serde_json::json;
283
284 #[test]
285 fn test_validate_uuid() {
286 let valid_uuid = "550e8400-e29b-41d4-a716-446655440000";
287 assert!(Validator::validate_uuid(valid_uuid).is_ok());
288
289 let invalid_uuid = "not-a-uuid";
290 assert!(Validator::validate_uuid(invalid_uuid).is_err());
291 }
292
293 #[test]
294 fn test_validate_non_empty() {
295 assert!(Validator::validate_non_empty("valid", "field").is_ok());
296 assert!(Validator::validate_non_empty("", "field").is_err());
297 assert!(Validator::validate_non_empty(" ", "field").is_err());
298 }
299
300 #[test]
301 fn test_validate_tool_name() {
302 assert!(Validator::validate_tool_name("valid_tool").is_ok());
303 assert!(Validator::validate_tool_name("tool-name").is_ok());
304 assert!(Validator::validate_tool_name("tool123").is_ok());
305 assert!(Validator::validate_tool_name("").is_err());
306 assert!(Validator::validate_tool_name("invalid tool").is_err());
307 assert!(Validator::validate_tool_name("tool@name").is_err());
308 }
309
310 #[test]
311 fn test_validate_json_schema() {
312 let valid_schema = json!({"type": "object"});
313 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
314
315 let invalid_schema = json!("not an object");
316 assert!(Validator::validate_json_schema(&invalid_schema).is_err());
317
318 let no_type_schema = json!({"properties": {}});
319 assert!(Validator::validate_json_schema(&no_type_schema).is_err());
320 }
321
322 #[test]
323 fn test_validate_pagination() {
324 assert!(Validator::validate_pagination(None, None).is_ok());
325 assert!(Validator::validate_pagination(Some("cursor"), Some(10)).is_ok());
326 assert!(Validator::validate_pagination(Some(""), None).is_err());
327 assert!(Validator::validate_pagination(None, Some(0)).is_err());
328 assert!(Validator::validate_pagination(None, Some(1001)).is_err());
329 }
330
331 #[test]
332 fn test_validate_resource_uri() {
333 assert!(Validator::validate_resource_uri("http://example.com/resource").is_ok());
335 assert!(Validator::validate_resource_uri("file:///path/to/resource").is_ok());
336 assert!(Validator::validate_resource_uri("custom://protocol/resource").is_ok());
337
338 assert!(Validator::validate_resource_uri("").is_err());
340 assert!(Validator::validate_resource_uri(" ").is_err());
341 assert!(Validator::validate_resource_uri("uri\nwith\nnewlines").is_err());
342 assert!(Validator::validate_resource_uri("uri\twith\ttabs").is_err());
343 assert!(Validator::validate_resource_uri("uri\rwith\rcarriage\rreturns").is_err());
344 }
345
346 #[test]
347 fn test_validate_prompt_name() {
348 assert!(Validator::validate_prompt_name("valid_prompt").is_ok());
350 assert!(Validator::validate_prompt_name("prompt-name").is_ok());
351 assert!(Validator::validate_prompt_name("prompt.name").is_ok());
352 assert!(Validator::validate_prompt_name("prompt123").is_ok());
353 assert!(Validator::validate_prompt_name("Prompt_Name-123.test").is_ok());
354
355 assert!(Validator::validate_prompt_name("").is_err());
357 assert!(Validator::validate_prompt_name(" ").is_err());
358 assert!(Validator::validate_prompt_name("prompt name").is_err());
359 assert!(Validator::validate_prompt_name("prompt@name").is_err());
360 assert!(Validator::validate_prompt_name("prompt/name").is_err());
361 assert!(Validator::validate_prompt_name("prompt:name").is_err());
362 }
363
364 #[test]
365 fn test_validate_tool_arguments() {
366 let schema = json!({
368 "type": "object",
369 "properties": {
370 "name": {"type": "string"},
371 "age": {"type": "number"}
372 }
373 });
374 let args = HashMap::new();
375 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
376
377 let schema = json!({
379 "type": "object",
380 "properties": {
381 "name": {"type": "string"},
382 "age": {"type": "number"}
383 },
384 "required": ["name"]
385 });
386 let mut args = HashMap::new();
387 args.insert("name".to_string(), json!("John"));
388 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
389
390 let args = HashMap::new();
392 let result = Validator::validate_tool_arguments(&args, &schema);
393 assert!(result.is_err());
394 assert!(result
395 .unwrap_err()
396 .message
397 .contains("Required argument 'name' is missing"));
398
399 let schema = json!({
401 "type": "object",
402 "properties": {
403 "name": {"type": "string"},
404 "age": {"type": "number"},
405 "email": {"type": "string"}
406 },
407 "required": ["name", "email"]
408 });
409 let mut args = HashMap::new();
410 args.insert("name".to_string(), json!("John"));
411 args.insert("email".to_string(), json!("john@example.com"));
412 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
413
414 let mut args = HashMap::new();
416 args.insert("name".to_string(), json!("John"));
417 let result = Validator::validate_tool_arguments(&args, &schema);
418 assert!(result.is_err());
419 assert!(result
420 .unwrap_err()
421 .message
422 .contains("Required argument 'email' is missing"));
423
424 let schema = json!({
426 "type": "object"
427 });
428 let args = HashMap::new();
429 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
430
431 let schema = json!({
433 "type": "object",
434 "properties": {
435 "name": {"type": "string"}
436 },
437 "required": []
438 });
439 let args = HashMap::new();
440 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
441
442 let schema = json!({
444 "type": "object",
445 "properties": {
446 "name": {"type": "string"}
447 },
448 "required": [123]
449 });
450 let args = HashMap::new();
451 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
452 }
453
454 #[test]
455 fn test_validate_structured_content() {
456 let content = json!({
458 "name": "John Doe",
459 "age": 30,
460 "email": "john@example.com"
461 });
462 let schema = json!({
463 "type": "object",
464 "properties": {
465 "name": {"type": "string"},
466 "age": {"type": "integer", "minimum": 0},
467 "email": {"type": "string", "format": "email"}
468 },
469 "required": ["name", "age"]
470 });
471
472 assert!(Validator::validate_structured_content(&content, &schema).is_ok());
473
474 let invalid_content = json!({
476 "name": "John Doe"
477 });
478 let result = Validator::validate_structured_content(&invalid_content, &schema);
479 assert!(result.is_err());
480 assert!(result.unwrap_err().message.contains("validation failed"));
481
482 let invalid_content = json!({
484 "name": "John Doe",
485 "age": "thirty"
486 });
487 let result = Validator::validate_structured_content(&invalid_content, &schema);
488 assert!(result.is_err());
489
490 let invalid_schema = json!({
492 "type": "invalid_type"
493 });
494 let result = Validator::validate_structured_content(&content, &invalid_schema);
495 assert!(result.is_err());
496 let error_msg = result.unwrap_err().message;
498 assert!(error_msg.contains("JSON schema") || error_msg.contains("Invalid"));
499 }
500
501 #[test]
502 fn test_validate_tool_output_schema() {
503 let valid_object_schema = json!({
505 "type": "object",
506 "properties": {
507 "result": {"type": "string"},
508 "metadata": {"type": "object"}
509 }
510 });
511 assert!(Validator::validate_tool_output_schema(&valid_object_schema).is_ok());
512
513 let valid_array_schema = json!({
515 "type": "array",
516 "items": {"type": "string"}
517 });
518 assert!(Validator::validate_tool_output_schema(&valid_array_schema).is_ok());
519
520 let invalid_primitive_schema = json!({
522 "type": "string"
523 });
524 let result = Validator::validate_tool_output_schema(&invalid_primitive_schema);
525 assert!(result.is_err());
526 assert!(result
527 .unwrap_err()
528 .message
529 .contains("should define structured data"));
530
531 let invalid_object_schema = json!({
533 "type": "object"
534 });
535 let result = Validator::validate_tool_output_schema(&invalid_object_schema);
536 assert!(result.is_err());
537 assert!(result
538 .unwrap_err()
539 .message
540 .contains("must define properties"));
541
542 let invalid_props_schema = json!({
544 "type": "object",
545 "properties": "not an object"
546 });
547 let result = Validator::validate_tool_output_schema(&invalid_props_schema);
548 assert!(result.is_err());
549 assert!(result
550 .unwrap_err()
551 .message
552 .contains("properties must be an object"));
553
554 let no_type_schema = json!({
556 "properties": {}
557 });
558 let result = Validator::validate_tool_output_schema(&no_type_schema);
559 assert!(result.is_err());
560 assert!(result
561 .unwrap_err()
562 .message
563 .contains("JSON schema must have a 'type' field"));
564 }
565
566 #[test]
567 fn test_structured_content_with_arrays() {
568 let content = json!([
570 {"id": 1, "name": "Item 1"},
571 {"id": 2, "name": "Item 2"}
572 ]);
573 let schema = json!({
574 "type": "array",
575 "items": {
576 "type": "object",
577 "properties": {
578 "id": {"type": "integer"},
579 "name": {"type": "string"}
580 },
581 "required": ["id", "name"]
582 }
583 });
584
585 assert!(Validator::validate_structured_content(&content, &schema).is_ok());
586
587 let invalid_content = json!([
589 {"id": 1, "name": "Item 1"},
590 {"id": "not a number", "name": "Item 2"}
591 ]);
592 let result = Validator::validate_structured_content(&invalid_content, &schema);
593 assert!(result.is_err());
594 }
595
596 #[test]
597 fn test_nested_structured_content() {
598 let content = json!({
600 "user": {
601 "name": "John",
602 "profile": {
603 "age": 30,
604 "preferences": ["reading", "coding"]
605 }
606 },
607 "timestamp": "2023-01-01T00:00:00Z"
608 });
609
610 let schema = json!({
611 "type": "object",
612 "properties": {
613 "user": {
614 "type": "object",
615 "properties": {
616 "name": {"type": "string"},
617 "profile": {
618 "type": "object",
619 "properties": {
620 "age": {"type": "integer"},
621 "preferences": {
622 "type": "array",
623 "items": {"type": "string"}
624 }
625 },
626 "required": ["age"]
627 }
628 },
629 "required": ["name", "profile"]
630 },
631 "timestamp": {"type": "string"}
632 },
633 "required": ["user"]
634 });
635
636 assert!(Validator::validate_structured_content(&content, &schema).is_ok());
637
638 let invalid_content = json!({
640 "user": {
641 "name": "John",
642 "profile": {
643 "preferences": ["reading", "coding"]
644 }
646 }
647 });
648 let result = Validator::validate_structured_content(&invalid_content, &schema);
649 assert!(result.is_err());
650 }
651
652 #[test]
653 fn test_format_validation_errors() {
654 let empty_errors = std::iter::empty();
657 let result = Validator::format_validation_errors(empty_errors);
658 assert_eq!(result, "Unknown validation error");
659 }
660
661 #[test]
662 fn test_call_tool_result_structured_validation() {
663 use crate::model::{CallToolResult, Content};
664
665 let structured_data = json!({
667 "result": "success",
668 "data": {"count": 42}
669 });
670 let schema = json!({
671 "type": "object",
672 "properties": {
673 "result": {"type": "string"},
674 "data": {"type": "object"}
675 },
676 "required": ["result"]
677 });
678
679 let result =
680 CallToolResult::structured(vec![Content::text("Operation completed")], structured_data);
681
682 assert!(result.validate_structured_content(&schema).is_ok());
683
684 let invalid_data = json!({
686 "result": 123 });
688 let invalid_result =
689 CallToolResult::structured(vec![Content::text("Operation completed")], invalid_data);
690
691 assert!(invalid_result.validate_structured_content(&schema).is_err());
692
693 let simple_result = CallToolResult::text("Simple result");
695 assert!(simple_result.validate_structured_content(&schema).is_ok());
696 }
697
698 #[test]
699 fn test_validate_uuid_edge_cases() {
700 assert!(Validator::validate_uuid("550e8400-e29b-41d4-a716-446655440000").is_ok());
702 assert!(Validator::validate_uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8").is_ok());
703 assert!(Validator::validate_uuid("123e4567-e89b-12d3-a456-426614174000").is_ok());
704
705 assert!(Validator::validate_uuid("550e8400-e29b-41d4-a716-44665544000").is_err()); assert!(Validator::validate_uuid("550e8400-e29b-41d4-a716-4466554400000").is_err()); assert!(Validator::validate_uuid("550e8400-e29b-41d4-a716-44665544000g").is_err()); assert!(Validator::validate_uuid("550e8400e29b41d4a716446655440000").is_ok()); assert!(Validator::validate_uuid("").is_err()); assert!(Validator::validate_uuid("not-a-uuid-at-all").is_err()); }
713
714 #[test]
715 fn test_validate_non_empty_edge_cases() {
716 assert!(Validator::validate_non_empty("valid", "field").is_ok());
718 assert!(Validator::validate_non_empty("a", "field").is_ok());
719 assert!(Validator::validate_non_empty("123", "field").is_ok());
720 assert!(Validator::validate_non_empty("special!@#$%^&*()", "field").is_ok());
721 assert!(Validator::validate_non_empty(" text ", "field").is_ok()); let result = Validator::validate_non_empty("", "field");
725 assert!(result.is_err());
726 assert!(result
727 .unwrap_err()
728 .message
729 .contains("field cannot be empty"));
730
731 let result = Validator::validate_non_empty(" ", "field");
732 assert!(result.is_err());
733 assert!(result
734 .unwrap_err()
735 .message
736 .contains("field cannot be empty"));
737
738 let result = Validator::validate_non_empty("\t\n\r", "field");
739 assert!(result.is_err());
740 assert!(result
741 .unwrap_err()
742 .message
743 .contains("field cannot be empty"));
744
745 let result = Validator::validate_non_empty("", "tool_name");
747 assert!(result.is_err());
748 assert!(result
749 .unwrap_err()
750 .message
751 .contains("tool_name cannot be empty"));
752 }
753
754 #[test]
755 fn test_validate_tool_name_edge_cases() {
756 assert!(Validator::validate_tool_name("a").is_ok());
758 assert!(Validator::validate_tool_name("tool").is_ok());
759 assert!(Validator::validate_tool_name("tool_name").is_ok());
760 assert!(Validator::validate_tool_name("tool-name").is_ok());
761 assert!(Validator::validate_tool_name("tool123").is_ok());
762 assert!(Validator::validate_tool_name("123tool").is_ok());
763 assert!(Validator::validate_tool_name("Tool_Name-123").is_ok());
764 assert!(Validator::validate_tool_name("_tool").is_ok());
765 assert!(Validator::validate_tool_name("tool_").is_ok());
766 assert!(Validator::validate_tool_name("-tool").is_ok());
767 assert!(Validator::validate_tool_name("tool-").is_ok());
768
769 let result = Validator::validate_tool_name("");
771 assert!(result.is_err());
772 assert!(result
773 .unwrap_err()
774 .message
775 .contains("Tool name cannot be empty"));
776
777 let result = Validator::validate_tool_name(" ");
778 assert!(result.is_err());
779 assert!(result
780 .unwrap_err()
781 .message
782 .contains("Tool name cannot be empty"));
783
784 let result = Validator::validate_tool_name("tool name");
785 assert!(result.is_err());
786 assert!(result.unwrap_err().message.contains(
787 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
788 ));
789
790 let result = Validator::validate_tool_name("tool.name");
791 assert!(result.is_err());
792 assert!(result.unwrap_err().message.contains(
793 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
794 ));
795
796 let result = Validator::validate_tool_name("tool@name");
797 assert!(result.is_err());
798 assert!(result.unwrap_err().message.contains(
799 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
800 ));
801
802 let result = Validator::validate_tool_name("tool/name");
803 assert!(result.is_err());
804 assert!(result.unwrap_err().message.contains(
805 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
806 ));
807 }
808
809 #[test]
810 fn test_validate_json_schema_edge_cases() {
811 let valid_schema = json!({"type": "object"});
813 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
814
815 let valid_schema = json!({
816 "type": "object",
817 "properties": {
818 "name": {"type": "string"}
819 }
820 });
821 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
822
823 let valid_schema = json!({
824 "type": "string",
825 "minLength": 1
826 });
827 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
828
829 let result = Validator::validate_json_schema(&json!("not an object"));
831 assert!(result.is_err());
832 assert!(result
833 .unwrap_err()
834 .message
835 .contains("JSON schema must be an object"));
836
837 let result = Validator::validate_json_schema(&json!(123));
838 assert!(result.is_err());
839 assert!(result
840 .unwrap_err()
841 .message
842 .contains("JSON schema must be an object"));
843
844 let result = Validator::validate_json_schema(&json!([]));
845 assert!(result.is_err());
846 assert!(result
847 .unwrap_err()
848 .message
849 .contains("JSON schema must be an object"));
850
851 let result = Validator::validate_json_schema(&json!(null));
852 assert!(result.is_err());
853 assert!(result
854 .unwrap_err()
855 .message
856 .contains("JSON schema must be an object"));
857
858 let result = Validator::validate_json_schema(&json!({"properties": {}}));
859 assert!(result.is_err());
860 assert!(result
861 .unwrap_err()
862 .message
863 .contains("JSON schema must have a 'type' field"));
864
865 let result = Validator::validate_json_schema(&json!({}));
866 assert!(result.is_err());
867 assert!(result
868 .unwrap_err()
869 .message
870 .contains("JSON schema must have a 'type' field"));
871 }
872
873 #[test]
874 fn test_validate_pagination_edge_cases() {
875 assert!(Validator::validate_pagination(None, None).is_ok());
877 assert!(Validator::validate_pagination(Some("cursor"), None).is_ok());
878 assert!(Validator::validate_pagination(None, Some(1)).is_ok());
879 assert!(Validator::validate_pagination(Some("cursor"), Some(1)).is_ok());
880 assert!(Validator::validate_pagination(Some("cursor"), Some(1000)).is_ok());
881 assert!(Validator::validate_pagination(
882 Some("very-long-cursor-value-that-should-still-be-valid"),
883 Some(500)
884 )
885 .is_ok());
886
887 let result = Validator::validate_pagination(Some(""), None);
889 assert!(result.is_err());
890 assert!(result
891 .unwrap_err()
892 .message
893 .contains("Cursor cannot be empty"));
894
895 let result = Validator::validate_pagination(Some(" "), None);
896 assert!(result.is_err());
897 assert!(result
898 .unwrap_err()
899 .message
900 .contains("Cursor cannot be empty"));
901
902 let result = Validator::validate_pagination(Some("\t\n\r"), None);
903 assert!(result.is_err());
904 assert!(result
905 .unwrap_err()
906 .message
907 .contains("Cursor cannot be empty"));
908
909 let result = Validator::validate_pagination(None, Some(0));
911 assert!(result.is_err());
912 assert!(result
913 .unwrap_err()
914 .message
915 .contains("Limit must be greater than 0"));
916
917 let result = Validator::validate_pagination(None, Some(1001));
918 assert!(result.is_err());
919 assert!(result
920 .unwrap_err()
921 .message
922 .contains("Limit cannot exceed 1000"));
923
924 let result = Validator::validate_pagination(None, Some(u32::MAX));
925 assert!(result.is_err());
926 assert!(result
927 .unwrap_err()
928 .message
929 .contains("Limit cannot exceed 1000"));
930
931 let result = Validator::validate_pagination(Some(""), Some(0));
933 assert!(result.is_err());
934 assert!(result
936 .unwrap_err()
937 .message
938 .contains("Cursor cannot be empty"));
939
940 let result = Validator::validate_pagination(Some("valid-cursor"), Some(0));
941 assert!(result.is_err());
942 assert!(result
943 .unwrap_err()
944 .message
945 .contains("Limit must be greater than 0"));
946 }
947}