Skip to main content

ash_rpc/
traits.rs

1//! Core traits for JSON-RPC handlers and processors.
2
3use crate::types::{Message, Notification, Request, RequestId, Response};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// Async trait for individual JSON-RPC method implementations
8#[async_trait::async_trait]
9pub trait JsonRPCMethod: Send + Sync {
10    /// Get the method name that this implementation handles
11    fn method_name(&self) -> &'static str;
12
13    /// Execute the JSON-RPC method asynchronously
14    async fn call(&self, params: Option<serde_json::Value>, id: Option<RequestId>) -> Response;
15
16    /// Get `OpenAPI` components for this method
17    fn openapi_components(&self) -> OpenApiMethodSpec {
18        OpenApiMethodSpec::new(self.method_name())
19    }
20}
21
22/// Trait for handling JSON-RPC requests and notifications
23#[async_trait::async_trait]
24pub trait Handler: Send + Sync {
25    /// Handle a JSON-RPC request and return a response
26    async fn handle_request(&self, request: Request) -> Response;
27
28    /// Handle a JSON-RPC notification (no response expected)
29    async fn handle_notification(&self, notification: Notification);
30
31    /// Check if a method is supported
32    fn supports_method(&self, method: &str) -> bool {
33        let _ = method;
34        true
35    }
36
37    /// Get list of supported methods
38    fn get_supported_methods(&self) -> Vec<String> {
39        vec![]
40    }
41}
42
43/// Trait for processing JSON-RPC messages
44#[async_trait::async_trait]
45pub trait MessageProcessor: Send + Sync {
46    /// Process a single JSON-RPC message
47    async fn process_message(&self, message: Message) -> Option<Response>;
48
49    /// Process a batch of JSON-RPC messages
50    async fn process_batch(&self, messages: Vec<Message>) -> Vec<Response> {
51        let mut results = Vec::new();
52        for msg in messages {
53            if let Some(response) = self.process_message(msg).await {
54                results.push(response);
55            }
56        }
57        results
58    }
59
60    /// Check if batch processing is supported
61    fn supports_batching(&self) -> bool {
62        true
63    }
64
65    /// Get processor capabilities
66    fn get_capabilities(&self) -> ProcessorCapabilities {
67        ProcessorCapabilities::default()
68    }
69}
70
71/// Trait for processing streaming JSON-RPC messages with subscriptions
72#[cfg(feature = "streaming")]
73#[async_trait::async_trait]
74pub trait StreamingMessageProcessor: MessageProcessor {
75    /// Process a stream subscription request
76    async fn process_stream_request(
77        &self,
78        request: crate::streaming::StreamRequest,
79    ) -> crate::streaming::StreamResponse;
80
81    /// Process an unsubscribe request
82    async fn process_unsubscribe(&self, stream_id: &str) -> Result<(), crate::Error>;
83
84    /// Check if streaming is supported
85    fn supports_streaming(&self) -> bool {
86        true
87    }
88
89    /// Get active stream count
90    async fn active_stream_count(&self) -> usize {
91        0
92    }
93}
94
95#[derive(Debug, Clone)]
96pub struct ProcessorCapabilities {
97    pub supports_batch: bool,
98    pub supports_notifications: bool,
99    pub max_batch_size: Option<usize>,
100    pub max_request_size: Option<usize>,
101    pub request_timeout_secs: Option<u64>,
102    pub supported_versions: Vec<String>,
103}
104
105impl Default for ProcessorCapabilities {
106    fn default() -> Self {
107        Self {
108            supports_batch: true,
109            supports_notifications: true,
110            max_batch_size: Some(100), // Secure default: limit batch size
111            max_request_size: Some(1024 * 1024), // 1 MB
112            request_timeout_secs: Some(30),
113            supported_versions: vec!["2.0".to_owned()],
114        }
115    }
116}
117
118/// Builder for `ProcessorCapabilities` with validation
119pub struct ProcessorCapabilitiesBuilder {
120    supports_batch: bool,
121    supports_notifications: bool,
122    max_batch_size: Option<usize>,
123    max_request_size: Option<usize>,
124    request_timeout_secs: Option<u64>,
125    supported_versions: Vec<String>,
126}
127
128impl ProcessorCapabilitiesBuilder {
129    /// Create a new builder with secure defaults
130    #[must_use]
131    pub fn new() -> Self {
132        Self {
133            supports_batch: true,
134            supports_notifications: true,
135            max_batch_size: Some(100),
136            max_request_size: Some(1024 * 1024),
137            request_timeout_secs: Some(30),
138            supported_versions: vec!["2.0".to_owned()],
139        }
140    }
141
142    /// Enable or disable batch support
143    #[must_use]
144    pub fn supports_batch(mut self, enabled: bool) -> Self {
145        self.supports_batch = enabled;
146        self
147    }
148
149    /// Enable or disable notification support
150    #[must_use]
151    pub fn supports_notifications(mut self, enabled: bool) -> Self {
152        self.supports_notifications = enabled;
153        self
154    }
155
156    /// Set maximum batch size with validation
157    ///
158    /// # Arguments
159    /// * `size` - Maximum batch size (1-1000), or None for unlimited
160    ///
161    /// # Panics
162    /// Panics if size is 0 or greater than 1000
163    #[must_use]
164    pub fn max_batch_size(mut self, size: Option<usize>) -> Self {
165        if let Some(s) = size {
166            assert!(
167                s > 0 && s <= 1000,
168                "max_batch_size must be between 1 and 1000"
169            );
170        }
171        self.max_batch_size = size;
172        self
173    }
174
175    /// Set maximum request size in bytes with validation
176    ///
177    /// # Arguments
178    /// * `size` - Maximum size in bytes (1KB-100MB), or None for unlimited
179    ///
180    /// # Panics
181    /// Panics if size is less than 1KB or greater than 100MB
182    #[must_use]
183    pub fn max_request_size(mut self, size: Option<usize>) -> Self {
184        if let Some(s) = size {
185            assert!(
186                (1024..=100 * 1024 * 1024).contains(&s),
187                "max_request_size must be between 1KB and 100MB"
188            );
189        }
190        self.max_request_size = size;
191        self
192    }
193
194    /// Set request timeout in seconds with validation
195    ///
196    /// # Arguments
197    /// * `timeout` - Timeout in seconds (1-300), or None for no timeout
198    ///
199    /// # Panics
200    /// Panics if timeout is 0 or greater than 300 seconds
201    #[must_use]
202    pub fn request_timeout_secs(mut self, timeout: Option<u64>) -> Self {
203        if let Some(t) = timeout {
204            assert!(
205                t > 0 && t <= 300,
206                "request_timeout_secs must be between 1 and 300"
207            );
208        }
209        self.request_timeout_secs = timeout;
210        self
211    }
212
213    /// Add a supported JSON-RPC version
214    #[must_use]
215    pub fn add_version(mut self, version: impl Into<String>) -> Self {
216        self.supported_versions.push(version.into());
217        self
218    }
219
220    /// Build the capabilities with validation
221    pub fn build(self) -> ProcessorCapabilities {
222        tracing::debug!(
223            supports_batch = self.supports_batch,
224            max_batch_size = ?self.max_batch_size,
225            max_request_size = ?self.max_request_size,
226            request_timeout_secs = ?self.request_timeout_secs,
227            "creating processor capabilities"
228        );
229
230        ProcessorCapabilities {
231            supports_batch: self.supports_batch,
232            supports_notifications: self.supports_notifications,
233            max_batch_size: self.max_batch_size,
234            max_request_size: self.max_request_size,
235            request_timeout_secs: self.request_timeout_secs,
236            supported_versions: self.supported_versions,
237        }
238    }
239}
240
241impl Default for ProcessorCapabilitiesBuilder {
242    fn default() -> Self {
243        Self::new()
244    }
245}
246
247/// `OpenAPI` specification for a single JSON-RPC method
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct OpenApiMethodSpec {
250    pub method_name: String,
251    pub summary: Option<String>,
252    pub description: Option<String>,
253    pub parameters: Option<serde_json::Value>,
254    pub result: Option<serde_json::Value>,
255    pub errors: Vec<OpenApiError>,
256    pub tags: Vec<String>,
257    pub examples: Vec<OpenApiExample>,
258}
259
260impl OpenApiMethodSpec {
261    /// Create a new `OpenAPI` method specification
262    pub fn new(method_name: impl Into<String>) -> Self {
263        Self {
264            method_name: method_name.into(),
265            summary: None,
266            description: None,
267            parameters: None,
268            result: None,
269            errors: Vec::new(),
270            tags: Vec::new(),
271            examples: Vec::new(),
272        }
273    }
274
275    /// Add a summary
276    #[must_use]
277    pub fn with_summary(mut self, summary: impl Into<String>) -> Self {
278        self.summary = Some(summary.into());
279        self
280    }
281
282    /// Add a description
283    #[must_use]
284    pub fn with_description(mut self, description: impl Into<String>) -> Self {
285        self.description = Some(description.into());
286        self
287    }
288
289    /// Add parameter schema
290    #[must_use]
291    pub fn with_parameters(mut self, params: serde_json::Value) -> Self {
292        self.parameters = Some(params);
293        self
294    }
295
296    /// Add result schema
297    #[must_use]
298    pub fn with_result(mut self, result: serde_json::Value) -> Self {
299        self.result = Some(result);
300        self
301    }
302
303    /// Add an error specification
304    #[must_use]
305    pub fn with_error(mut self, error: OpenApiError) -> Self {
306        self.errors.push(error);
307        self
308    }
309
310    /// Add a tag
311    #[must_use]
312    pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
313        self.tags.push(tag.into());
314        self
315    }
316
317    /// Add an example
318    #[must_use]
319    pub fn with_example(mut self, example: OpenApiExample) -> Self {
320        self.examples.push(example);
321        self
322    }
323}
324
325/// `OpenAPI` error specification
326#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct OpenApiError {
328    pub code: i32,
329    pub message: String,
330    pub description: Option<String>,
331}
332
333impl OpenApiError {
334    /// Create a new error specification
335    pub fn new(code: i32, message: impl Into<String>) -> Self {
336        Self {
337            code,
338            message: message.into(),
339            description: None,
340        }
341    }
342
343    /// Add description
344    #[must_use]
345    pub fn with_description(mut self, description: impl Into<String>) -> Self {
346        self.description = Some(description.into());
347        self
348    }
349}
350
351/// `OpenAPI` example for a method
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct OpenApiExample {
354    pub name: String,
355    pub summary: Option<String>,
356    pub description: Option<String>,
357    pub params: Option<serde_json::Value>,
358    pub result: Option<serde_json::Value>,
359}
360
361impl OpenApiExample {
362    /// Create a new example
363    pub fn new(name: impl Into<String>) -> Self {
364        Self {
365            name: name.into(),
366            summary: None,
367            description: None,
368            params: None,
369            result: None,
370        }
371    }
372
373    /// Add summary
374    #[must_use]
375    pub fn with_summary(mut self, summary: impl Into<String>) -> Self {
376        self.summary = Some(summary.into());
377        self
378    }
379
380    /// Add description
381    #[must_use]
382    pub fn with_description(mut self, description: impl Into<String>) -> Self {
383        self.description = Some(description.into());
384        self
385    }
386
387    /// Add parameters
388    #[must_use]
389    pub fn with_params(mut self, params: serde_json::Value) -> Self {
390        self.params = Some(params);
391        self
392    }
393
394    /// Add result
395    #[must_use]
396    pub fn with_result(mut self, result: serde_json::Value) -> Self {
397        self.result = Some(result);
398        self
399    }
400}
401
402/// Complete `OpenAPI` specification composed from all registered methods
403#[derive(Debug, Clone, Serialize, Deserialize)]
404pub struct OpenApiSpec {
405    pub openapi: String,
406    pub info: OpenApiInfo,
407    pub servers: Vec<OpenApiServer>,
408    pub methods: HashMap<String, OpenApiMethodSpec>,
409    pub components: OpenApiComponents,
410}
411
412impl OpenApiSpec {
413    /// Create a new `OpenAPI` specification
414    pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
415        Self {
416            openapi: "3.0.3".to_owned(),
417            info: OpenApiInfo {
418                title: title.into(),
419                version: version.into(),
420                description: None,
421            },
422            servers: Vec::new(),
423            methods: HashMap::new(),
424            components: OpenApiComponents::default(),
425        }
426    }
427
428    /// Add a method specification
429    pub fn add_method(&mut self, spec: OpenApiMethodSpec) {
430        self.methods.insert(spec.method_name.clone(), spec);
431    }
432
433    /// Add multiple method specifications
434    pub fn add_methods(&mut self, specs: Vec<OpenApiMethodSpec>) {
435        for spec in specs {
436            self.add_method(spec);
437        }
438    }
439
440    /// Add a server
441    pub fn add_server(&mut self, server: OpenApiServer) {
442        self.servers.push(server);
443    }
444
445    /// Set description
446    #[must_use]
447    pub fn with_description(mut self, description: impl Into<String>) -> Self {
448        self.info.description = Some(description.into());
449        self
450    }
451}
452
453/// `OpenAPI` info section
454#[derive(Debug, Clone, Serialize, Deserialize)]
455pub struct OpenApiInfo {
456    pub title: String,
457    pub version: String,
458    pub description: Option<String>,
459}
460
461/// `OpenAPI` server specification
462#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct OpenApiServer {
464    pub url: String,
465    pub description: Option<String>,
466}
467
468impl OpenApiServer {
469    /// Create a new server specification
470    pub fn new(url: impl Into<String>) -> Self {
471        Self {
472            url: url.into(),
473            description: None,
474        }
475    }
476
477    /// Add description
478    #[must_use]
479    pub fn with_description(mut self, description: impl Into<String>) -> Self {
480        self.description = Some(description.into());
481        self
482    }
483}
484
485/// `OpenAPI` components section
486#[derive(Debug, Clone, Serialize, Deserialize, Default)]
487pub struct OpenApiComponents {
488    pub schemas: HashMap<String, serde_json::Value>,
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494    use serde_json::json;
495
496    // ProcessorCapabilities tests
497    #[test]
498    fn test_processor_capabilities_default() {
499        let caps = ProcessorCapabilities::default();
500        assert!(caps.supports_batch);
501        assert!(caps.supports_notifications);
502        assert_eq!(caps.max_batch_size, Some(100));
503        assert_eq!(caps.max_request_size, Some(1024 * 1024));
504        assert_eq!(caps.request_timeout_secs, Some(30));
505        assert_eq!(caps.supported_versions, vec!["2.0"]);
506    }
507
508    #[test]
509    fn test_processor_capabilities_builder() {
510        let caps = ProcessorCapabilitiesBuilder::new()
511            .max_batch_size(Some(50))
512            .max_request_size(Some(2 * 1024 * 1024))
513            .request_timeout_secs(Some(60))
514            .build();
515
516        assert_eq!(caps.max_batch_size, Some(50));
517        assert_eq!(caps.max_request_size, Some(2 * 1024 * 1024));
518        assert_eq!(caps.request_timeout_secs, Some(60));
519    }
520
521    #[test]
522    #[should_panic(expected = "max_batch_size must be between 1 and 1000")]
523    fn test_processor_capabilities_invalid_batch_size() {
524        ProcessorCapabilitiesBuilder::new()
525            .max_batch_size(Some(0))
526            .build();
527    }
528
529    #[test]
530    #[should_panic(expected = "max_batch_size must be between 1 and 1000")]
531    fn test_processor_capabilities_batch_size_too_large() {
532        ProcessorCapabilitiesBuilder::new()
533            .max_batch_size(Some(2000))
534            .build();
535    }
536
537    #[test]
538    fn test_processor_capabilities_builder_boundary() {
539        // Test minimum
540        let caps_min = ProcessorCapabilitiesBuilder::new()
541            .max_batch_size(Some(1))
542            .build();
543        assert_eq!(caps_min.max_batch_size, Some(1));
544
545        // Test maximum
546        let caps_max = ProcessorCapabilitiesBuilder::new()
547            .max_batch_size(Some(1000))
548            .build();
549        assert_eq!(caps_max.max_batch_size, Some(1000));
550    }
551
552    // OpenApiMethodSpec tests
553    #[test]
554    fn test_openapi_method_spec_creation() {
555        let spec = OpenApiMethodSpec::new("test_method");
556        assert_eq!(spec.method_name, "test_method");
557        assert!(spec.description.is_none());
558        assert!(spec.parameters.is_none());
559        assert!(spec.result.is_none());
560    }
561
562    #[test]
563    fn test_openapi_method_spec_with_description() {
564        let spec = OpenApiMethodSpec::new("method").with_description("Test description");
565        assert_eq!(spec.description, Some("Test description".to_string()));
566    }
567
568    #[test]
569    fn test_openapi_method_spec_with_schemas() {
570        let params = json!({"type": "object"});
571        let result = json!({"type": "string"});
572
573        let spec = OpenApiMethodSpec::new("method")
574            .with_parameters(params.clone())
575            .with_result(result.clone());
576
577        assert_eq!(spec.parameters, Some(params));
578        assert_eq!(spec.result, Some(result));
579    }
580
581    #[test]
582    fn test_openapi_method_spec_complete() {
583        let spec = OpenApiMethodSpec::new("complete_method")
584            .with_description("A complete method")
585            .with_parameters(json!({"type": "array"}))
586            .with_result(json!({"type": "number"}));
587
588        assert_eq!(spec.method_name, "complete_method");
589        assert_eq!(spec.description, Some("A complete method".to_string()));
590        assert!(spec.parameters.is_some());
591        assert!(spec.result.is_some());
592    }
593
594    // OpenApiSpec tests
595    #[test]
596    fn test_openapi_spec_creation() {
597        let spec = OpenApiSpec::new("Test API", "1.0.0");
598        assert_eq!(spec.openapi, "3.0.3");
599        assert_eq!(spec.info.title, "Test API");
600        assert_eq!(spec.info.version, "1.0.0");
601    }
602
603    #[test]
604    fn test_openapi_spec_add_server() {
605        let mut spec = OpenApiSpec::new("API", "1.0.0");
606        spec.add_server(OpenApiServer::new("http://localhost:8080"));
607        assert_eq!(spec.servers.len(), 1);
608        assert_eq!(spec.servers[0].url, "http://localhost:8080");
609    }
610
611    #[test]
612    fn test_openapi_spec_add_method() {
613        let mut spec = OpenApiSpec::new("API", "1.0.0");
614        let method_spec = OpenApiMethodSpec::new("test");
615        spec.add_method(method_spec);
616        assert_eq!(spec.methods.len(), 1);
617    }
618
619    #[test]
620    fn test_openapi_spec_serialization() {
621        let mut spec = OpenApiSpec::new("Test", "1.0");
622        spec.add_server(OpenApiServer::new("http://api.example.com"));
623
624        let json = serde_json::to_string(&spec).unwrap();
625        let deserialized: OpenApiSpec = serde_json::from_str(&json).unwrap();
626
627        assert_eq!(deserialized.info.title, "Test");
628        assert_eq!(deserialized.servers.len(), 1);
629    }
630
631    // OpenApiServer tests
632    #[test]
633    fn test_openapi_server_creation() {
634        let server = OpenApiServer::new("http://localhost:3000");
635        assert_eq!(server.url, "http://localhost:3000");
636        assert!(server.description.is_none());
637    }
638
639    #[test]
640    fn test_openapi_server_with_description() {
641        let server = OpenApiServer::new("http://api.com").with_description("Production API");
642        assert_eq!(server.description, Some("Production API".to_string()));
643    }
644
645    // OpenApiInfo tests
646    #[test]
647    fn test_openapi_info_creation() {
648        let info = OpenApiInfo {
649            title: "My API".to_string(),
650            version: "2.0.0".to_string(),
651            description: Some("API description".to_string()),
652        };
653
654        assert_eq!(info.title, "My API");
655        assert_eq!(info.version, "2.0.0");
656        assert_eq!(info.description, Some("API description".to_string()));
657    }
658
659    // OpenApiComponents tests
660    #[test]
661    fn test_openapi_components_default() {
662        let components = OpenApiComponents::default();
663        assert_eq!(components.schemas.len(), 0);
664    }
665
666    #[test]
667    fn test_openapi_components_with_schemas() {
668        let mut components = OpenApiComponents::default();
669        components.schemas.insert(
670            "User".to_string(),
671            json!({"type": "object", "properties": {"name": {"type": "string"}}}),
672        );
673
674        assert_eq!(components.schemas.len(), 1);
675        assert!(components.schemas.contains_key("User"));
676    }
677
678    // Test JsonRPCMethod trait implementation
679    struct TestMethod;
680
681    #[async_trait::async_trait]
682    impl JsonRPCMethod for TestMethod {
683        fn method_name(&self) -> &'static str {
684            "test"
685        }
686
687        async fn call(&self, params: Option<serde_json::Value>, id: Option<RequestId>) -> Response {
688            Response::success(params.unwrap_or(json!(null)), id)
689        }
690    }
691
692    #[tokio::test]
693    async fn test_jsonrpc_method_trait() {
694        let method = TestMethod;
695        assert_eq!(method.method_name(), "test");
696
697        let params = json!({"key": "value"});
698        let response = method.call(Some(params.clone()), Some(json!(1))).await;
699
700        assert!(response.is_success());
701        assert_eq!(response.result, Some(params));
702    }
703
704    #[tokio::test]
705    async fn test_jsonrpc_method_openapi_components() {
706        let method = TestMethod;
707        let spec = method.openapi_components();
708        assert_eq!(spec.method_name, "test");
709    }
710
711    // Handler trait tests with mock implementation
712    struct TestHandler;
713
714    #[async_trait::async_trait]
715    impl Handler for TestHandler {
716        async fn handle_request(&self, request: Request) -> Response {
717            Response::success(json!({"handled": request.method}), request.id)
718        }
719
720        async fn handle_notification(&self, _notification: Notification) {
721            // No-op
722        }
723
724        fn supports_method(&self, method: &str) -> bool {
725            method == "supported"
726        }
727
728        fn get_supported_methods(&self) -> Vec<String> {
729            vec!["supported".to_string(), "another".to_string()]
730        }
731    }
732
733    #[tokio::test]
734    async fn test_handler_handle_request() {
735        let handler = TestHandler;
736        let request = Request {
737            jsonrpc: "2.0".to_string(),
738            method: "test".to_string(),
739            params: None,
740            id: Some(json!(1)),
741            correlation_id: None,
742        };
743
744        let response = handler.handle_request(request).await;
745        assert!(response.is_success());
746    }
747
748    #[tokio::test]
749    async fn test_handler_supports_method() {
750        let handler = TestHandler;
751        assert!(handler.supports_method("supported"));
752        assert!(!handler.supports_method("unsupported"));
753    }
754
755    #[tokio::test]
756    async fn test_handler_get_supported_methods() {
757        let handler = TestHandler;
758        let methods = handler.get_supported_methods();
759        assert_eq!(methods.len(), 2);
760        assert!(methods.contains(&"supported".to_string()));
761        assert!(methods.contains(&"another".to_string()));
762    }
763
764    // MessageProcessor trait tests
765    struct TestProcessor;
766
767    #[async_trait::async_trait]
768    impl MessageProcessor for TestProcessor {
769        async fn process_message(&self, message: Message) -> Option<Response> {
770            match message {
771                Message::Request(req) => Some(Response::success(json!("ok"), req.id)),
772                _ => None,
773            }
774        }
775    }
776
777    #[tokio::test]
778    async fn test_message_processor_single_message() {
779        let processor = TestProcessor;
780        let request = Message::Request(Request {
781            jsonrpc: "2.0".to_string(),
782            method: "test".to_string(),
783            params: None,
784            id: Some(json!(1)),
785            correlation_id: None,
786        });
787
788        let response = processor.process_message(request).await;
789        assert!(response.is_some());
790    }
791
792    #[tokio::test]
793    async fn test_message_processor_batch() {
794        let processor = TestProcessor;
795        let messages = vec![
796            Message::Request(Request {
797                jsonrpc: "2.0".to_string(),
798                method: "test1".to_string(),
799                params: None,
800                id: Some(json!(1)),
801                correlation_id: None,
802            }),
803            Message::Request(Request {
804                jsonrpc: "2.0".to_string(),
805                method: "test2".to_string(),
806                params: None,
807                id: Some(json!(2)),
808                correlation_id: None,
809            }),
810        ];
811
812        let responses = processor.process_batch(messages).await;
813        assert_eq!(responses.len(), 2);
814    }
815
816    #[tokio::test]
817    async fn test_message_processor_supports_batching() {
818        let processor = TestProcessor;
819        assert!(processor.supports_batching());
820    }
821
822    #[tokio::test]
823    async fn test_message_processor_capabilities() {
824        let processor = TestProcessor;
825        let caps = processor.get_capabilities();
826        assert!(caps.supports_batch);
827        assert!(caps.supports_notifications);
828    }
829
830    // ProcessorCapabilities additional tests
831    #[test]
832    fn test_processor_capabilities_builder_disabled_batch() {
833        let caps = ProcessorCapabilitiesBuilder::new()
834            .supports_batch(false)
835            .build();
836        assert!(!caps.supports_batch);
837    }
838
839    #[test]
840    fn test_processor_capabilities_builder_disabled_notifications() {
841        let caps = ProcessorCapabilitiesBuilder::new()
842            .supports_notifications(false)
843            .build();
844        assert!(!caps.supports_notifications);
845    }
846
847    #[test]
848    fn test_processor_capabilities_builder_add_version() {
849        let caps = ProcessorCapabilitiesBuilder::new()
850            .add_version("3.0")
851            .build();
852        assert!(caps.supported_versions.contains(&"2.0".to_string()));
853        assert!(caps.supported_versions.contains(&"3.0".to_string()));
854    }
855
856    #[test]
857    fn test_processor_capabilities_builder_none_limits() {
858        let caps = ProcessorCapabilitiesBuilder::new()
859            .max_batch_size(None)
860            .max_request_size(None)
861            .request_timeout_secs(None)
862            .build();
863
864        assert!(caps.max_batch_size.is_none());
865        assert!(caps.max_request_size.is_none());
866        assert!(caps.request_timeout_secs.is_none());
867    }
868
869    // OpenApiError tests
870    #[test]
871    fn test_openapi_error_creation() {
872        let error = OpenApiError::new(crate::error_codes::INVALID_REQUEST, "Invalid Request");
873        assert_eq!(error.code, crate::error_codes::INVALID_REQUEST);
874        assert_eq!(error.message, "Invalid Request");
875        assert!(error.description.is_none());
876    }
877
878    #[test]
879    fn test_openapi_error_with_description() {
880        let error = OpenApiError::new(crate::error_codes::INVALID_REQUEST, "Invalid Request")
881            .with_description("The JSON sent is not a valid Request object");
882        assert_eq!(
883            error.description,
884            Some("The JSON sent is not a valid Request object".to_string())
885        );
886    }
887
888    // OpenApiExample tests
889    #[test]
890    fn test_openapi_example_creation() {
891        let example = OpenApiExample::new("basic_example");
892        assert_eq!(example.name, "basic_example");
893        assert!(example.summary.is_none());
894        assert!(example.description.is_none());
895    }
896
897    #[test]
898    fn test_openapi_example_complete() {
899        let example = OpenApiExample::new("complete")
900            .with_summary("Complete example")
901            .with_description("A complete example with all fields")
902            .with_params(json!({"x": 1, "y": 2}))
903            .with_result(json!(3));
904
905        assert_eq!(example.summary, Some("Complete example".to_string()));
906        assert_eq!(
907            example.description,
908            Some("A complete example with all fields".to_string())
909        );
910        assert!(example.params.is_some());
911        assert!(example.result.is_some());
912    }
913
914    // OpenApiMethodSpec additional tests
915    #[test]
916    fn test_openapi_method_spec_with_error() {
917        let error = OpenApiError::new(crate::error_codes::INVALID_PARAMS, "Invalid params");
918        let spec = OpenApiMethodSpec::new("method").with_error(error);
919
920        assert_eq!(spec.errors.len(), 1);
921        assert_eq!(spec.errors[0].code, crate::error_codes::INVALID_PARAMS);
922    }
923
924    #[test]
925    fn test_openapi_method_spec_with_tag() {
926        let spec = OpenApiMethodSpec::new("method")
927            .with_tag("utility")
928            .with_tag("public");
929
930        assert_eq!(spec.tags.len(), 2);
931        assert!(spec.tags.contains(&"utility".to_string()));
932        assert!(spec.tags.contains(&"public".to_string()));
933    }
934
935    #[test]
936    fn test_openapi_method_spec_with_example() {
937        let example = OpenApiExample::new("example1");
938        let spec = OpenApiMethodSpec::new("method").with_example(example);
939
940        assert_eq!(spec.examples.len(), 1);
941        assert_eq!(spec.examples[0].name, "example1");
942    }
943
944    #[test]
945    fn test_openapi_method_spec_with_summary() {
946        let spec = OpenApiMethodSpec::new("method").with_summary("Method summary");
947        assert_eq!(spec.summary, Some("Method summary".to_string()));
948    }
949
950    // OpenApiSpec additional tests
951    #[test]
952    fn test_openapi_spec_add_multiple_methods() {
953        let mut spec = OpenApiSpec::new("API", "1.0");
954        let methods = vec![
955            OpenApiMethodSpec::new("method1"),
956            OpenApiMethodSpec::new("method2"),
957            OpenApiMethodSpec::new("method3"),
958        ];
959
960        spec.add_methods(methods);
961        assert_eq!(spec.methods.len(), 3);
962    }
963
964    #[test]
965    fn test_openapi_spec_with_description() {
966        let spec = OpenApiSpec::new("API", "1.0").with_description("Test API description");
967        assert_eq!(
968            spec.info.description,
969            Some("Test API description".to_string())
970        );
971    }
972
973    #[test]
974    fn test_openapi_spec_multiple_servers() {
975        let mut spec = OpenApiSpec::new("API", "1.0");
976        spec.add_server(OpenApiServer::new("http://dev.example.com"));
977        spec.add_server(OpenApiServer::new("https://prod.example.com"));
978
979        assert_eq!(spec.servers.len(), 2);
980    }
981}