添加qiming-rcoder模块

This commit is contained in:
Codex
2026-06-01 13:54:52 +08:00
parent 8092c4b1f8
commit 4b1a580132
539 changed files with 151650 additions and 0 deletions

View File

@@ -0,0 +1,399 @@
//! 集成测试模块
//!
//! 提供代理功能的集成测试和端到端测试。
use axum::{
body::Body,
http::{Method, Request, StatusCode},
response::Response,
};
use std::collections::HashMap;
use std::time::Duration;
use tokio::time::sleep;
use crate::config::ProxyConfig;
use crate::service::PortProxyService;
use crate::server::ProxyServer;
/// 创建测试配置
fn create_test_config() -> ProxyConfig {
ProxyConfig {
listen_port: 0, // 使用随机可用端口
default_backend_port: 3000,
backend_host: "127.0.0.1".to_string(),
port_param: "port".to_string(),
config_file: None,
verbose: false,
}
}
/// 创建完整的测试服务器配置
fn create_test_server_config(listen_port: u16) -> ProxyConfig {
ProxyConfig {
listen_port,
default_backend_port: 3000,
backend_host: "127.0.0.1".to_string(),
port_param: "port".to_string(),
config_file: None,
verbose: false,
}
}
#[tokio::test]
async fn test_config_validation() {
// 测试有效配置
let valid_config = ProxyConfig::default();
assert!(valid_config.validate().is_ok());
// 测试无效监听端口
let mut invalid_config = ProxyConfig::default();
invalid_config.listen_port = 0;
assert!(invalid_config.validate().is_err());
// 测试无效后端端口
let mut invalid_config = ProxyConfig::default();
invalid_config.default_backend_port = 0;
assert!(invalid_config.validate().is_err());
// 测试空主机地址
let mut invalid_config = ProxyConfig::default();
invalid_config.backend_host = String::new();
assert!(invalid_config.validate().is_err());
// 测试空端口参数名
let mut invalid_config = ProxyConfig::default();
invalid_config.port_param = String::new();
assert!(invalid_config.validate().is_err());
}
#[tokio::test]
async fn test_service_backend_management() {
let config = create_test_config();
let service = PortProxyService::new(config);
// 初始状态应该有默认后端
assert_eq!(service.backend_count().await, 1);
assert!(service.has_backend(3000).await);
assert!(!service.has_backend(3001).await);
// 添加后端
service.add_backend(3001, "localhost".to_string()).await;
assert_eq!(service.backend_count().await, 2);
assert!(service.has_backend(3001).await);
// 获取后端列表
let backends = service.list_backends().await;
assert_eq!(backends.len(), 2);
assert!(backends.contains_key(&3000));
assert!(backends.contains_key(&3001));
// 移除后端
service.remove_backend(3001).await;
assert_eq!(service.backend_count().await, 1);
assert!(!service.has_backend(3001).await);
}
#[tokio::test]
async fn test_port_extraction_scenarios() {
let config = create_test_config();
let service = PortProxyService::new(config);
// 测试场景1: 从查询参数提取端口
let request = Request::builder()
.uri("/api/users?port=8080&format=json")
.body(Body::empty())
.unwrap();
let port = service.extract_target_port(&request).unwrap();
assert_eq!(port, 8080);
// 测试场景2: 从路径提取端口
let request = Request::builder()
.uri("/proxy/9000/api/v1/data")
.body(Body::empty())
.unwrap();
let port = service.extract_target_port(&request).unwrap();
assert_eq!(port, 9000);
// 测试场景3: 多个查询参数
let request = Request::builder()
.uri("/api/search?q=test&port=7000&page=1")
.body(Body::empty())
.unwrap();
let port = service.extract_target_port(&request).unwrap();
assert_eq!(port, 7000);
// 测试场景4: 端口参数格式错误
let request = Request::builder()
.uri("/api/data?port=invalid")
.body(Body::empty())
.unwrap();
let port = service.extract_target_port(&request).unwrap();
assert_eq!(port, 3000); // 应该回退到默认端口
// 测试场景5: 路径格式错误
let request = Request::builder()
.uri("/proxy/not_a_number/api")
.body(Body::empty())
.unwrap();
let port = service.extract_target_port(&request).unwrap();
assert_eq!(port, 3000); // 应该回退到默认端口
// 测试场景6: 没有端口参数
let request = Request::builder()
.uri("/api/data")
.body(Body::empty())
.unwrap();
let port = service.extract_target_port(&request).unwrap();
assert_eq!(port, 3000); // 应该使用默认端口
}
#[tokio::test]
async fn test_uri_building() {
let config = create_test_config();
let service = PortProxyService::new(config);
// 测试普通路径
let uri = service.build_target_uri("localhost", 3000, "/api/data").unwrap();
assert_eq!(uri, "http://localhost:3000/api/data");
// 测试带查询参数的路径
let uri = service.build_target_uri("localhost", 3000, "/api/data?format=json").unwrap();
assert_eq!(uri, "http://localhost:3000/api/data?format=json");
// 测试代理路径前缀
let uri = service.build_target_uri("localhost", 8080, "/proxy/8080/api/data").unwrap();
assert_eq!(uri, "http://localhost:8080/api/data");
// 测试代理路径前缀带查询参数
let uri = service.build_target_uri("localhost", 8080, "/proxy/8080/api/data?format=json").unwrap();
assert_eq!(uri, "http://localhost:8080/api/data?format=json");
// 测试根路径
let uri = service.build_target_uri("localhost", 3000, "/").unwrap();
assert_eq!(uri, "http://localhost:3000/");
// 测试代理根路径
let uri = service.build_target_uri("localhost", 8080, "/proxy/8080/").unwrap();
assert_eq!(uri, "http://localhost:8080/");
}
#[tokio::test]
async fn test_server_builder() {
// 测试默认构建器
let server = ProxyServerBuilder::new().build();
assert_eq!(server.listen_port(), 8080);
assert_eq!(server.default_backend_port(), 3000);
// 测试自定义配置
let server = ProxyServerBuilder::new()
.listen_port(9000)
.default_backend_port(4000)
.backend_host("example.com")
.port_param("target")
.verbose(true)
.build();
assert_eq!(server.listen_port(), 9000);
assert_eq!(server.default_backend_port(), 4000);
assert_eq!(server.config().backend_host, "example.com");
assert_eq!(server.config().port_param, "target");
assert!(server.config().verbose);
}
#[tokio::test]
async fn test_server_convenience_methods() {
// 测试默认服务器
let server = ProxyServer::default();
assert_eq!(server.listen_port(), 8080);
assert_eq!(server.default_backend_port(), 3000);
// 测试自定义端口
let server = ProxyServer::with_listen_port(9090);
assert_eq!(server.listen_port(), 9090);
assert_eq!(server.default_backend_port(), 3000);
// 测试链式配置
let server = ProxyServer::with_listen_port(8080)
.with_backend_host("localhost")
.with_port_param("port")
.with_default_backend_port(3000);
assert_eq!(server.config().backend_host, "localhost");
assert_eq!(server.config().port_param, "port");
assert_eq!(server.config().default_backend_port, 3000);
}
#[tokio::test]
async fn test_server_pre_start_check() {
let server = ProxyServer::default();
// 默认配置应该通过预启动检查
let result = server.pre_start_check().await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_proxy_headers() {
let config = create_test_config();
let service = PortProxyService::new(config);
let mut request = Request::builder()
.uri("/api/data")
.body(Body::empty())
.unwrap();
// 添加代理头
service.add_proxy_headers(&mut request).unwrap();
// 验证头信息
assert_eq!(request.headers().get("X-Forwarded-Proto").unwrap(), "http");
assert_eq!(request.headers().get("X-Port-Proxy").unwrap(), "simple-proxy");
}
// 这个测试需要实际的网络连接,只在集成测试中运行
#[ignore]
#[tokio::test]
async fn test_end_to_end_proxy_request() {
// 注意这个测试需要有一个在端口3000运行的实际服务
// 在实际运行时需要先启动一个简单的HTTP服务器
let config = create_test_server_config(8080);
let service = PortProxyService::new(config);
// 创建测试请求
let request = Request::builder()
.method(Method::GET)
.uri("/?port=3000")
.body(Body::empty())
.unwrap();
// 尝试代理请求(可能会失败,因为可能没有实际的服务在运行)
let result = service.proxy_request(request).await;
// 检查是否正确解析了端口(即使连接失败)
// 这主要验证端口提取和URI构建逻辑
match result {
Ok(_) => println!("proxyrequestsucceeded"),
Err(e) => println!("proxyrequestfailed( message ): {}", e),
}
}
// 性能测试
#[tokio::test]
async fn test_concurrent_backend_operations() {
let config = create_test_config();
let service = PortProxyService::new(config);
// 并发添加多个后端
let mut handles = Vec::new();
for i in 1..=10 {
let service_clone = service.clone();
let handle = tokio::spawn(async move {
service_clone.add_backend(3000 + i, format!("host{}", i)).await;
});
handles.push(handle);
}
// 等待所有操作完成
for handle in handles {
handle.await.unwrap();
}
// 验证所有后端都已添加
assert_eq!(service.backend_count().await, 11); // 1个默认 + 10个新增
// 并发读取后端列表
let mut handles = Vec::new();
for _ in 0..5 {
let service_clone = service.clone();
let handle = tokio::spawn(async move {
service_clone.list_backends().await
});
handles.push(handle);
}
// 验证并发读取的一致性
for handle in handles {
let backends = handle.await.unwrap();
assert_eq!(backends.len(), 11);
}
}
// 边界条件测试
#[tokio::test]
async fn test_edge_cases() {
let config = create_test_config();
let service = PortProxyService::new(config);
// 测试添加和移除相同的端口
service.add_backend(3001, "host1".to_string()).await;
service.add_backend(3001, "host2".to_string()).await; // 覆盖
let backend = service.get_target_backend(3001).await.unwrap();
assert_eq!(backend, "host2"); // 应该是最后的值
// 测试移除不存在的端口
service.remove_backend(9999).await; // 不应该崩溃
// 测试获取不存在的后端
let result = service.get_target_backend(9999).await;
assert!(result.is_err());
// 测试空配置
let empty_config = ProxyConfig {
listen_port: 8080,
default_backend_port: 0, // 无效端口
backend_host: "127.0.0.1".to_string(),
port_param: "port".to_string(),
config_file: None,
verbose: false,
};
assert!(empty_config.validate().is_err());
}
#[test]
fn test_config_default_methods() {
// 测试默认配置
let default_config = ProxyConfig::default();
assert_eq!(default_config.listen_port, 8080);
assert_eq!(default_config.default_backend_port, 3000);
assert_eq!(default_config.backend_host, "127.0.0.1");
assert_eq!(default_config.port_param, "port");
assert!(default_config.config_file.is_none());
assert!(!default_config.verbose);
// 测试便捷方法
let config = ProxyConfig::new();
assert_eq!(config.listen_port, 8080);
let config = ProxyConfig::with_listen_port(9090);
assert_eq!(config.listen_port, 9090);
assert_eq!(config.default_backend_port, 3000); // 其他应该保持默认值
let config = ProxyConfig::with_listen_port(8080)
.with_backend_host("example.com")
.with_port_param("service_port");
assert_eq!(config.listen_port, 8080);
assert_eq!(config.backend_host, "example.com");
assert_eq!(config.port_param, "service_port");
}
// 测试模块集成
#[test]
fn test_module_integration() {
// 验证各模块之间的集成是否正常
let config = ProxyConfig::default();
let service = PortProxyService::new(config.clone());
let server = ProxyServer::new(config);
// 验证配置一致性
assert_eq!(service.config().default_backend_port, server.default_backend_port());
assert_eq!(service.config().port_param, server.config().port_param);
// 验证服务访问
let server_service = server.service();
let service_backends = service.backends();
// 两个服务实例应该是独立的
assert_ne!(service.config() as *const _, server_service.config() as *const _);
}