use std::collections::HashMap; use std::sync::{Arc, RwLock}; use axum::{ body::Body, extract::Request, http::{HeaderMap, HeaderValue, Method, StatusCode}, response::IntoResponse, }; use mock_server::config::MockSource; use mock_server::handler::{AppState, mock_handler}; use mock_server::router::MockRouter; use tempfile::tempdir; use std::fs::{self, File}; use std::io::Write; use tokio::runtime::Runtime; /// 创建带有 Mock 规则的 AppState fn create_app_state_with_rules(rules: Vec<&str>) -> Arc { let temp_dir = tempdir().expect("无法创建临时目录"); let root_path = temp_dir.path(); let mut all_rules = Vec::new(); for (i, yaml_content) in rules.iter().enumerate() { let file_path = root_path.join(format!("rule_{}.yaml", i)); let mut file = File::create(&file_path).unwrap(); writeln!(file, "{}", yaml_content).unwrap(); let content = fs::read_to_string(&file_path).unwrap(); if let Ok(source) = serde_yaml::from_str::(&content) { all_rules.extend(source.flatten()); } } let mut index = HashMap::new(); for rule in all_rules { let key = rule.request.path.trim_start_matches('/') .split('/') .next() .unwrap_or("root") .to_string(); index.entry(key).or_insert_with(Vec::new).push(rule); } let router = MockRouter::new(index); Arc::new(AppState { router: RwLock::new(router) }) } #[test] fn test_handler_basic_request() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "basic_test" request: method: "GET" path: "/api/test" response: status: 200 body: "test response" "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let request = Request::builder() .method(Method::GET) .uri("/api/test") .body(Body::empty()) .unwrap(); let response = mock_handler( axum::extract::State(state), Method::GET, HeaderMap::new(), axum::extract::Query(HashMap::new()), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::OK); }); } #[test] fn test_handler_with_query_params() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "query_test" request: method: "GET" path: "/api/search" query_params: q: "rust" response: status: 200 body: "search results" "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let request = Request::builder() .method(Method::GET) .uri("/api/search?q=rust") .body(Body::empty()) .unwrap(); let mut query_params = HashMap::new(); query_params.insert("q".to_string(), "rust".to_string()); let response = mock_handler( axum::extract::State(state), Method::GET, HeaderMap::new(), axum::extract::Query(query_params), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::OK); }); } #[test] fn test_handler_with_headers() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "header_test" request: method: "POST" path: "/api/data" headers: Authorization: "Bearer token123" Content-Type: "application/json" response: status: 200 body: "data response" "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let request = Request::builder() .method(Method::POST) .uri("/api/data") .body(Body::empty()) .unwrap(); let mut headers = HeaderMap::new(); headers.insert("authorization", HeaderValue::from_static("Bearer token123")); headers.insert("content-type", HeaderValue::from_static("application/json")); let response = mock_handler( axum::extract::State(state), Method::POST, headers, axum::extract::Query(HashMap::new()), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::OK); }); } #[test] fn test_handler_with_body() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "body_test" request: method: "POST" path: "/api/users" body: name: "John" age: 30 response: status: 201 body: "user created" "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let body_json = serde_json::json!({ "name": "John", "age": 30 }); let body_bytes = serde_json::to_vec(&body_json).unwrap(); let request = Request::builder() .method(Method::POST) .uri("/api/users") .header("content-type", "application/json") .body(Body::from(body_bytes)) .unwrap(); let response = mock_handler( axum::extract::State(state), Method::POST, HeaderMap::new(), axum::extract::Query(HashMap::new()), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::CREATED); }); } #[test] fn test_handler_no_match() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "existing_rule" request: method: "GET" path: "/api/existing" response: status: 200 body: "exists" "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let request = Request::builder() .method(Method::GET) .uri("/api/nonexistent") .body(Body::empty()) .unwrap(); let response = mock_handler( axum::extract::State(state), Method::GET, HeaderMap::new(), axum::extract::Query(HashMap::new()), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::NOT_FOUND); }); } #[test] fn test_handler_response_headers() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "headers_test" request: method: "GET" path: "/api/with-headers" response: status: 200 headers: Content-Type: "application/json" Cache-Control: "no-cache" body: '{"key": "value"}' "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let request = Request::builder() .method(Method::GET) .uri("/api/with-headers") .body(Body::empty()) .unwrap(); let response = mock_handler( axum::extract::State(state), Method::GET, HeaderMap::new(), axum::extract::Query(HashMap::new()), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::OK); assert_eq!( response.headers().get("content-type").unwrap(), "application/json" ); assert_eq!( response.headers().get("cache-control").unwrap(), "no-cache" ); }); } #[test] fn test_handler_different_status_codes() { let rt = Runtime::new().unwrap(); let yaml1 = r#" id: "success" request: method: "GET" path: "/api/success" response: status: 200 body: "ok" "#; let yaml2 = r#" id: "created" request: method: "POST" path: "/api/created" response: status: 201 body: "created" "#; let yaml3 = r#" id: "error" request: method: "GET" path: "/api/error" response: status: 500 body: "server error" "#; let state = create_app_state_with_rules(vec![yaml1, yaml2, yaml3]); rt.block_on(async { // 测试 200 let request1 = Request::builder() .method(Method::GET) .uri("/api/success") .body(Body::empty()) .unwrap(); let response1 = mock_handler( axum::extract::State(state.clone()), Method::GET, HeaderMap::new(), axum::extract::Query(HashMap::new()), request1, ) .await .into_response(); assert_eq!(response1.status(), StatusCode::OK); // 测试 201 let request2 = Request::builder() .method(Method::POST) .uri("/api/created") .body(Body::empty()) .unwrap(); let response2 = mock_handler( axum::extract::State(state.clone()), Method::POST, HeaderMap::new(), axum::extract::Query(HashMap::new()), request2, ) .await .into_response(); assert_eq!(response2.status(), StatusCode::CREATED); // 测试 500 let request3 = Request::builder() .method(Method::GET) .uri("/api/error") .body(Body::empty()) .unwrap(); let response3 = mock_handler( axum::extract::State(state), Method::GET, HeaderMap::new(), axum::extract::Query(HashMap::new()), request3, ) .await .into_response(); assert_eq!(response3.status(), StatusCode::INTERNAL_SERVER_ERROR); }); } #[test] fn test_handler_complex_matching() { let rt = Runtime::new().unwrap(); let yaml = r#" id: "complex" request: method: "PUT" path: "/api/users/123" query_params: update: "true" headers: Authorization: "Bearer secret" body: name: "Updated Name" response: status: 200 body: "updated" "#; let state = create_app_state_with_rules(vec![yaml]); rt.block_on(async { let body_json = serde_json::json!({ "name": "Updated Name" }); let body_bytes = serde_json::to_vec(&body_json).unwrap(); let mut headers = HeaderMap::new(); headers.insert("content-type", HeaderValue::from_static("application/json")); headers.insert("authorization", HeaderValue::from_static("Bearer secret")); let request = Request::builder() .method(Method::PUT) .uri("/api/users/123") .header("content-type", "application/json") .header("authorization", "Bearer secret") .body(Body::from(body_bytes)) .unwrap(); let mut query_params = HashMap::new(); query_params.insert("update".to_string(), "true".to_string()); let response = mock_handler( axum::extract::State(state), Method::PUT, headers, axum::extract::Query(query_params), request, ) .await .into_response(); assert_eq!(response.status(), StatusCode::OK); }); }