//! 集成测试 (Phase 1 + Phase 2) use axum::{ body::Body, http::{Request, StatusCode}, }; use serde_json::json; use tower::ServiceExt; const MAX_BODY_SIZE: usize = 1024 * 1024; // 1MB async fn build_test_app() -> axum::Router { use zclaw_saas::{config::SaaSConfig, db::init_memory_db, state::AppState}; let db = init_memory_db().await.unwrap(); let mut config = SaaSConfig::default(); config.auth.jwt_expiration_hours = 24; let state = AppState::new(db, config); let public_routes = zclaw_saas::auth::routes(); let protected_routes = zclaw_saas::auth::protected_routes() .merge(zclaw_saas::account::routes()) .merge(zclaw_saas::model_config::routes()) .layer(axum::middleware::from_fn_with_state( state.clone(), zclaw_saas::auth::auth_middleware, )); axum::Router::new() .merge(public_routes) .merge(protected_routes) .with_state(state) } /// 注册并登录,返回 JWT token async fn register_and_login(app: &axum::Router, username: &str, email: &str) -> String { let register_req = Request::builder() .method("POST") .uri("/api/v1/auth/register") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&json!({ "username": username, "email": email, "password": "password123" })).unwrap())) .unwrap(); app.clone().oneshot(register_req).await.unwrap(); let login_req = Request::builder() .method("POST") .uri("/api/v1/auth/login") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&json!({ "username": username, "password": "password123" })).unwrap())) .unwrap(); let resp = app.clone().oneshot(login_req).await.unwrap(); let body_bytes = axum::body::to_bytes(resp.into_body(), MAX_BODY_SIZE).await.unwrap(); let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); body["token"].as_str().unwrap().to_string() } fn auth_header(token: &str) -> String { format!("Bearer {}", token) } #[tokio::test] async fn test_register_and_login() { let app = build_test_app().await; let token = register_and_login(&app, "testuser", "test@example.com").await; assert!(!token.is_empty()); } #[tokio::test] async fn test_register_duplicate_fails() { let app = build_test_app().await; let body = json!({ "username": "dupuser", "email": "dup@example.com", "password": "password123" }); let req = Request::builder() .method("POST") .uri("/api/v1/auth/register") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&body).unwrap())) .unwrap(); let resp = app.clone().oneshot(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::CREATED); let req = Request::builder() .method("POST") .uri("/api/v1/auth/register") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&body).unwrap())) .unwrap(); let resp = app.oneshot(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::CONFLICT); } #[tokio::test] async fn test_unauthorized_access() { let app = build_test_app().await; let req = Request::builder() .method("GET") .uri("/api/v1/accounts") .body(Body::empty()) .unwrap(); let resp = app.oneshot(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); } #[tokio::test] async fn test_login_wrong_password() { let app = build_test_app().await; register_and_login(&app, "wrongpwd", "wrongpwd@example.com").await; let req = Request::builder() .method("POST") .uri("/api/v1/auth/login") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&json!({ "username": "wrongpwd", "password": "wrong_password" })).unwrap())) .unwrap(); let resp = app.oneshot(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::UNAUTHORIZED); } #[tokio::test] async fn test_full_authenticated_flow() { let app = build_test_app().await; let token = register_and_login(&app, "fulltest", "full@example.com").await; // 创建 API Token let create_token_req = Request::builder() .method("POST") .uri("/api/v1/tokens") .header("Content-Type", "application/json") .header("Authorization", auth_header(&token)) .body(Body::from(serde_json::to_string(&json!({ "name": "test-token", "permissions": ["model:read", "relay:use"] })).unwrap())) .unwrap(); let resp = app.clone().oneshot(create_token_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); let body_bytes = axum::body::to_bytes(resp.into_body(), MAX_BODY_SIZE).await.unwrap(); let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); assert!(!body["token"].is_null()); // 列出 Tokens let list_req = Request::builder() .method("GET") .uri("/api/v1/tokens") .header("Authorization", auth_header(&token)) .body(Body::empty()) .unwrap(); let resp = app.clone().oneshot(list_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); // 查看操作日志 let logs_req = Request::builder() .method("GET") .uri("/api/v1/logs/operations") .header("Authorization", auth_header(&token)) .body(Body::empty()) .unwrap(); let resp = app.oneshot(logs_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); } // ============ Phase 2: 模型配置测试 ============ #[tokio::test] async fn test_providers_crud() { let app = build_test_app().await; // 注册 super_admin 角色用户 (通过直接插入角色权限) let token = register_and_login(&app, "adminprov", "adminprov@example.com").await; // 创建 provider (普通用户无权限 → 403) let create_req = Request::builder() .method("POST") .uri("/api/v1/providers") .header("Content-Type", "application/json") .header("Authorization", auth_header(&token)) .body(Body::from(serde_json::to_string(&json!({ "name": "test-provider", "display_name": "Test Provider", "base_url": "https://api.example.com/v1" })).unwrap())) .unwrap(); let resp = app.clone().oneshot(create_req).await.unwrap(); // user 角色默认无 provider:manage 权限 → 403 assert_eq!(resp.status(), StatusCode::FORBIDDEN); // 列出 providers (只读权限 → 200) let list_req = Request::builder() .method("GET") .uri("/api/v1/providers") .header("Authorization", auth_header(&token)) .body(Body::empty()) .unwrap(); let resp = app.clone().oneshot(list_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); } #[tokio::test] async fn test_models_list_and_usage() { let app = build_test_app().await; let token = register_and_login(&app, "modeluser", "modeluser@example.com").await; // 列出模型 (空列表) let list_req = Request::builder() .method("GET") .uri("/api/v1/models") .header("Authorization", auth_header(&token)) .body(Body::empty()) .unwrap(); let resp = app.clone().oneshot(list_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); let body_bytes = axum::body::to_bytes(resp.into_body(), MAX_BODY_SIZE).await.unwrap(); let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); assert!(body.is_array()); assert_eq!(body.as_array().unwrap().len(), 0); // 查看用量统计 let usage_req = Request::builder() .method("GET") .uri("/api/v1/usage") .header("Authorization", auth_header(&token)) .body(Body::empty()) .unwrap(); let resp = app.clone().oneshot(usage_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); let body_bytes = axum::body::to_bytes(resp.into_body(), MAX_BODY_SIZE).await.unwrap(); let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap(); assert_eq!(body["total_requests"], 0); } #[tokio::test] async fn test_api_keys_lifecycle() { let app = build_test_app().await; let token = register_and_login(&app, "keyuser", "keyuser@example.com").await; // 列出 keys (空) let list_req = Request::builder() .method("GET") .uri("/api/v1/keys") .header("Authorization", auth_header(&token)) .body(Body::empty()) .unwrap(); let resp = app.clone().oneshot(list_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); // 创建 key (需要已有 provider → 404 或由 service 层验证) let create_req = Request::builder() .method("POST") .uri("/api/v1/keys") .header("Content-Type", "application/json") .header("Authorization", auth_header(&token)) .body(Body::from(serde_json::to_string(&json!({ "provider_id": "nonexistent", "key_value": "sk-test-12345", "key_label": "Test Key" })).unwrap())) .unwrap(); let resp = app.clone().oneshot(create_req).await.unwrap(); // provider 不存在 → 404 assert_eq!(resp.status(), StatusCode::NOT_FOUND); }