- 将mock配置从YAML格式迁移到JSON格式 - 修复JSON字符串格式body匹配失败问题 - 添加MCP功能模块 - 更新mock-spec.md规范文档 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
227 lines
7.4 KiB
Rust
227 lines
7.4 KiB
Rust
use std::path::Path;
|
||
use std::collections::HashMap;
|
||
use serde_json::json;
|
||
use mock_server::models::Payload;
|
||
use mock_server::loader::MockLoader;
|
||
use mock_server::router::MockRouter;
|
||
|
||
/// 加载 v1 目录的 mock 规则
|
||
fn load_v1_mocks() -> HashMap<String, Vec<mock_server::models::MockRule>> {
|
||
let v1_path = Path::new("./mocks/v1");
|
||
MockLoader::load_all_from_dir(v1_path)
|
||
}
|
||
|
||
// ========== 模块一:验证所有 JSON 文件正确加载 ==========
|
||
|
||
#[test]
|
||
fn test_v1_load_all_mocks() {
|
||
let index = load_v1_mocks();
|
||
|
||
// 验证索引键存在
|
||
assert!(index.contains_key("v1"), "应包含 'v1' 索引键");
|
||
|
||
// 验证规则总数
|
||
let total: usize = index.values().map(|v| v.len()).sum();
|
||
assert!(total >= 10, "v1 目录应有至少 10 个 mock 规则");
|
||
}
|
||
|
||
// ========== 模块二:JSON Payload 测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_json_login() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Content-Type".to_string(), "application/json".to_string());
|
||
headers.insert("Authorization".to_string(), "eyJhbGciOiJIUzI1NiIsInR5cCI6".to_string());
|
||
headers.insert("host".to_string(), "127.0.0.1:8080".to_string());
|
||
|
||
let payload = Payload::Json(json!({
|
||
"username": "user001",
|
||
"password": "password123"
|
||
}));
|
||
|
||
let matched = router.match_rule("POST", "/v1/auth/login", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "JSON 登录应匹配成功");
|
||
let rule = matched.unwrap();
|
||
assert_eq!(rule.name, "user_login_001");
|
||
assert_eq!(rule.response.status, 200);
|
||
}
|
||
|
||
#[test]
|
||
fn test_v1_json_register() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Content-Type".to_string(), "application/json".to_string());
|
||
|
||
let payload = Payload::Json(json!({
|
||
"username": "newuser",
|
||
"password": "newpass123",
|
||
"email": "newuser@example.com"
|
||
}));
|
||
|
||
let matched = router.match_rule("POST", "/v1/auth/register", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "JSON 注册应匹配成功");
|
||
let rule = matched.unwrap();
|
||
assert_eq!(rule.name, "user_register");
|
||
assert_eq!(rule.response.status, 201);
|
||
}
|
||
|
||
// ========== 模块三:Form Payload 测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_form_login() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Content-Type".to_string(), "application/x-www-form-urlencoded".to_string());
|
||
|
||
let mut form_data = HashMap::new();
|
||
form_data.insert("username".to_string(), "formuser".to_string());
|
||
form_data.insert("password".to_string(), "formpass".to_string());
|
||
let payload = Payload::Form(form_data);
|
||
|
||
let matched = router.match_rule("POST", "/v1/user/login/form", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "Form 登录应匹配成功");
|
||
assert_eq!(matched.unwrap().name, "_user_login_form");
|
||
}
|
||
|
||
// ========== 模块四:Text Payload 测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_text_echo() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Content-Type".to_string(), "text/plain".to_string());
|
||
|
||
let payload = Payload::Text("Hello V1 Mock Server".to_string());
|
||
|
||
let matched = router.match_rule("POST", "/v1/user/echo", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "Text 回显应匹配成功");
|
||
let rule = matched.unwrap();
|
||
assert_eq!(rule.name, "user_echo");
|
||
assert!(rule.response.body.contains("Echo from V1"));
|
||
}
|
||
|
||
// ========== 模块五:XML Payload 测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_xml_export() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Content-Type".to_string(), "application/xml".to_string());
|
||
|
||
let xml_body = r#"<?xml version="1.0" encoding="UTF-8"?><request><userId>10001</userId><format>xml</format></request>"#;
|
||
let payload = Payload::Xml(xml_body.to_string());
|
||
|
||
let matched = router.match_rule("POST", "/v1/data/export", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "XML 导出应匹配成功");
|
||
assert_eq!(matched.unwrap().name, "data_export");
|
||
}
|
||
|
||
// ========== 模块六:Multipart Payload 测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_multipart_upload() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Content-Type".to_string(), "multipart/form-data".to_string());
|
||
|
||
let mut multipart_data = HashMap::new();
|
||
multipart_data.insert("avatar1".to_string(), "file_content".to_string());
|
||
multipart_data.insert("description1".to_string(), "user avatar".to_string());
|
||
let payload = Payload::Multipart(multipart_data);
|
||
|
||
let matched = router.match_rule("POST", "/v1/user/avatar", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "Multipart 上传应匹配成功");
|
||
assert_eq!(matched.unwrap().name, "user_upload_avatar_001");
|
||
}
|
||
|
||
// ========== 模块七:None Payload 测试 (GET 无 Body) ==========
|
||
|
||
#[test]
|
||
fn test_v1_health_check() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let payload = Payload::None;
|
||
|
||
let matched = router.match_rule("GET", "/v1/health", &HashMap::new(), &HashMap::new(), &payload);
|
||
|
||
assert!(matched.is_some(), "健康检查应匹配成功");
|
||
let rule = matched.unwrap();
|
||
assert_eq!(rule.name, "health_check");
|
||
assert_eq!(rule.response.status, 200);
|
||
}
|
||
|
||
#[test]
|
||
fn test_v1_get_profile() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Authorization".to_string(), "Bearer v1_test_token".to_string());
|
||
|
||
let payload = Payload::None;
|
||
|
||
let matched = router.match_rule("GET", "/v1/user/profile", &HashMap::new(), &headers, &payload);
|
||
|
||
assert!(matched.is_some(), "获取用户信息应匹配成功");
|
||
assert_eq!(matched.unwrap().name, "user_profile");
|
||
}
|
||
|
||
// ========== 模块八:Query Params 测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_query_params() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
let mut query = HashMap::new();
|
||
query.insert("format".to_string(), "json".to_string());
|
||
|
||
let payload = Payload::None;
|
||
|
||
let matched = router.match_rule("GET", "/v1/user/download", &query, &HashMap::new(), &payload);
|
||
|
||
assert!(matched.is_some(), "带 query params 的下载应匹配成功");
|
||
let rule = matched.unwrap();
|
||
assert_eq!(rule.name, "user_download");
|
||
assert!(rule.response.body.starts_with("file://"));
|
||
}
|
||
|
||
// ========== 模块九:Header 匹配测试 ==========
|
||
|
||
#[test]
|
||
fn test_v1_header_required() {
|
||
let index = load_v1_mocks();
|
||
let router = MockRouter::new(index);
|
||
|
||
// 无 Authorization header 不应匹配
|
||
let payload = Payload::None;
|
||
let no_match = router.match_rule("GET", "/v1/user/profile", &HashMap::new(), &HashMap::new(), &payload);
|
||
assert!(no_match.is_none(), "无 Authorization 不应匹配");
|
||
|
||
// 有正确的 Authorization header 应匹配
|
||
let mut headers = HashMap::new();
|
||
headers.insert("Authorization".to_string(), "Bearer v1_test_token".to_string());
|
||
let matched = router.match_rule("GET", "/v1/user/profile", &HashMap::new(), &headers, &payload);
|
||
assert!(matched.is_some(), "有 Authorization 应匹配");
|
||
}
|