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!(
395 result
396 .unwrap_err()
397 .message
398 .contains("Required argument 'name' is missing")
399 );
400
401 let schema = json!({
403 "type": "object",
404 "properties": {
405 "name": {"type": "string"},
406 "age": {"type": "number"},
407 "email": {"type": "string"}
408 },
409 "required": ["name", "email"]
410 });
411 let mut args = HashMap::new();
412 args.insert("name".to_string(), json!("John"));
413 args.insert("email".to_string(), json!("john@example.com"));
414 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
415
416 let mut args = HashMap::new();
418 args.insert("name".to_string(), json!("John"));
419 let result = Validator::validate_tool_arguments(&args, &schema);
420 assert!(result.is_err());
421 assert!(
422 result
423 .unwrap_err()
424 .message
425 .contains("Required argument 'email' is missing")
426 );
427
428 let schema = json!({
430 "type": "object"
431 });
432 let args = HashMap::new();
433 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
434
435 let schema = json!({
437 "type": "object",
438 "properties": {
439 "name": {"type": "string"}
440 },
441 "required": []
442 });
443 let args = HashMap::new();
444 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
445
446 let schema = json!({
448 "type": "object",
449 "properties": {
450 "name": {"type": "string"}
451 },
452 "required": [123]
453 });
454 let args = HashMap::new();
455 assert!(Validator::validate_tool_arguments(&args, &schema).is_ok());
456 }
457
458 #[test]
459 fn test_validate_structured_content() {
460 let content = json!({
462 "name": "John Doe",
463 "age": 30,
464 "email": "john@example.com"
465 });
466 let schema = json!({
467 "type": "object",
468 "properties": {
469 "name": {"type": "string"},
470 "age": {"type": "integer", "minimum": 0},
471 "email": {"type": "string", "format": "email"}
472 },
473 "required": ["name", "age"]
474 });
475
476 assert!(Validator::validate_structured_content(&content, &schema).is_ok());
477
478 let invalid_content = json!({
480 "name": "John Doe"
481 });
482 let result = Validator::validate_structured_content(&invalid_content, &schema);
483 assert!(result.is_err());
484 assert!(result.unwrap_err().message.contains("validation failed"));
485
486 let invalid_content = json!({
488 "name": "John Doe",
489 "age": "thirty"
490 });
491 let result = Validator::validate_structured_content(&invalid_content, &schema);
492 assert!(result.is_err());
493
494 let invalid_schema = json!({
496 "type": "invalid_type"
497 });
498 let result = Validator::validate_structured_content(&content, &invalid_schema);
499 assert!(result.is_err());
500 let error_msg = result.unwrap_err().message;
502 assert!(error_msg.contains("JSON schema") || error_msg.contains("Invalid"));
503 }
504
505 #[test]
506 fn test_validate_tool_output_schema() {
507 let valid_object_schema = json!({
509 "type": "object",
510 "properties": {
511 "result": {"type": "string"},
512 "metadata": {"type": "object"}
513 }
514 });
515 assert!(Validator::validate_tool_output_schema(&valid_object_schema).is_ok());
516
517 let valid_array_schema = json!({
519 "type": "array",
520 "items": {"type": "string"}
521 });
522 assert!(Validator::validate_tool_output_schema(&valid_array_schema).is_ok());
523
524 let invalid_primitive_schema = json!({
526 "type": "string"
527 });
528 let result = Validator::validate_tool_output_schema(&invalid_primitive_schema);
529 assert!(result.is_err());
530 assert!(
531 result
532 .unwrap_err()
533 .message
534 .contains("should define structured data")
535 );
536
537 let invalid_object_schema = json!({
539 "type": "object"
540 });
541 let result = Validator::validate_tool_output_schema(&invalid_object_schema);
542 assert!(result.is_err());
543 assert!(
544 result
545 .unwrap_err()
546 .message
547 .contains("must define properties")
548 );
549
550 let invalid_props_schema = json!({
552 "type": "object",
553 "properties": "not an object"
554 });
555 let result = Validator::validate_tool_output_schema(&invalid_props_schema);
556 assert!(result.is_err());
557 assert!(
558 result
559 .unwrap_err()
560 .message
561 .contains("properties must be an object")
562 );
563
564 let no_type_schema = json!({
566 "properties": {}
567 });
568 let result = Validator::validate_tool_output_schema(&no_type_schema);
569 assert!(result.is_err());
570 assert!(
571 result
572 .unwrap_err()
573 .message
574 .contains("JSON schema must have a 'type' field")
575 );
576 }
577
578 #[test]
579 fn test_structured_content_with_arrays() {
580 let content = json!([
582 {"id": 1, "name": "Item 1"},
583 {"id": 2, "name": "Item 2"}
584 ]);
585 let schema = json!({
586 "type": "array",
587 "items": {
588 "type": "object",
589 "properties": {
590 "id": {"type": "integer"},
591 "name": {"type": "string"}
592 },
593 "required": ["id", "name"]
594 }
595 });
596
597 assert!(Validator::validate_structured_content(&content, &schema).is_ok());
598
599 let invalid_content = json!([
601 {"id": 1, "name": "Item 1"},
602 {"id": "not a number", "name": "Item 2"}
603 ]);
604 let result = Validator::validate_structured_content(&invalid_content, &schema);
605 assert!(result.is_err());
606 }
607
608 #[test]
609 fn test_nested_structured_content() {
610 let content = json!({
612 "user": {
613 "name": "John",
614 "profile": {
615 "age": 30,
616 "preferences": ["reading", "coding"]
617 }
618 },
619 "timestamp": "2023-01-01T00:00:00Z"
620 });
621
622 let schema = json!({
623 "type": "object",
624 "properties": {
625 "user": {
626 "type": "object",
627 "properties": {
628 "name": {"type": "string"},
629 "profile": {
630 "type": "object",
631 "properties": {
632 "age": {"type": "integer"},
633 "preferences": {
634 "type": "array",
635 "items": {"type": "string"}
636 }
637 },
638 "required": ["age"]
639 }
640 },
641 "required": ["name", "profile"]
642 },
643 "timestamp": {"type": "string"}
644 },
645 "required": ["user"]
646 });
647
648 assert!(Validator::validate_structured_content(&content, &schema).is_ok());
649
650 let invalid_content = json!({
652 "user": {
653 "name": "John",
654 "profile": {
655 "preferences": ["reading", "coding"]
656 }
658 }
659 });
660 let result = Validator::validate_structured_content(&invalid_content, &schema);
661 assert!(result.is_err());
662 }
663
664 #[test]
665 fn test_format_validation_errors() {
666 let empty_errors = std::iter::empty();
669 let result = Validator::format_validation_errors(empty_errors);
670 assert_eq!(result, "Unknown validation error");
671 }
672
673 #[test]
674 fn test_call_tool_result_structured_validation() {
675 use crate::model::{CallToolResult, Content};
676
677 let structured_data = json!({
679 "result": "success",
680 "data": {"count": 42}
681 });
682 let schema = json!({
683 "type": "object",
684 "properties": {
685 "result": {"type": "string"},
686 "data": {"type": "object"}
687 },
688 "required": ["result"]
689 });
690
691 let result =
692 CallToolResult::structured(vec![Content::text("Operation completed")], structured_data);
693
694 assert!(result.validate_structured_content(&schema).is_ok());
695
696 let invalid_data = json!({
698 "result": 123 });
700 let invalid_result =
701 CallToolResult::structured(vec![Content::text("Operation completed")], invalid_data);
702
703 assert!(invalid_result.validate_structured_content(&schema).is_err());
704
705 let simple_result = CallToolResult::text("Simple result");
707 assert!(simple_result.validate_structured_content(&schema).is_ok());
708 }
709
710 #[test]
711 fn test_validate_uuid_edge_cases() {
712 assert!(Validator::validate_uuid("550e8400-e29b-41d4-a716-446655440000").is_ok());
714 assert!(Validator::validate_uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8").is_ok());
715 assert!(Validator::validate_uuid("123e4567-e89b-12d3-a456-426614174000").is_ok());
716
717 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()); }
725
726 #[test]
727 fn test_validate_non_empty_edge_cases() {
728 assert!(Validator::validate_non_empty("valid", "field").is_ok());
730 assert!(Validator::validate_non_empty("a", "field").is_ok());
731 assert!(Validator::validate_non_empty("123", "field").is_ok());
732 assert!(Validator::validate_non_empty("special!@#$%^&*()", "field").is_ok());
733 assert!(Validator::validate_non_empty(" text ", "field").is_ok()); let result = Validator::validate_non_empty("", "field");
737 assert!(result.is_err());
738 assert!(
739 result
740 .unwrap_err()
741 .message
742 .contains("field cannot be empty")
743 );
744
745 let result = Validator::validate_non_empty(" ", "field");
746 assert!(result.is_err());
747 assert!(
748 result
749 .unwrap_err()
750 .message
751 .contains("field cannot be empty")
752 );
753
754 let result = Validator::validate_non_empty("\t\n\r", "field");
755 assert!(result.is_err());
756 assert!(
757 result
758 .unwrap_err()
759 .message
760 .contains("field cannot be empty")
761 );
762
763 let result = Validator::validate_non_empty("", "tool_name");
765 assert!(result.is_err());
766 assert!(
767 result
768 .unwrap_err()
769 .message
770 .contains("tool_name cannot be empty")
771 );
772 }
773
774 #[test]
775 fn test_validate_tool_name_edge_cases() {
776 assert!(Validator::validate_tool_name("a").is_ok());
778 assert!(Validator::validate_tool_name("tool").is_ok());
779 assert!(Validator::validate_tool_name("tool_name").is_ok());
780 assert!(Validator::validate_tool_name("tool-name").is_ok());
781 assert!(Validator::validate_tool_name("tool123").is_ok());
782 assert!(Validator::validate_tool_name("123tool").is_ok());
783 assert!(Validator::validate_tool_name("Tool_Name-123").is_ok());
784 assert!(Validator::validate_tool_name("_tool").is_ok());
785 assert!(Validator::validate_tool_name("tool_").is_ok());
786 assert!(Validator::validate_tool_name("-tool").is_ok());
787 assert!(Validator::validate_tool_name("tool-").is_ok());
788
789 let result = Validator::validate_tool_name("");
791 assert!(result.is_err());
792 assert!(
793 result
794 .unwrap_err()
795 .message
796 .contains("Tool name cannot be empty")
797 );
798
799 let result = Validator::validate_tool_name(" ");
800 assert!(result.is_err());
801 assert!(
802 result
803 .unwrap_err()
804 .message
805 .contains("Tool name cannot be empty")
806 );
807
808 let result = Validator::validate_tool_name("tool name");
809 assert!(result.is_err());
810 assert!(result.unwrap_err().message.contains(
811 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
812 ));
813
814 let result = Validator::validate_tool_name("tool.name");
815 assert!(result.is_err());
816 assert!(result.unwrap_err().message.contains(
817 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
818 ));
819
820 let result = Validator::validate_tool_name("tool@name");
821 assert!(result.is_err());
822 assert!(result.unwrap_err().message.contains(
823 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
824 ));
825
826 let result = Validator::validate_tool_name("tool/name");
827 assert!(result.is_err());
828 assert!(result.unwrap_err().message.contains(
829 "Tool name must contain only alphanumeric characters, underscores, and hyphens"
830 ));
831 }
832
833 #[test]
834 fn test_validate_json_schema_edge_cases() {
835 let valid_schema = json!({"type": "object"});
837 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
838
839 let valid_schema = json!({
840 "type": "object",
841 "properties": {
842 "name": {"type": "string"}
843 }
844 });
845 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
846
847 let valid_schema = json!({
848 "type": "string",
849 "minLength": 1
850 });
851 assert!(Validator::validate_json_schema(&valid_schema).is_ok());
852
853 let result = Validator::validate_json_schema(&json!("not an object"));
855 assert!(result.is_err());
856 assert!(
857 result
858 .unwrap_err()
859 .message
860 .contains("JSON schema must be an object")
861 );
862
863 let result = Validator::validate_json_schema(&json!(123));
864 assert!(result.is_err());
865 assert!(
866 result
867 .unwrap_err()
868 .message
869 .contains("JSON schema must be an object")
870 );
871
872 let result = Validator::validate_json_schema(&json!([]));
873 assert!(result.is_err());
874 assert!(
875 result
876 .unwrap_err()
877 .message
878 .contains("JSON schema must be an object")
879 );
880
881 let result = Validator::validate_json_schema(&json!(null));
882 assert!(result.is_err());
883 assert!(
884 result
885 .unwrap_err()
886 .message
887 .contains("JSON schema must be an object")
888 );
889
890 let result = Validator::validate_json_schema(&json!({"properties": {}}));
891 assert!(result.is_err());
892 assert!(
893 result
894 .unwrap_err()
895 .message
896 .contains("JSON schema must have a 'type' field")
897 );
898
899 let result = Validator::validate_json_schema(&json!({}));
900 assert!(result.is_err());
901 assert!(
902 result
903 .unwrap_err()
904 .message
905 .contains("JSON schema must have a 'type' field")
906 );
907 }
908
909 #[test]
910 fn test_validate_pagination_edge_cases() {
911 assert!(Validator::validate_pagination(None, None).is_ok());
913 assert!(Validator::validate_pagination(Some("cursor"), None).is_ok());
914 assert!(Validator::validate_pagination(None, Some(1)).is_ok());
915 assert!(Validator::validate_pagination(Some("cursor"), Some(1)).is_ok());
916 assert!(Validator::validate_pagination(Some("cursor"), Some(1000)).is_ok());
917 assert!(
918 Validator::validate_pagination(
919 Some("very-long-cursor-value-that-should-still-be-valid"),
920 Some(500)
921 )
922 .is_ok()
923 );
924
925 let result = Validator::validate_pagination(Some(""), None);
927 assert!(result.is_err());
928 assert!(
929 result
930 .unwrap_err()
931 .message
932 .contains("Cursor cannot be empty")
933 );
934
935 let result = Validator::validate_pagination(Some(" "), None);
936 assert!(result.is_err());
937 assert!(
938 result
939 .unwrap_err()
940 .message
941 .contains("Cursor cannot be empty")
942 );
943
944 let result = Validator::validate_pagination(Some("\t\n\r"), None);
945 assert!(result.is_err());
946 assert!(
947 result
948 .unwrap_err()
949 .message
950 .contains("Cursor cannot be empty")
951 );
952
953 let result = Validator::validate_pagination(None, Some(0));
955 assert!(result.is_err());
956 assert!(
957 result
958 .unwrap_err()
959 .message
960 .contains("Limit must be greater than 0")
961 );
962
963 let result = Validator::validate_pagination(None, Some(1001));
964 assert!(result.is_err());
965 assert!(
966 result
967 .unwrap_err()
968 .message
969 .contains("Limit cannot exceed 1000")
970 );
971
972 let result = Validator::validate_pagination(None, Some(u32::MAX));
973 assert!(result.is_err());
974 assert!(
975 result
976 .unwrap_err()
977 .message
978 .contains("Limit cannot exceed 1000")
979 );
980
981 let result = Validator::validate_pagination(Some(""), Some(0));
983 assert!(result.is_err());
984 assert!(
986 result
987 .unwrap_err()
988 .message
989 .contains("Cursor cannot be empty")
990 );
991
992 let result = Validator::validate_pagination(Some("valid-cursor"), Some(0));
993 assert!(result.is_err());
994 assert!(
995 result
996 .unwrap_err()
997 .message
998 .contains("Limit must be greater than 0")
999 );
1000 }
1001}