1pub mod database;
53pub mod federation;
54pub mod router;
55pub mod service;
56
57pub use database::FederationDatabase;
58pub use federation::{Federation, FederationConfig, FederationService};
59pub use router::{FederationRouter, RoutingResult};
60pub use service::{ServiceBoundary, ServiceRealityLevel};
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use std::collections::HashMap;
66 use std::sync::Arc;
67 use uuid::Uuid;
68
69 #[test]
72 fn test_service_reality_level_public_api() {
73 let level = ServiceRealityLevel::Real;
75 assert_eq!(level.as_str(), "real");
76
77 let parsed = ServiceRealityLevel::from_str("mock_v3");
78 assert_eq!(parsed, Some(ServiceRealityLevel::MockV3));
79 }
80
81 #[test]
82 fn test_service_boundary_public_api() {
83 let workspace_id = Uuid::new_v4();
85 let service = ServiceBoundary::new(
86 "test-service".to_string(),
87 workspace_id,
88 "/api".to_string(),
89 ServiceRealityLevel::Blended,
90 );
91
92 assert_eq!(service.name, "test-service");
93 assert_eq!(service.workspace_id, workspace_id);
94 assert_eq!(service.base_path, "/api");
95 assert_eq!(service.reality_level, ServiceRealityLevel::Blended);
96 assert!(service.matches_path("/api/users"));
97 assert!(!service.matches_path("/other"));
98 }
99
100 #[test]
101 fn test_federation_config_public_api() {
102 let config = FederationConfig {
104 name: "test-fed".to_string(),
105 description: "Test federation".to_string(),
106 services: vec![FederationService {
107 name: "service1".to_string(),
108 workspace_id: Uuid::new_v4().to_string(),
109 base_path: "/service1".to_string(),
110 reality_level: "real".to_string(),
111 config: HashMap::new(),
112 dependencies: Vec::new(),
113 }],
114 };
115
116 assert_eq!(config.name, "test-fed");
117 assert_eq!(config.services.len(), 1);
118
119 let json = serde_json::to_string(&config).unwrap();
121 assert!(json.contains("test-fed"));
122 }
123
124 #[test]
125 fn test_federation_public_api() {
126 let org_id = Uuid::new_v4();
128 let config = FederationConfig {
129 name: "my-federation".to_string(),
130 description: "My test federation".to_string(),
131 services: vec![FederationService {
132 name: "auth".to_string(),
133 workspace_id: Uuid::new_v4().to_string(),
134 base_path: "/auth".to_string(),
135 reality_level: "real".to_string(),
136 config: HashMap::new(),
137 dependencies: Vec::new(),
138 }],
139 };
140
141 let federation = Federation::from_config(org_id, config).unwrap();
142
143 assert_eq!(federation.name, "my-federation");
144 assert_eq!(federation.org_id, org_id);
145 assert_eq!(federation.services.len(), 1);
146 assert_eq!(federation.services[0].name, "auth");
147 }
148
149 #[test]
150 fn test_federation_router_public_api() {
151 let org_id = Uuid::new_v4();
153 let workspace_id = Uuid::new_v4();
154
155 let config = FederationConfig {
156 name: "router-test".to_string(),
157 description: String::new(),
158 services: vec![
159 FederationService {
160 name: "auth".to_string(),
161 workspace_id: workspace_id.to_string(),
162 base_path: "/auth".to_string(),
163 reality_level: "real".to_string(),
164 config: HashMap::new(),
165 dependencies: Vec::new(),
166 },
167 FederationService {
168 name: "payments".to_string(),
169 workspace_id: Uuid::new_v4().to_string(),
170 base_path: "/payments".to_string(),
171 reality_level: "mock_v3".to_string(),
172 config: HashMap::new(),
173 dependencies: Vec::new(),
174 },
175 ],
176 };
177
178 let federation = Federation::from_config(org_id, config).unwrap();
179 let router = FederationRouter::new(Arc::new(federation));
180
181 let result = router.route("/auth/login").unwrap();
183 assert_eq!(result.service.name, "auth");
184 assert_eq!(result.workspace_id, workspace_id);
185 assert_eq!(result.service_path, "/login");
186
187 assert!(router.route("/unknown").is_none());
189 }
190
191 #[test]
192 fn test_routing_result_public_api() {
193 let workspace_id = Uuid::new_v4();
195 let service = Arc::new(ServiceBoundary::new(
196 "test".to_string(),
197 workspace_id,
198 "/test".to_string(),
199 ServiceRealityLevel::Real,
200 ));
201
202 let result = RoutingResult {
203 workspace_id,
204 service: service.clone(),
205 service_path: "/path".to_string(),
206 };
207
208 assert_eq!(result.workspace_id, workspace_id);
209 assert_eq!(result.service.name, "test");
210 assert_eq!(result.service_path, "/path");
211
212 let cloned = result.clone();
214 assert_eq!(cloned.workspace_id, result.workspace_id);
215 }
216
217 #[test]
218 fn test_federation_service_with_config() {
219 let mut config = HashMap::new();
221 config.insert("timeout".to_string(), serde_json::json!(3000));
222 config.insert("retries".to_string(), serde_json::json!(5));
223
224 let service = FederationService {
225 name: "api".to_string(),
226 workspace_id: Uuid::new_v4().to_string(),
227 base_path: "/api".to_string(),
228 reality_level: "blended".to_string(),
229 config: config.clone(),
230 dependencies: vec!["auth".to_string(), "db".to_string()],
231 };
232
233 assert_eq!(service.config.len(), 2);
234 assert_eq!(service.dependencies.len(), 2);
235
236 let json = serde_json::to_string(&service).unwrap();
238 let deserialized: FederationService = serde_json::from_str(&json).unwrap();
239 assert_eq!(deserialized.name, service.name);
240 assert_eq!(deserialized.config.len(), service.config.len());
241 }
242
243 #[test]
244 fn test_all_reality_levels_exposed() {
245 let _real = ServiceRealityLevel::Real;
247 let _mock = ServiceRealityLevel::MockV3;
248 let _blended = ServiceRealityLevel::Blended;
249 let _chaos = ServiceRealityLevel::ChaosDriven;
250
251 assert_eq!(ServiceRealityLevel::Real.as_str(), "real");
253 assert_eq!(ServiceRealityLevel::MockV3.as_str(), "mock_v3");
254 assert_eq!(ServiceRealityLevel::Blended.as_str(), "blended");
255 assert_eq!(ServiceRealityLevel::ChaosDriven.as_str(), "chaos_driven");
256 }
257
258 #[test]
259 fn test_federation_mutation_methods() {
260 let org_id = Uuid::new_v4();
262 let config = FederationConfig {
263 name: "mutable-fed".to_string(),
264 description: String::new(),
265 services: vec![FederationService {
266 name: "initial".to_string(),
267 workspace_id: Uuid::new_v4().to_string(),
268 base_path: "/initial".to_string(),
269 reality_level: "real".to_string(),
270 config: HashMap::new(),
271 dependencies: Vec::new(),
272 }],
273 };
274
275 let mut federation = Federation::from_config(org_id, config).unwrap();
276 assert_eq!(federation.services.len(), 1);
277
278 federation.add_service(ServiceBoundary::new(
280 "added".to_string(),
281 Uuid::new_v4(),
282 "/added".to_string(),
283 ServiceRealityLevel::MockV3,
284 ));
285 assert_eq!(federation.services.len(), 2);
286
287 let removed = federation.remove_service("initial");
289 assert!(removed);
290 assert_eq!(federation.services.len(), 1);
291 assert_eq!(federation.services[0].name, "added");
292 }
293
294 #[test]
295 fn test_service_path_extraction() {
296 let service = ServiceBoundary::new(
298 "api".to_string(),
299 Uuid::new_v4(),
300 "/api/v1".to_string(),
301 ServiceRealityLevel::Real,
302 );
303
304 assert_eq!(service.extract_service_path("/api/v1/users"), Some("/users".to_string()));
305 assert_eq!(service.extract_service_path("/api/v1"), Some("/".to_string()));
306 assert_eq!(service.extract_service_path("/other"), None);
307 }
308
309 #[test]
310 fn test_federation_service_lookup() {
311 let org_id = Uuid::new_v4();
313 let config = FederationConfig {
314 name: "lookup-test".to_string(),
315 description: String::new(),
316 services: vec![
317 FederationService {
318 name: "service-a".to_string(),
319 workspace_id: Uuid::new_v4().to_string(),
320 base_path: "/a".to_string(),
321 reality_level: "real".to_string(),
322 config: HashMap::new(),
323 dependencies: Vec::new(),
324 },
325 FederationService {
326 name: "service-b".to_string(),
327 workspace_id: Uuid::new_v4().to_string(),
328 base_path: "/b".to_string(),
329 reality_level: "mock_v3".to_string(),
330 config: HashMap::new(),
331 dependencies: Vec::new(),
332 },
333 ],
334 };
335
336 let federation = Federation::from_config(org_id, config).unwrap();
337
338 let service_a = federation.get_service("service-a");
340 assert!(service_a.is_some());
341 assert_eq!(service_a.unwrap().base_path, "/a");
342
343 let service_b = federation.find_service_by_path("/b/endpoint");
345 assert!(service_b.is_some());
346 assert_eq!(service_b.unwrap().name, "service-b");
347 }
348
349 #[test]
350 fn test_complete_workflow() {
351 let org_id = Uuid::new_v4();
353
354 let config = FederationConfig {
356 name: "e-commerce".to_string(),
357 description: "E-commerce platform federation".to_string(),
358 services: vec![
359 FederationService {
360 name: "auth".to_string(),
361 workspace_id: Uuid::new_v4().to_string(),
362 base_path: "/auth".to_string(),
363 reality_level: "real".to_string(),
364 config: HashMap::new(),
365 dependencies: Vec::new(),
366 },
367 FederationService {
368 name: "catalog".to_string(),
369 workspace_id: Uuid::new_v4().to_string(),
370 base_path: "/catalog".to_string(),
371 reality_level: "mock_v3".to_string(),
372 config: HashMap::new(),
373 dependencies: vec!["auth".to_string()],
374 },
375 ],
376 };
377
378 let federation = Federation::from_config(org_id, config).unwrap();
380 assert_eq!(federation.services.len(), 2);
381
382 let router = FederationRouter::new(Arc::new(federation));
384
385 let auth_route = router.route("/auth/login").unwrap();
387 assert_eq!(auth_route.service.name, "auth");
388 assert_eq!(auth_route.service.reality_level, ServiceRealityLevel::Real);
389
390 let catalog_route = router.route("/catalog/products/123").unwrap();
391 assert_eq!(catalog_route.service.name, "catalog");
392 assert_eq!(catalog_route.service_path, "/products/123");
393 assert_eq!(catalog_route.service.reality_level, ServiceRealityLevel::MockV3);
394
395 let catalog_service = router.services().iter().find(|s| s.name == "catalog").unwrap();
397 assert_eq!(catalog_service.dependencies, vec!["auth".to_string()]);
398 }
399
400 #[test]
401 fn test_yaml_config_roundtrip() {
402 let config = FederationConfig {
404 name: "yaml-test".to_string(),
405 description: "Testing YAML".to_string(),
406 services: vec![FederationService {
407 name: "test".to_string(),
408 workspace_id: Uuid::new_v4().to_string(),
409 base_path: "/test".to_string(),
410 reality_level: "real".to_string(),
411 config: HashMap::new(),
412 dependencies: Vec::new(),
413 }],
414 };
415
416 let yaml = serde_yaml::to_string(&config).unwrap();
417 let deserialized: FederationConfig = serde_yaml::from_str(&yaml).unwrap();
418
419 assert_eq!(deserialized.name, config.name);
420 assert_eq!(deserialized.description, config.description);
421 assert_eq!(deserialized.services.len(), config.services.len());
422 }
423
424 #[test]
425 fn test_json_config_roundtrip() {
426 let mut service_config = HashMap::new();
428 service_config.insert("key".to_string(), serde_json::json!("value"));
429
430 let config = FederationConfig {
431 name: "json-test".to_string(),
432 description: "Testing JSON".to_string(),
433 services: vec![FederationService {
434 name: "test".to_string(),
435 workspace_id: Uuid::new_v4().to_string(),
436 base_path: "/test".to_string(),
437 reality_level: "blended".to_string(),
438 config: service_config,
439 dependencies: vec!["dep1".to_string()],
440 }],
441 };
442
443 let json = serde_json::to_string(&config).unwrap();
444 let deserialized: FederationConfig = serde_json::from_str(&json).unwrap();
445
446 assert_eq!(deserialized.name, config.name);
447 assert_eq!(deserialized.services[0].config.len(), 1);
448 assert_eq!(deserialized.services[0].dependencies.len(), 1);
449 }
450}