Skip to main content

ash_rpc/
traits.rs

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