pub mod database;
pub mod federation;
pub mod router;
pub mod service;
pub use database::FederationDatabase;
pub use federation::{Federation, FederationConfig, FederationService};
pub use router::{FederationRouter, RoutingResult};
pub use service::{ServiceBoundary, ServiceRealityLevel};
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use std::sync::Arc;
use uuid::Uuid;
#[test]
fn test_service_reality_level_public_api() {
let level = ServiceRealityLevel::Real;
assert_eq!(level.as_str(), "real");
let parsed = ServiceRealityLevel::from_str("mock_v3");
assert_eq!(parsed, Some(ServiceRealityLevel::MockV3));
}
#[test]
fn test_service_boundary_public_api() {
let workspace_id = Uuid::new_v4();
let service = ServiceBoundary::new(
"test-service".to_string(),
workspace_id,
"/api".to_string(),
ServiceRealityLevel::Blended,
);
assert_eq!(service.name, "test-service");
assert_eq!(service.workspace_id, workspace_id);
assert_eq!(service.base_path, "/api");
assert_eq!(service.reality_level, ServiceRealityLevel::Blended);
assert!(service.matches_path("/api/users"));
assert!(!service.matches_path("/other"));
}
#[test]
fn test_federation_config_public_api() {
let config = FederationConfig {
name: "test-fed".to_string(),
description: "Test federation".to_string(),
services: vec![FederationService {
name: "service1".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/service1".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
}],
};
assert_eq!(config.name, "test-fed");
assert_eq!(config.services.len(), 1);
let json = serde_json::to_string(&config).unwrap();
assert!(json.contains("test-fed"));
}
#[test]
fn test_federation_public_api() {
let org_id = Uuid::new_v4();
let config = FederationConfig {
name: "my-federation".to_string(),
description: "My test federation".to_string(),
services: vec![FederationService {
name: "auth".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/auth".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
}],
};
let federation = Federation::from_config(org_id, config).unwrap();
assert_eq!(federation.name, "my-federation");
assert_eq!(federation.org_id, org_id);
assert_eq!(federation.services.len(), 1);
assert_eq!(federation.services[0].name, "auth");
}
#[test]
fn test_federation_router_public_api() {
let org_id = Uuid::new_v4();
let workspace_id = Uuid::new_v4();
let config = FederationConfig {
name: "router-test".to_string(),
description: String::new(),
services: vec![
FederationService {
name: "auth".to_string(),
workspace_id: workspace_id.to_string(),
base_path: "/auth".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
},
FederationService {
name: "payments".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/payments".to_string(),
reality_level: "mock_v3".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
},
],
};
let federation = Federation::from_config(org_id, config).unwrap();
let router = FederationRouter::new(Arc::new(federation));
let result = router.route("/auth/login").unwrap();
assert_eq!(result.service.name, "auth");
assert_eq!(result.workspace_id, workspace_id);
assert_eq!(result.service_path, "/login");
assert!(router.route("/unknown").is_none());
}
#[test]
fn test_routing_result_public_api() {
let workspace_id = Uuid::new_v4();
let service = Arc::new(ServiceBoundary::new(
"test".to_string(),
workspace_id,
"/test".to_string(),
ServiceRealityLevel::Real,
));
let result = RoutingResult {
workspace_id,
service,
service_path: "/path".to_string(),
};
assert_eq!(result.workspace_id, workspace_id);
assert_eq!(result.service.name, "test");
assert_eq!(result.service_path, "/path");
let cloned = result.clone();
assert_eq!(cloned.workspace_id, result.workspace_id);
}
#[test]
fn test_federation_service_with_config() {
let mut config = HashMap::new();
config.insert("timeout".to_string(), serde_json::json!(3000));
config.insert("retries".to_string(), serde_json::json!(5));
let service = FederationService {
name: "api".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/api".to_string(),
reality_level: "blended".to_string(),
config: config.clone(),
dependencies: vec!["auth".to_string(), "db".to_string()],
};
assert_eq!(service.config.len(), 2);
assert_eq!(service.dependencies.len(), 2);
let json = serde_json::to_string(&service).unwrap();
let deserialized: FederationService = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.name, service.name);
assert_eq!(deserialized.config.len(), service.config.len());
}
#[test]
fn test_all_reality_levels_exposed() {
assert_eq!(ServiceRealityLevel::Real.as_str(), "real");
assert_eq!(ServiceRealityLevel::MockV3.as_str(), "mock_v3");
assert_eq!(ServiceRealityLevel::Blended.as_str(), "blended");
assert_eq!(ServiceRealityLevel::ChaosDriven.as_str(), "chaos_driven");
assert_eq!(ServiceRealityLevel::Real.as_str(), "real");
assert_eq!(ServiceRealityLevel::MockV3.as_str(), "mock_v3");
assert_eq!(ServiceRealityLevel::Blended.as_str(), "blended");
assert_eq!(ServiceRealityLevel::ChaosDriven.as_str(), "chaos_driven");
}
#[test]
fn test_federation_mutation_methods() {
let org_id = Uuid::new_v4();
let config = FederationConfig {
name: "mutable-fed".to_string(),
description: String::new(),
services: vec![FederationService {
name: "initial".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/initial".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
}],
};
let mut federation = Federation::from_config(org_id, config).unwrap();
assert_eq!(federation.services.len(), 1);
federation.add_service(ServiceBoundary::new(
"added".to_string(),
Uuid::new_v4(),
"/added".to_string(),
ServiceRealityLevel::MockV3,
));
assert_eq!(federation.services.len(), 2);
let removed = federation.remove_service("initial");
assert!(removed);
assert_eq!(federation.services.len(), 1);
assert_eq!(federation.services[0].name, "added");
}
#[test]
fn test_service_path_extraction() {
let service = ServiceBoundary::new(
"api".to_string(),
Uuid::new_v4(),
"/api/v1".to_string(),
ServiceRealityLevel::Real,
);
assert_eq!(service.extract_service_path("/api/v1/users"), Some("/users".to_string()));
assert_eq!(service.extract_service_path("/api/v1"), Some("/".to_string()));
assert_eq!(service.extract_service_path("/other"), None);
}
#[test]
fn test_federation_service_lookup() {
let org_id = Uuid::new_v4();
let config = FederationConfig {
name: "lookup-test".to_string(),
description: String::new(),
services: vec![
FederationService {
name: "service-a".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/a".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
},
FederationService {
name: "service-b".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/b".to_string(),
reality_level: "mock_v3".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
},
],
};
let federation = Federation::from_config(org_id, config).unwrap();
let service_a = federation.get_service("service-a");
assert!(service_a.is_some());
assert_eq!(service_a.unwrap().base_path, "/a");
let service_b = federation.find_service_by_path("/b/endpoint");
assert!(service_b.is_some());
assert_eq!(service_b.unwrap().name, "service-b");
}
#[test]
fn test_complete_workflow() {
let org_id = Uuid::new_v4();
let config = FederationConfig {
name: "e-commerce".to_string(),
description: "E-commerce platform federation".to_string(),
services: vec![
FederationService {
name: "auth".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/auth".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
},
FederationService {
name: "catalog".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/catalog".to_string(),
reality_level: "mock_v3".to_string(),
config: HashMap::new(),
dependencies: vec!["auth".to_string()],
},
],
};
let federation = Federation::from_config(org_id, config).unwrap();
assert_eq!(federation.services.len(), 2);
let router = FederationRouter::new(Arc::new(federation));
let auth_route = router.route("/auth/login").unwrap();
assert_eq!(auth_route.service.name, "auth");
assert_eq!(auth_route.service.reality_level, ServiceRealityLevel::Real);
let catalog_route = router.route("/catalog/products/123").unwrap();
assert_eq!(catalog_route.service.name, "catalog");
assert_eq!(catalog_route.service_path, "/products/123");
assert_eq!(catalog_route.service.reality_level, ServiceRealityLevel::MockV3);
let catalog_service = router.services().iter().find(|s| s.name == "catalog").unwrap();
assert_eq!(catalog_service.dependencies, vec!["auth".to_string()]);
}
#[test]
fn test_yaml_config_roundtrip() {
let config = FederationConfig {
name: "yaml-test".to_string(),
description: "Testing YAML".to_string(),
services: vec![FederationService {
name: "test".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/test".to_string(),
reality_level: "real".to_string(),
config: HashMap::new(),
dependencies: Vec::new(),
}],
};
let yaml = serde_yaml::to_string(&config).unwrap();
let deserialized: FederationConfig = serde_yaml::from_str(&yaml).unwrap();
assert_eq!(deserialized.name, config.name);
assert_eq!(deserialized.description, config.description);
assert_eq!(deserialized.services.len(), config.services.len());
}
#[test]
fn test_json_config_roundtrip() {
let mut service_config = HashMap::new();
service_config.insert("key".to_string(), serde_json::json!("value"));
let config = FederationConfig {
name: "json-test".to_string(),
description: "Testing JSON".to_string(),
services: vec![FederationService {
name: "test".to_string(),
workspace_id: Uuid::new_v4().to_string(),
base_path: "/test".to_string(),
reality_level: "blended".to_string(),
config: service_config,
dependencies: vec!["dep1".to_string()],
}],
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: FederationConfig = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.name, config.name);
assert_eq!(deserialized.services[0].config.len(), 1);
assert_eq!(deserialized.services[0].dependencies.len(), 1);
}
}