use mockforge_core::protocol_abstraction::{
MessagePattern, Protocol, ProtocolRequest, SpecRegistry,
};
use mockforge_grpc::dynamic::{discover_services, DynamicGrpcConfig};
use mockforge_grpc::registry::GrpcProtoRegistry;
use std::collections::HashMap;
fn proto_dir() -> String {
let manifest_dir =
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should be set in tests");
format!("{}/proto", manifest_dir)
}
#[tokio::test]
async fn test_grpc_service_discovery_from_proto_files() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let config = DynamicGrpcConfig {
proto_dir: dir,
enable_reflection: true,
excluded_services: vec![],
http_bridge: None,
tls: None,
};
let registry = discover_services(&config).await.expect("Service discovery should succeed");
let services = registry.service_names();
assert!(!services.is_empty(), "Should discover at least one service from proto files");
let has_greeter = services.iter().any(|s| s.contains("Greeter"));
assert!(has_greeter, "Should discover the Greeter service, found: {:?}", services);
}
#[tokio::test]
async fn test_grpc_service_discovery_with_exclusion() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let config = DynamicGrpcConfig {
proto_dir: dir,
enable_reflection: false,
excluded_services: vec!["mockforge.greeter.Greeter".to_string()],
http_bridge: None,
tls: None,
};
let registry = discover_services(&config).await.expect("Service discovery should succeed");
let services = registry.service_names();
let has_greeter = services.iter().any(|s| s.contains("Greeter"));
assert!(!has_greeter, "Greeter service should be excluded, found: {:?}", services);
}
#[tokio::test]
async fn test_grpc_registry_operations_from_proto() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let registry = GrpcProtoRegistry::from_directory(&dir)
.await
.expect("Should create registry from proto directory");
let operations = registry.operations();
assert!(!operations.is_empty(), "Should have operations from the Greeter service");
let say_hello = operations.iter().find(|op| op.name == "SayHello");
assert!(say_hello.is_some(), "Should find SayHello operation");
let say_hello = say_hello.unwrap();
assert_eq!(say_hello.operation_type, "Unary");
assert!(say_hello.input_schema.is_some());
assert!(say_hello.output_schema.is_some());
let stream_op = operations.iter().find(|op| op.name == "SayHelloStream");
assert!(stream_op.is_some(), "Should find SayHelloStream operation");
assert_eq!(stream_op.unwrap().operation_type, "ServerStreaming");
let client_stream_op = operations.iter().find(|op| op.name == "SayHelloClientStream");
assert!(client_stream_op.is_some(), "Should find SayHelloClientStream operation");
assert_eq!(client_stream_op.unwrap().operation_type, "ClientStreaming");
let chat_op = operations.iter().find(|op| op.name == "Chat");
assert!(chat_op.is_some(), "Should find Chat operation");
assert_eq!(chat_op.unwrap().operation_type, "BidirectionalStreaming");
}
#[tokio::test]
async fn test_grpc_mock_response_generation() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let registry = GrpcProtoRegistry::from_directory(&dir)
.await
.expect("Should create registry from proto directory");
let operations = registry.operations();
assert!(!operations.is_empty(), "Should have operations");
let say_hello = operations
.iter()
.find(|op| op.name == "SayHello")
.expect("Should find SayHello operation");
let request = ProtocolRequest {
protocol: Protocol::Grpc,
pattern: MessagePattern::RequestResponse,
operation: say_hello.path.clone(),
path: say_hello.path.clone(),
metadata: HashMap::new(),
body: None,
..Default::default()
};
let response = registry
.generate_mock_response(&request)
.expect("Should generate mock response");
assert_eq!(response.content_type, "application/grpc+json");
assert!(!response.body.is_empty(), "Response body should not be empty");
let body: serde_json::Value =
serde_json::from_slice(&response.body).expect("Response body should be valid JSON");
assert!(body.is_object(), "Response should be a JSON object");
}
#[tokio::test]
async fn test_grpc_validate_request_known_operation() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let registry = GrpcProtoRegistry::from_directory(&dir)
.await
.expect("Should create registry from proto directory");
let operations = registry.operations();
let say_hello = operations
.iter()
.find(|op| op.name == "SayHello")
.expect("Should find SayHello operation");
let request = ProtocolRequest {
protocol: Protocol::Grpc,
pattern: MessagePattern::RequestResponse,
operation: say_hello.path.clone(),
path: say_hello.path.clone(),
metadata: HashMap::new(),
body: None,
..Default::default()
};
let result = registry.validate_request(&request).expect("Validation should not error");
assert!(result.valid, "Known operation should pass validation");
}
#[tokio::test]
async fn test_grpc_validate_request_unknown_operation() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let registry = GrpcProtoRegistry::from_directory(&dir)
.await
.expect("Should create registry from proto directory");
let request = ProtocolRequest {
protocol: Protocol::Grpc,
pattern: MessagePattern::RequestResponse,
operation: "nonexistent.Service/DoSomething".to_string(),
path: "nonexistent.Service/DoSomething".to_string(),
metadata: HashMap::new(),
body: None,
..Default::default()
};
let result = registry.validate_request(&request).expect("Validation should not error");
assert!(!result.valid, "Unknown operation should fail validation");
assert!(!result.errors.is_empty(), "Should have validation errors for unknown operation");
}
#[tokio::test]
async fn test_grpc_descriptor_pool_accessible() {
let dir = proto_dir();
assert!(std::path::Path::new(&dir).exists(), "Proto directory not found at {}", dir);
let config = DynamicGrpcConfig {
proto_dir: dir,
enable_reflection: true,
excluded_services: vec![],
http_bridge: None,
tls: None,
};
let registry = discover_services(&config).await.expect("Service discovery should succeed");
let pool = registry.descriptor_pool();
let hello_reply = pool.get_message_by_name("mockforge.greeter.HelloReply");
assert!(hello_reply.is_some(), "Descriptor pool should contain HelloReply message");
let descriptor = hello_reply.unwrap();
let field_names: Vec<String> = descriptor.fields().map(|f| f.name().to_string()).collect();
assert!(
field_names.iter().any(|n| n == "message"),
"HelloReply should have a 'message' field"
);
assert!(
field_names.iter().any(|n| n == "metadata"),
"HelloReply should have a 'metadata' field"
);
assert!(
field_names.iter().any(|n| n == "items"),
"HelloReply should have an 'items' field"
);
}