allframe_core/router/
mod.rs

1//! Router module for protocol-agnostic request handling
2//!
3//! This module provides the core abstractions for routing requests across
4//! multiple protocols (REST, GraphQL, gRPC) using a unified handler interface.
5
6use std::{collections::HashMap, future::Future};
7
8pub mod adapter;
9pub mod builder;
10#[cfg(feature = "router")]
11pub mod config;
12pub mod contract;
13pub mod docs;
14pub mod graphiql;
15pub mod graphql;
16pub mod grpc;
17pub mod grpc_explorer;
18pub mod handler;
19pub mod metadata;
20pub mod method;
21pub mod openapi;
22pub mod rest;
23pub mod scalar;
24pub mod schema;
25
26// Production adapters (optional features)
27#[cfg(feature = "router-graphql")]
28pub mod graphql_prod;
29#[cfg(feature = "router-grpc")]
30pub mod grpc_prod;
31
32pub use adapter::ProtocolAdapter;
33pub use builder::RouteBuilder;
34#[cfg(feature = "router")]
35pub use config::{GraphQLConfig, GrpcConfig, RestConfig, RouterConfig, ServerConfig};
36pub use contract::{
37    ContractTestConfig, ContractTestResult, ContractTestResults, ContractTestable, ContractTester,
38};
39pub use docs::DocsConfig;
40pub use graphiql::{graphiql_html, GraphiQLConfig, GraphiQLTheme};
41pub use graphql::{GraphQLAdapter, GraphQLOperation, OperationType};
42// Re-export production adapters when features are enabled
43#[cfg(feature = "router-graphql")]
44pub use graphql_prod::GraphQLProductionAdapter;
45pub use grpc::{GrpcAdapter, GrpcMethod, GrpcMethodType, GrpcRequest, GrpcStatus};
46pub use grpc_explorer::{grpc_explorer_html, GrpcExplorerConfig, GrpcExplorerTheme};
47#[cfg(feature = "router-grpc")]
48pub use grpc_prod::{protobuf, status, streaming, GrpcProductionAdapter, GrpcService};
49pub use handler::{Handler, HandlerFn};
50pub use metadata::RouteMetadata;
51pub use method::Method;
52pub use openapi::{OpenApiGenerator, OpenApiServer};
53pub use rest::{RestAdapter, RestRequest, RestResponse, RestRoute};
54pub use scalar::{scalar_html, ScalarConfig, ScalarLayout, ScalarTheme};
55pub use schema::ToJsonSchema;
56
57/// Router manages handler registration and protocol adapters
58///
59/// The router allows you to register handlers once and expose them via
60/// multiple protocols based on configuration.
61pub struct Router {
62    handlers: HashMap<String, Box<dyn Handler>>,
63    adapters: HashMap<String, Box<dyn ProtocolAdapter>>,
64    routes: Vec<RouteMetadata>,
65    #[cfg(feature = "router")]
66    #[allow(dead_code)]
67    config: Option<RouterConfig>,
68}
69
70impl Router {
71    /// Create a new router
72    pub fn new() -> Self {
73        Self {
74            handlers: HashMap::new(),
75            adapters: HashMap::new(),
76            routes: Vec::new(),
77            #[cfg(feature = "router")]
78            config: None,
79        }
80    }
81
82    /// Create a new router with configuration
83    #[cfg(feature = "router")]
84    pub fn with_config(config: RouterConfig) -> Self {
85        let mut router = Self {
86            handlers: HashMap::new(),
87            adapters: HashMap::new(),
88            routes: Vec::new(),
89            config: Some(config.clone()),
90        };
91
92        // Auto-register adapters based on config
93        if config.has_protocol("rest") {
94            router.add_adapter(Box::new(RestAdapter::new()));
95        }
96        if config.has_protocol("graphql") {
97            router.add_adapter(Box::new(GraphQLAdapter::new()));
98        }
99        if config.has_protocol("grpc") {
100            router.add_adapter(Box::new(GrpcAdapter::new()));
101        }
102
103        router
104    }
105
106    /// Register a handler with a name
107    pub fn register<F, Fut>(&mut self, name: &str, handler: F)
108    where
109        F: Fn() -> Fut + Send + Sync + 'static,
110        Fut: Future<Output = String> + Send + 'static,
111    {
112        self.handlers
113            .insert(name.to_string(), Box::new(HandlerFn::new(handler)));
114    }
115
116    /// Get the number of registered handlers
117    pub fn handlers_count(&self) -> usize {
118        self.handlers.len()
119    }
120
121    /// Add a protocol adapter
122    pub fn add_adapter(&mut self, adapter: Box<dyn ProtocolAdapter>) {
123        self.adapters.insert(adapter.name().to_string(), adapter);
124    }
125
126    /// Check if an adapter is registered
127    pub fn has_adapter(&self, name: &str) -> bool {
128        self.adapters.contains_key(name)
129    }
130
131    /// Get an adapter by name
132    pub fn get_adapter(&self, name: &str) -> Option<&dyn ProtocolAdapter> {
133        self.adapters.get(name).map(|b| &**b)
134    }
135
136    /// Route a request through the appropriate protocol adapter
137    pub async fn route_request(&self, protocol: &str, request: &str) -> Result<String, String> {
138        let adapter = self
139            .get_adapter(protocol)
140            .ok_or_else(|| format!("Adapter not found: {}", protocol))?;
141
142        adapter.handle(request).await
143    }
144
145    /// Execute a handler by name
146    pub async fn execute(&self, name: &str) -> Result<String, String> {
147        match self.handlers.get(name) {
148            Some(handler) => handler.call().await,
149            None => Err(format!("Handler '{}' not found", name)),
150        }
151    }
152
153    /// List all registered handler names
154    ///
155    /// Returns a vector of all handler names that have been registered
156    /// with this router. Used by MCP server for tool discovery.
157    pub fn list_handlers(&self) -> Vec<String> {
158        self.handlers.keys().cloned().collect()
159    }
160
161    /// Call a handler by name with request data
162    ///
163    /// This is an alias for `execute()` that provides a more explicit
164    /// API for directly calling handlers. Used by MCP server.
165    pub async fn call_handler(&self, name: &str, _request: &str) -> Result<String, String> {
166        // For now, ignore request parameter and just execute the handler
167        // In Phase 2, we'll add proper parameter parsing
168        self.execute(name).await
169    }
170
171    /// Check if handler can be called via REST
172    pub fn can_handle_rest(&self, _name: &str) -> bool {
173        self.has_adapter("rest")
174    }
175
176    /// Check if handler can be called via GraphQL
177    pub fn can_handle_graphql(&self, _name: &str) -> bool {
178        self.has_adapter("graphql")
179    }
180
181    /// Check if handler can be called via gRPC
182    pub fn can_handle_grpc(&self, _name: &str) -> bool {
183        self.has_adapter("grpc")
184    }
185
186    /// Get list of enabled protocols
187    pub fn enabled_protocols(&self) -> Vec<String> {
188        self.adapters.keys().cloned().collect()
189    }
190
191    /// Add a route with metadata
192    ///
193    /// This stores route metadata that can be used to generate
194    /// documentation (OpenAPI, GraphQL schemas, gRPC reflection).
195    pub fn add_route(&mut self, metadata: RouteMetadata) {
196        self.routes.push(metadata);
197    }
198
199    /// Get all registered routes
200    ///
201    /// Returns an immutable reference to all route metadata.
202    /// This is used for documentation generation.
203    pub fn routes(&self) -> &[RouteMetadata] {
204        &self.routes
205    }
206
207    /// Register a GET route
208    ///
209    /// This is a convenience method that registers both a handler and route
210    /// metadata for a GET request. The handler name is automatically
211    /// generated as "GET:{path}".
212    pub fn get<F, Fut>(&mut self, path: &str, handler: F)
213    where
214        F: Fn() -> Fut + Send + Sync + 'static,
215        Fut: Future<Output = String> + Send + 'static,
216    {
217        let handler_name = format!("GET:{}", path);
218        self.register(&handler_name, handler);
219        self.add_route(RouteMetadata::new(path, Method::GET, "rest"));
220    }
221
222    /// Register a POST route
223    ///
224    /// This is a convenience method that registers both a handler and route
225    /// metadata for a POST request. The handler name is automatically
226    /// generated as "POST:{path}".
227    pub fn post<F, Fut>(&mut self, path: &str, handler: F)
228    where
229        F: Fn() -> Fut + Send + Sync + 'static,
230        Fut: Future<Output = String> + Send + 'static,
231    {
232        let handler_name = format!("POST:{}", path);
233        self.register(&handler_name, handler);
234        self.add_route(RouteMetadata::new(path, Method::POST, "rest"));
235    }
236
237    /// Register a PUT route
238    ///
239    /// This is a convenience method that registers both a handler and route
240    /// metadata for a PUT request. The handler name is automatically
241    /// generated as "PUT:{path}".
242    pub fn put<F, Fut>(&mut self, path: &str, handler: F)
243    where
244        F: Fn() -> Fut + Send + Sync + 'static,
245        Fut: Future<Output = String> + Send + 'static,
246    {
247        let handler_name = format!("PUT:{}", path);
248        self.register(&handler_name, handler);
249        self.add_route(RouteMetadata::new(path, Method::PUT, "rest"));
250    }
251
252    /// Register a DELETE route
253    ///
254    /// This is a convenience method that registers both a handler and route
255    /// metadata for a DELETE request. The handler name is automatically
256    /// generated as "DELETE:{path}".
257    pub fn delete<F, Fut>(&mut self, path: &str, handler: F)
258    where
259        F: Fn() -> Fut + Send + Sync + 'static,
260        Fut: Future<Output = String> + Send + 'static,
261    {
262        let handler_name = format!("DELETE:{}", path);
263        self.register(&handler_name, handler);
264        self.add_route(RouteMetadata::new(path, Method::DELETE, "rest"));
265    }
266
267    /// Register a PATCH route
268    ///
269    /// This is a convenience method that registers both a handler and route
270    /// metadata for a PATCH request. The handler name is automatically
271    /// generated as "PATCH:{path}".
272    pub fn patch<F, Fut>(&mut self, path: &str, handler: F)
273    where
274        F: Fn() -> Fut + Send + Sync + 'static,
275        Fut: Future<Output = String> + Send + 'static,
276    {
277        let handler_name = format!("PATCH:{}", path);
278        self.register(&handler_name, handler);
279        self.add_route(RouteMetadata::new(path, Method::PATCH, "rest"));
280    }
281
282    /// Register a HEAD route
283    ///
284    /// This is a convenience method that registers both a handler and route
285    /// metadata for a HEAD request. The handler name is automatically
286    /// generated as "HEAD:{path}".
287    pub fn head<F, Fut>(&mut self, path: &str, handler: F)
288    where
289        F: Fn() -> Fut + Send + Sync + 'static,
290        Fut: Future<Output = String> + Send + 'static,
291    {
292        let handler_name = format!("HEAD:{}", path);
293        self.register(&handler_name, handler);
294        self.add_route(RouteMetadata::new(path, Method::HEAD, "rest"));
295    }
296
297    /// Register an OPTIONS route
298    ///
299    /// This is a convenience method that registers both a handler and route
300    /// metadata for an OPTIONS request. The handler name is automatically
301    /// generated as "OPTIONS:{path}".
302    pub fn options<F, Fut>(&mut self, path: &str, handler: F)
303    where
304        F: Fn() -> Fut + Send + Sync + 'static,
305        Fut: Future<Output = String> + Send + 'static,
306    {
307        let handler_name = format!("OPTIONS:{}", path);
308        self.register(&handler_name, handler);
309        self.add_route(RouteMetadata::new(path, Method::OPTIONS, "rest"));
310    }
311
312    /// Call handler via REST
313    pub async fn call_rest(&self, method: &str, path: &str) -> Result<String, String> {
314        let adapter = self
315            .adapters
316            .get("rest")
317            .ok_or_else(|| "REST adapter not enabled".to_string())?;
318
319        let request = format!("{} {}", method, path);
320        adapter.handle(&request).await
321    }
322
323    /// Call handler via GraphQL
324    pub async fn call_graphql(&self, query: &str) -> Result<String, String> {
325        let adapter = self
326            .adapters
327            .get("graphql")
328            .ok_or_else(|| "GraphQL adapter not enabled".to_string())?;
329
330        adapter.handle(query).await
331    }
332
333    /// Call handler via gRPC
334    pub async fn call_grpc(&self, method: &str, request: &str) -> Result<String, String> {
335        let adapter = self
336            .adapters
337            .get("grpc")
338            .ok_or_else(|| "gRPC adapter not enabled".to_string())?;
339
340        let grpc_request = format!("{}:{}", method, request);
341        adapter.handle(&grpc_request).await
342    }
343
344    /// Generate Scalar documentation HTML with default configuration
345    ///
346    /// This is a convenience method that generates a complete HTML page
347    /// with Scalar UI for interactive API documentation.
348    ///
349    /// # Arguments
350    ///
351    /// * `title` - API title
352    /// * `version` - API version
353    ///
354    /// # Example
355    ///
356    /// ```rust
357    /// use allframe_core::router::Router;
358    ///
359    /// let mut router = Router::new();
360    /// router.get("/users", || async { "Users".to_string() });
361    ///
362    /// let html = router.scalar("My API", "1.0.0");
363    /// // Serve this HTML at /docs endpoint
364    /// ```
365    pub fn scalar(&self, title: &str, version: &str) -> String {
366        let config = scalar::ScalarConfig::default();
367        self.scalar_docs(config, title, version)
368    }
369
370    /// Generate Scalar documentation HTML with custom configuration
371    ///
372    /// This method allows full customization of the Scalar UI appearance
373    /// and behavior.
374    ///
375    /// # Arguments
376    ///
377    /// * `config` - Scalar configuration
378    /// * `title` - API title
379    /// * `version` - API version
380    ///
381    /// # Example
382    ///
383    /// ```rust
384    /// use allframe_core::router::{Router, ScalarConfig, ScalarTheme};
385    ///
386    /// let mut router = Router::new();
387    /// router.get("/users", || async { "Users".to_string() });
388    ///
389    /// let config = ScalarConfig::new()
390    ///     .theme(ScalarTheme::Light)
391    ///     .show_sidebar(false);
392    ///
393    /// let html = router.scalar_docs(config, "My API", "1.0.0");
394    /// ```
395    pub fn scalar_docs(&self, config: scalar::ScalarConfig, title: &str, version: &str) -> String {
396        // Generate OpenAPI spec
397        let spec = OpenApiGenerator::new(title, version).generate(self);
398        let spec_json = serde_json::to_string(&spec).unwrap_or_else(|_| "{}".to_string());
399
400        // Generate Scalar HTML
401        scalar::scalar_html(&config, title, &spec_json)
402    }
403}
404
405impl Default for Router {
406    fn default() -> Self {
407        Self::new()
408    }
409}
410
411#[cfg(test)]
412mod tests {
413    use super::*;
414
415    #[tokio::test]
416    async fn test_router_creation() {
417        let router = Router::new();
418        assert_eq!(router.handlers_count(), 0);
419    }
420
421    #[tokio::test]
422    async fn test_handler_registration() {
423        let mut router = Router::new();
424        router.register("test", || async { "Hello".to_string() });
425        assert_eq!(router.handlers_count(), 1);
426    }
427
428    #[tokio::test]
429    async fn test_handler_execution() {
430        let mut router = Router::new();
431        router.register("test", || async { "Hello".to_string() });
432        let result = router.execute("test").await;
433        assert_eq!(result, Ok("Hello".to_string()));
434    }
435
436    // New tests for route metadata tracking
437    #[tokio::test]
438    async fn test_router_starts_with_no_routes() {
439        let router = Router::new();
440        let routes = router.routes();
441        assert_eq!(routes.len(), 0);
442    }
443
444    #[tokio::test]
445    async fn test_add_route_metadata() {
446        let mut router = Router::new();
447        let metadata = RouteMetadata::new("/users", "GET", "rest");
448
449        router.add_route(metadata.clone());
450
451        let routes = router.routes();
452        assert_eq!(routes.len(), 1);
453        assert_eq!(routes[0].path, "/users");
454        assert_eq!(routes[0].method, "GET");
455        assert_eq!(routes[0].protocol, "rest");
456    }
457
458    #[tokio::test]
459    async fn test_add_multiple_routes() {
460        let mut router = Router::new();
461
462        router.add_route(RouteMetadata::new("/users", "GET", "rest"));
463        router.add_route(RouteMetadata::new("/users", "POST", "rest"));
464        router.add_route(RouteMetadata::new("/posts", "GET", "rest"));
465
466        let routes = router.routes();
467        assert_eq!(routes.len(), 3);
468    }
469
470    #[tokio::test]
471    async fn test_routes_with_different_protocols() {
472        let mut router = Router::new();
473
474        router.add_route(RouteMetadata::new("/users", "GET", "rest"));
475        router.add_route(RouteMetadata::new("users", "query", "graphql"));
476        router.add_route(RouteMetadata::new("UserService.GetUser", "unary", "grpc"));
477
478        let routes = router.routes();
479        assert_eq!(routes.len(), 3);
480
481        assert_eq!(routes[0].protocol, "rest");
482        assert_eq!(routes[1].protocol, "graphql");
483        assert_eq!(routes[2].protocol, "grpc");
484    }
485
486    #[tokio::test]
487    async fn test_routes_returns_immutable_reference() {
488        let mut router = Router::new();
489        router.add_route(RouteMetadata::new("/test", "GET", "rest"));
490
491        let routes1 = router.routes();
492        let routes2 = router.routes();
493
494        // Both should have the same data
495        assert_eq!(routes1.len(), routes2.len());
496        assert_eq!(routes1[0].path, routes2[0].path);
497    }
498
499    // Tests for type-safe route registration
500    #[tokio::test]
501    async fn test_route_get_method() {
502        let mut router = Router::new();
503        router.get("/users", || async { "User list".to_string() });
504
505        let routes = router.routes();
506        assert_eq!(routes.len(), 1);
507        assert_eq!(routes[0].path, "/users");
508        assert_eq!(routes[0].method, "GET");
509        assert_eq!(routes[0].protocol, "rest");
510    }
511
512    #[tokio::test]
513    async fn test_route_post_method() {
514        let mut router = Router::new();
515        router.post("/users", || async { "User created".to_string() });
516
517        let routes = router.routes();
518        assert_eq!(routes.len(), 1);
519        assert_eq!(routes[0].path, "/users");
520        assert_eq!(routes[0].method, "POST");
521        assert_eq!(routes[0].protocol, "rest");
522    }
523
524    #[tokio::test]
525    async fn test_route_put_method() {
526        let mut router = Router::new();
527        router.put("/users/1", || async { "User updated".to_string() });
528
529        let routes = router.routes();
530        assert_eq!(routes.len(), 1);
531        assert_eq!(routes[0].method, "PUT");
532    }
533
534    #[tokio::test]
535    async fn test_route_delete_method() {
536        let mut router = Router::new();
537        router.delete("/users/1", || async { "User deleted".to_string() });
538
539        let routes = router.routes();
540        assert_eq!(routes.len(), 1);
541        assert_eq!(routes[0].method, "DELETE");
542    }
543
544    #[tokio::test]
545    async fn test_route_patch_method() {
546        let mut router = Router::new();
547        router.patch("/users/1", || async { "User patched".to_string() });
548
549        let routes = router.routes();
550        assert_eq!(routes.len(), 1);
551        assert_eq!(routes[0].method, "PATCH");
552    }
553
554    #[tokio::test]
555    async fn test_multiple_routes_different_methods() {
556        let mut router = Router::new();
557        router.get("/users", || async { "List".to_string() });
558        router.post("/users", || async { "Create".to_string() });
559        router.put("/users/1", || async { "Update".to_string() });
560        router.delete("/users/1", || async { "Delete".to_string() });
561
562        let routes = router.routes();
563        assert_eq!(routes.len(), 4);
564
565        assert_eq!(routes[0].method, "GET");
566        assert_eq!(routes[1].method, "POST");
567        assert_eq!(routes[2].method, "PUT");
568        assert_eq!(routes[3].method, "DELETE");
569    }
570
571    #[tokio::test]
572    async fn test_route_method_with_path_params() {
573        let mut router = Router::new();
574        router.get("/users/{id}", || async { "User details".to_string() });
575        router.get("/users/{id}/posts/{post_id}", || async {
576            "Post details".to_string()
577        });
578
579        let routes = router.routes();
580        assert_eq!(routes.len(), 2);
581        assert_eq!(routes[0].path, "/users/{id}");
582        assert_eq!(routes[1].path, "/users/{id}/posts/{post_id}");
583    }
584
585    #[tokio::test]
586    async fn test_route_registration_and_execution() {
587        let mut router = Router::new();
588        router.get("/test", || async { "GET response".to_string() });
589        router.post("/test", || async { "POST response".to_string() });
590
591        // Both route metadata and handler should be registered
592        assert_eq!(router.routes().len(), 2);
593        assert_eq!(router.handlers_count(), 2);
594
595        // Handlers should be executable
596        let result1 = router.execute("GET:/test").await;
597        let result2 = router.execute("POST:/test").await;
598
599        assert_eq!(result1, Ok("GET response".to_string()));
600        assert_eq!(result2, Ok("POST response".to_string()));
601    }
602
603    // Tests for Scalar integration
604    #[tokio::test]
605    async fn test_scalar_generates_html() {
606        let mut router = Router::new();
607        router.get("/users", || async { "Users".to_string() });
608
609        let html = router.scalar("Test API", "1.0.0");
610
611        assert!(html.contains("<!DOCTYPE html>"));
612        assert!(html.contains("<title>Test API - API Documentation</title>"));
613        assert!(html.contains("@scalar/api-reference"));
614    }
615
616    #[tokio::test]
617    async fn test_scalar_contains_openapi_spec() {
618        let mut router = Router::new();
619        router.get("/users", || async { "Users".to_string() });
620        router.post("/users", || async { "User created".to_string() });
621
622        let html = router.scalar("Test API", "1.0.0");
623
624        // Should contain the OpenAPI spec
625        assert!(html.contains("openapi"));
626        assert!(html.contains("Test API"));
627        assert!(html.contains("1.0.0"));
628    }
629
630    #[tokio::test]
631    async fn test_scalar_docs_with_custom_config() {
632        let mut router = Router::new();
633        router.get("/users", || async { "Users".to_string() });
634
635        let config = scalar::ScalarConfig::new()
636            .theme(scalar::ScalarTheme::Light)
637            .show_sidebar(false);
638
639        let html = router.scalar_docs(config, "Custom API", "2.0.0");
640
641        assert!(html.contains("Custom API"));
642        assert!(html.contains(r#""theme":"light""#));
643        assert!(html.contains(r#""showSidebar":false"#));
644    }
645
646    #[tokio::test]
647    async fn test_scalar_docs_with_custom_css() {
648        let mut router = Router::new();
649        router.get("/test", || async { "Test".to_string() });
650
651        let config = scalar::ScalarConfig::new().custom_css("body { font-family: 'Inter'; }");
652
653        let html = router.scalar_docs(config, "API", "1.0");
654
655        assert!(html.contains("<style>body { font-family: 'Inter'; }</style>"));
656    }
657
658    #[tokio::test]
659    async fn test_scalar_with_multiple_routes() {
660        let mut router = Router::new();
661        router.get("/users", || async { "Users".to_string() });
662        router.post("/users", || async { "Create".to_string() });
663        router.get("/users/{id}", || async { "User details".to_string() });
664        router.delete("/users/{id}", || async { "Delete".to_string() });
665
666        let html = router.scalar("API", "1.0.0");
667
668        // Should contain all routes in the OpenAPI spec
669        assert!(html.contains("/users"));
670    }
671
672    // Tests for protocol adapter management
673    #[tokio::test]
674    async fn test_get_adapter_returns_adapter() {
675        let mut router = Router::new();
676        router.add_adapter(Box::new(RestAdapter::new()));
677
678        let adapter = router.get_adapter("rest");
679        assert!(adapter.is_some());
680        assert_eq!(adapter.unwrap().name(), "rest");
681    }
682
683    #[tokio::test]
684    async fn test_get_adapter_returns_none_for_missing() {
685        let router = Router::new();
686        let adapter = router.get_adapter("rest");
687        assert!(adapter.is_none());
688    }
689
690    #[tokio::test]
691    async fn test_route_request_success() {
692        let mut router = Router::new();
693        router.register("test_handler", || async { "Success!".to_string() });
694
695        // Register adapter with a route
696        let mut rest_adapter = RestAdapter::new();
697        rest_adapter.route("GET", "/test", "test_handler");
698        router.add_adapter(Box::new(rest_adapter));
699
700        let result = router.route_request("rest", "GET /test").await;
701        assert!(result.is_ok());
702        let response = result.unwrap();
703        assert!(response.contains("HTTP 200") || response.contains("test_handler"));
704    }
705
706    #[tokio::test]
707    async fn test_route_request_unknown_adapter() {
708        let router = Router::new();
709        let result = router.route_request("unknown", "test").await;
710        assert!(result.is_err());
711        assert!(result.unwrap_err().contains("Adapter not found"));
712    }
713
714    #[tokio::test]
715    async fn test_enabled_protocols_empty() {
716        let router = Router::new();
717        let protocols = router.enabled_protocols();
718        assert_eq!(protocols.len(), 0);
719    }
720
721    #[tokio::test]
722    async fn test_enabled_protocols_multiple() {
723        let mut router = Router::new();
724        router.add_adapter(Box::new(RestAdapter::new()));
725        router.add_adapter(Box::new(GraphQLAdapter::new()));
726        router.add_adapter(Box::new(GrpcAdapter::new()));
727
728        let protocols = router.enabled_protocols();
729        assert_eq!(protocols.len(), 3);
730        assert!(protocols.contains(&"rest".to_string()));
731        assert!(protocols.contains(&"graphql".to_string()));
732        assert!(protocols.contains(&"grpc".to_string()));
733    }
734
735    #[tokio::test]
736    async fn test_can_handle_rest() {
737        let mut router = Router::new();
738        assert!(!router.can_handle_rest("test"));
739
740        router.add_adapter(Box::new(RestAdapter::new()));
741        assert!(router.can_handle_rest("test"));
742    }
743
744    #[tokio::test]
745    async fn test_can_handle_graphql() {
746        let mut router = Router::new();
747        assert!(!router.can_handle_graphql("test"));
748
749        router.add_adapter(Box::new(GraphQLAdapter::new()));
750        assert!(router.can_handle_graphql("test"));
751    }
752
753    #[tokio::test]
754    async fn test_can_handle_grpc() {
755        let mut router = Router::new();
756        assert!(!router.can_handle_grpc("test"));
757
758        router.add_adapter(Box::new(GrpcAdapter::new()));
759        assert!(router.can_handle_grpc("test"));
760    }
761
762    // ===== Integration Tests: Multi-Protocol Routing =====
763
764    #[tokio::test]
765    async fn test_integration_single_handler_rest() {
766        // Test: Single handler exposed via REST
767        let mut router = Router::new();
768        router.register("get_user", || async { "User data".to_string() });
769
770        // Configure REST adapter
771        let mut rest = RestAdapter::new();
772        rest.route("GET", "/users/:id", "get_user");
773        router.add_adapter(Box::new(rest));
774
775        // Route REST request
776        let response = router.route_request("rest", "GET /users/42").await;
777        assert!(response.is_ok());
778        assert!(response.unwrap().contains("get_user"));
779    }
780
781    #[tokio::test]
782    async fn test_integration_single_handler_graphql() {
783        // Test: Single handler exposed via GraphQL
784        let mut router = Router::new();
785        router.register("get_user", || async { "User data".to_string() });
786
787        // Configure GraphQL adapter
788        let mut graphql = GraphQLAdapter::new();
789        graphql.query("user", "get_user");
790        router.add_adapter(Box::new(graphql));
791
792        // Route GraphQL request
793        let response = router.route_request("graphql", "query { user }").await;
794        assert!(response.is_ok());
795        assert!(response.unwrap().contains("get_user"));
796    }
797
798    #[tokio::test]
799    async fn test_integration_single_handler_grpc() {
800        // Test: Single handler exposed via gRPC
801        let mut router = Router::new();
802        router.register("get_user", || async { "User data".to_string() });
803
804        // Configure gRPC adapter
805        let mut grpc = GrpcAdapter::new();
806        grpc.unary("UserService", "GetUser", "get_user");
807        router.add_adapter(Box::new(grpc));
808
809        // Route gRPC request
810        let response = router
811            .route_request("grpc", "UserService.GetUser:{\"id\":42}")
812            .await;
813        assert!(response.is_ok());
814        assert!(response.unwrap().contains("get_user"));
815    }
816
817    #[tokio::test]
818    async fn test_integration_single_handler_all_protocols() {
819        // Test: Single handler exposed via ALL protocols
820        let mut router = Router::new();
821        router.register("get_user", || async { "User data".to_string() });
822
823        // Configure REST adapter
824        let mut rest = RestAdapter::new();
825        rest.route("GET", "/users/:id", "get_user");
826        router.add_adapter(Box::new(rest));
827
828        // Configure GraphQL adapter
829        let mut graphql = GraphQLAdapter::new();
830        graphql.query("user", "get_user");
831        router.add_adapter(Box::new(graphql));
832
833        // Configure gRPC adapter
834        let mut grpc = GrpcAdapter::new();
835        grpc.unary("UserService", "GetUser", "get_user");
836        router.add_adapter(Box::new(grpc));
837
838        // Test REST
839        let rest_response = router.route_request("rest", "GET /users/42").await;
840        assert!(rest_response.is_ok());
841        assert!(rest_response.unwrap().contains("get_user"));
842
843        // Test GraphQL
844        let graphql_response = router.route_request("graphql", "query { user }").await;
845        assert!(graphql_response.is_ok());
846        assert!(graphql_response.unwrap().contains("get_user"));
847
848        // Test gRPC
849        let grpc_response = router
850            .route_request("grpc", "UserService.GetUser:{\"id\":42}")
851            .await;
852        assert!(grpc_response.is_ok());
853        assert!(grpc_response.unwrap().contains("get_user"));
854    }
855
856    #[tokio::test]
857    async fn test_integration_multiple_handlers_all_protocols() {
858        // Test: Multiple handlers, each exposed via all protocols
859        let mut router = Router::new();
860        router.register("get_user", || async { "User data".to_string() });
861        router.register("list_users", || async { "Users list".to_string() });
862        router.register("create_user", || async { "Created user".to_string() });
863
864        // Configure REST adapter
865        let mut rest = RestAdapter::new();
866        rest.route("GET", "/users/:id", "get_user");
867        rest.route("GET", "/users", "list_users");
868        rest.route("POST", "/users", "create_user");
869        router.add_adapter(Box::new(rest));
870
871        // Configure GraphQL adapter
872        let mut graphql = GraphQLAdapter::new();
873        graphql.query("user", "get_user");
874        graphql.query("users", "list_users");
875        graphql.mutation("createUser", "create_user");
876        router.add_adapter(Box::new(graphql));
877
878        // Configure gRPC adapter
879        let mut grpc = GrpcAdapter::new();
880        grpc.unary("UserService", "GetUser", "get_user");
881        grpc.unary("UserService", "ListUsers", "list_users");
882        grpc.unary("UserService", "CreateUser", "create_user");
883        router.add_adapter(Box::new(grpc));
884
885        // Test each handler via each protocol
886        assert!(router
887            .route_request("rest", "GET /users/42")
888            .await
889            .unwrap()
890            .contains("get_user"));
891        assert!(router
892            .route_request("graphql", "query { user }")
893            .await
894            .unwrap()
895            .contains("get_user"));
896        assert!(router
897            .route_request("grpc", "UserService.GetUser:{}")
898            .await
899            .unwrap()
900            .contains("get_user"));
901    }
902
903    #[tokio::test]
904    async fn test_integration_error_handling_rest_404() {
905        // Test: REST 404 error
906        let mut router = Router::new();
907
908        let mut rest = RestAdapter::new();
909        rest.route("GET", "/users/:id", "get_user");
910        router.add_adapter(Box::new(rest));
911
912        let response = router.route_request("rest", "GET /posts/42").await;
913        assert!(response.is_ok());
914        assert!(response.unwrap().contains("HTTP 404"));
915    }
916
917    #[tokio::test]
918    async fn test_integration_error_handling_graphql_not_found() {
919        // Test: GraphQL operation not found
920        let mut router = Router::new();
921
922        let mut graphql = GraphQLAdapter::new();
923        graphql.query("user", "get_user");
924        router.add_adapter(Box::new(graphql));
925
926        let response = router.route_request("graphql", "query { post }").await;
927        assert!(response.is_ok());
928        assert!(response.unwrap().contains("errors"));
929    }
930
931    #[tokio::test]
932    async fn test_integration_error_handling_grpc_unimplemented() {
933        // Test: gRPC method not implemented
934        let mut router = Router::new();
935
936        let mut grpc = GrpcAdapter::new();
937        grpc.unary("UserService", "GetUser", "get_user");
938        router.add_adapter(Box::new(grpc));
939
940        let response = router.route_request("grpc", "UserService.GetPost:{}").await;
941        assert!(response.is_ok());
942        assert!(response.unwrap().contains("grpc-status: 12")); // UNIMPLEMENTED
943    }
944
945    #[tokio::test]
946    async fn test_integration_unknown_protocol() {
947        // Test: Unknown protocol error
948        let router = Router::new();
949
950        let response = router.route_request("unknown", "request").await;
951        assert!(response.is_err());
952        assert!(response.unwrap_err().contains("Adapter not found"));
953    }
954
955    #[tokio::test]
956    async fn test_integration_protocol_specific_features_rest_methods() {
957        // Test: REST-specific HTTP methods
958        let mut router = Router::new();
959        router.register("get_users", || async { "Users".to_string() });
960        router.register("create_user", || async { "Created".to_string() });
961        router.register("update_user", || async { "Updated".to_string() });
962        router.register("delete_user", || async { "Deleted".to_string() });
963
964        let mut rest = RestAdapter::new();
965        rest.route("GET", "/users", "get_users");
966        rest.route("POST", "/users", "create_user");
967        rest.route("PUT", "/users/:id", "update_user");
968        rest.route("DELETE", "/users/:id", "delete_user");
969        router.add_adapter(Box::new(rest));
970
971        // Test different HTTP methods
972        assert!(router
973            .route_request("rest", "GET /users")
974            .await
975            .unwrap()
976            .contains("get_users"));
977        assert!(router
978            .route_request("rest", "POST /users")
979            .await
980            .unwrap()
981            .contains("create_user"));
982        assert!(router
983            .route_request("rest", "PUT /users/42")
984            .await
985            .unwrap()
986            .contains("update_user"));
987        assert!(router
988            .route_request("rest", "DELETE /users/42")
989            .await
990            .unwrap()
991            .contains("delete_user"));
992    }
993
994    #[tokio::test]
995    async fn test_integration_protocol_specific_features_graphql_types() {
996        // Test: GraphQL-specific query vs mutation
997        let mut router = Router::new();
998        router.register("get_user", || async { "User".to_string() });
999        router.register("create_user", || async { "Created".to_string() });
1000
1001        let mut graphql = GraphQLAdapter::new();
1002        graphql.query("user", "get_user");
1003        graphql.mutation("createUser", "create_user");
1004        router.add_adapter(Box::new(graphql));
1005
1006        // Test query
1007        assert!(router
1008            .route_request("graphql", "query { user }")
1009            .await
1010            .unwrap()
1011            .contains("get_user"));
1012
1013        // Test mutation
1014        assert!(router
1015            .route_request("graphql", "mutation { createUser }")
1016            .await
1017            .unwrap()
1018            .contains("create_user"));
1019    }
1020
1021    #[tokio::test]
1022    async fn test_integration_protocol_specific_features_grpc_streaming() {
1023        // Test: gRPC-specific streaming modes
1024        let mut router = Router::new();
1025        router.register("get_user", || async { "User".to_string() });
1026        router.register("list_users", || async { "Users".to_string() });
1027
1028        let mut grpc = GrpcAdapter::new();
1029        grpc.unary("UserService", "GetUser", "get_user");
1030        grpc.server_streaming("UserService", "ListUsers", "list_users");
1031        router.add_adapter(Box::new(grpc));
1032
1033        // Test unary
1034        let unary_response = router
1035            .route_request("grpc", "UserService.GetUser:{}")
1036            .await
1037            .unwrap();
1038        assert!(unary_response.contains("unary"));
1039
1040        // Test server streaming
1041        let streaming_response = router
1042            .route_request("grpc", "UserService.ListUsers:{}")
1043            .await
1044            .unwrap();
1045        assert!(streaming_response.contains("server_streaming"));
1046    }
1047}