//! Phase 1 集成测试 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()) .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) } #[tokio::test] async fn test_register_and_login() { let app = build_test_app().await; // 注册 let req = Request::builder() .method("POST") .uri("/api/v1/auth/register") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&json!({ "username": "testuser", "email": "test@example.com", "password": "password123" })).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/login") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&json!({ "username": "testuser", "password": "password123" })).unwrap())) .unwrap(); let resp = app.clone().oneshot(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.get("token").is_some()); assert_eq!(body["account"]["username"], "testuser"); } #[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; // 先注册 let req = Request::builder() .method("POST") .uri("/api/v1/auth/register") .header("Content-Type", "application/json") .body(Body::from(serde_json::to_string(&json!({ "username": "wrongpwd", "email": "wrongpwd@example.com", "password": "password123" })).unwrap())) .unwrap(); app.clone().oneshot(req).await.unwrap(); // 错误密码登录 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 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": "fulltest", "email": "full@example.com", "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": "fulltest", "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(); let token = body["token"].as_str().unwrap().to_string(); // 创建 API Token let create_token_req = Request::builder() .method("POST") .uri("/api/v1/tokens") .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", 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()); // 原始 token 仅创建时返回 // 列出 Tokens let list_req = Request::builder() .method("GET") .uri("/api/v1/tokens") .header("Authorization", format!("Bearer {}", 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", format!("Bearer {}", token)) .body(Body::empty()) .unwrap(); let resp = app.oneshot(logs_req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); }