use axum::http::{HeaderMap, Method, Uri};
use mockforge_core::stateful_handler::{
ResourceIdExtract, StateResponse, StatefulConfig, StatefulResponseHandler, TransitionTrigger,
};
use std::collections::HashMap;
#[tokio::test]
async fn test_stateful_handler_new() {
let handler = StatefulResponseHandler::new();
assert!(handler.is_ok());
}
#[tokio::test]
async fn test_can_handle_no_configs() {
let handler = StatefulResponseHandler::new().unwrap();
assert!(!handler.can_handle(&Method::GET, "/api/users").await);
}
#[tokio::test]
async fn test_can_handle_with_config() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::PathParam {
param: "id".to_string(),
},
resource_type: "order".to_string(),
state_responses,
transitions: vec![],
};
handler.add_config("/orders/{id}".to_string(), config).await;
assert!(handler.can_handle(&Method::GET, "/orders/123").await);
assert!(!handler.can_handle(&Method::GET, "/api/users").await);
}
#[tokio::test]
async fn test_process_request_no_config() {
let handler = StatefulResponseHandler::new().unwrap();
let uri: Uri = "/api/users".parse().unwrap();
let headers = HeaderMap::new();
let result = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[tokio::test]
async fn test_process_request_with_config_path_param() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: r#"{"state": "initial", "id": "test"}"#.to_string(),
content_type: "application/json".to_string(),
},
);
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::PathParam {
param: "id".to_string(),
},
resource_type: "order".to_string(),
state_responses,
transitions: vec![],
};
handler.add_config("/orders/{id}".to_string(), config).await;
let uri: Uri = "/orders/123".parse().unwrap();
let headers = HeaderMap::new();
let result = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.is_some());
let response = response.unwrap();
assert_eq!(response.status_code, 200);
assert_eq!(response.state, "initial");
assert_eq!(response.resource_id, "123");
}
#[tokio::test]
async fn test_process_request_with_header_extraction() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::Header {
name: "x-resource-id".to_string(),
},
resource_type: "order".to_string(),
state_responses,
transitions: vec![],
};
handler.add_config("/test".to_string(), config).await;
let uri: Uri = "/test".parse().unwrap();
let mut headers = HeaderMap::new();
headers.insert("x-resource-id", "header-123".parse().unwrap());
let result = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.is_some());
assert_eq!(response.unwrap().resource_id, "header-123");
}
#[tokio::test]
async fn test_process_request_with_query_param_extraction() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::QueryParam {
param: "id".to_string(),
},
resource_type: "order".to_string(),
state_responses,
transitions: vec![],
};
handler.add_config("/test".to_string(), config).await;
let uri: Uri = "/test?id=query-456".parse().unwrap();
let headers = HeaderMap::new();
let result = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.is_some());
assert_eq!(response.unwrap().resource_id, "query-456");
}
#[tokio::test]
async fn test_process_request_with_json_path_extraction() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::JsonPath {
path: "user.id".to_string(),
},
resource_type: "order".to_string(),
state_responses,
transitions: vec![],
};
handler.add_config("/test".to_string(), config).await;
let uri: Uri = "/test".parse().unwrap();
let headers = HeaderMap::new();
let body = Some(r#"{"user": {"id": "json-789"}}"#.as_bytes());
let result = handler.process_request(&Method::POST, &uri, &headers, body).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.is_some());
assert_eq!(response.unwrap().resource_id, "json-789");
}
#[tokio::test]
async fn test_process_request_with_composite_extraction() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::Composite {
extractors: vec![
ResourceIdExtract::PathParam {
param: "id".to_string(),
},
ResourceIdExtract::Header {
name: "x-resource-id".to_string(),
},
],
},
resource_type: "order".to_string(),
state_responses,
transitions: vec![],
};
handler.add_config("/orders/{id}".to_string(), config).await;
let uri: Uri = "/orders/123".parse().unwrap();
let headers = HeaderMap::new();
let result = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result.is_ok());
let response = result.unwrap();
assert!(response.is_some());
assert_eq!(response.unwrap().resource_id, "123");
}
#[tokio::test]
async fn test_process_request_with_transition() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses = HashMap::new();
state_responses.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
state_responses.insert(
"processed".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let transitions = vec![TransitionTrigger {
method: Method::POST,
path_pattern: "/orders/{id}".to_string(), from_state: "initial".to_string(),
to_state: "processed".to_string(),
condition: None,
}];
let config = StatefulConfig {
resource_id_extract: ResourceIdExtract::PathParam {
param: "id".to_string(),
},
resource_type: "order".to_string(),
state_responses,
transitions,
};
handler.add_config("/orders/{id}".to_string(), config).await;
let uri: Uri = "/orders/123".parse().unwrap();
let headers = HeaderMap::new();
let result1 = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result1.is_ok());
let response1 = result1.unwrap();
assert!(response1.is_some());
assert_eq!(response1.unwrap().state, "initial");
let result2 = handler.process_request(&Method::POST, &uri, &headers, None).await;
assert!(result2.is_ok());
let response2 = result2.unwrap();
assert!(response2.is_some());
assert_eq!(response2.unwrap().state, "processed");
let result3 = handler.process_request(&Method::GET, &uri, &headers, None).await;
assert!(result3.is_ok());
let response3 = result3.unwrap();
assert!(response3.is_some());
assert_eq!(response3.unwrap().state, "processed");
}
#[tokio::test]
async fn test_multiple_configs() {
let handler = StatefulResponseHandler::new().unwrap();
let mut state_responses1 = HashMap::new();
state_responses1.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config1 = StatefulConfig {
resource_id_extract: ResourceIdExtract::PathParam {
param: "id".to_string(),
},
resource_type: "order".to_string(),
state_responses: state_responses1,
transitions: vec![],
};
let mut state_responses2 = HashMap::new();
state_responses2.insert(
"initial".to_string(),
StateResponse {
status_code: 200,
headers: HashMap::new(),
body_template: "{}".to_string(),
content_type: "application/json".to_string(),
},
);
let config2 = StatefulConfig {
resource_id_extract: ResourceIdExtract::PathParam {
param: "id".to_string(),
},
resource_type: "user".to_string(),
state_responses: state_responses2,
transitions: vec![],
};
handler.add_config("/orders/{id}".to_string(), config1).await;
handler.add_config("/users/{id}".to_string(), config2).await;
assert!(handler.can_handle(&Method::GET, "/orders/123").await);
assert!(handler.can_handle(&Method::GET, "/users/456").await);
assert!(!handler.can_handle(&Method::GET, "/products/789").await);
}