from fastapi.testclient import TestClient from zbt.core.config import Settings from zbt.domains.identity.repository import InMemoryIdentityRepository from zbt.main import create_app def test_h5_user_can_login_with_development_otp_and_read_profile() -> None: settings = Settings( app_env="test", jwt_access_secret="a" * 32, jwt_refresh_secret="b" * 32, field_encryption_key="c" * 32, dev_fixed_otp="147258", ) app = create_app( settings=settings, identity_repository=InMemoryIdentityRepository(), ) with TestClient(app) as client: code_response = client.post( "/api/v1/h5/auth/request-code", json={"mobile": "18800000001"}, ) assert code_response.status_code == 200 assert code_response.json()["data"] == {"expires_in": 300} login_response = client.post( "/api/v1/h5/auth/login", json={"mobile": "18800000001", "code": "147258"}, ) assert login_response.status_code == 200 assert login_response.json()["data"]["user"]["mobile"] == "18800000001" tokens = login_response.json()["data"]["tokens"] profile_response = client.get( "/api/v1/h5/me", headers={"Authorization": f"Bearer {tokens['access_token']}"}, ) assert profile_response.status_code == 200 assert profile_response.json()["data"]["mobile_masked"] == "188****0001" def test_refresh_token_is_rotated_and_cannot_be_reused() -> None: settings = Settings( app_env="test", jwt_access_secret="a" * 32, jwt_refresh_secret="b" * 32, field_encryption_key="c" * 32, dev_fixed_otp="147258", ) app = create_app( settings=settings, identity_repository=InMemoryIdentityRepository(), ) with TestClient(app) as client: login = client.post( "/api/v1/h5/auth/login", json={"mobile": "18800000001", "code": "147258"}, ).json()["data"]["tokens"] refreshed = client.post( "/api/v1/h5/auth/refresh", json={"refresh_token": login["refresh_token"]}, ) assert refreshed.status_code == 200 next_tokens = refreshed.json()["data"]["tokens"] assert next_tokens["refresh_token"] != login["refresh_token"] reused = client.post( "/api/v1/h5/auth/refresh", json={"refresh_token": login["refresh_token"]}, ) assert reused.status_code == 401 assert reused.json()["error"]["code"] == "AUTH_REQUIRED" def test_logout_revokes_current_h5_session() -> None: settings = Settings( app_env="test", jwt_access_secret="a" * 32, jwt_refresh_secret="b" * 32, field_encryption_key="c" * 32, dev_fixed_otp="147258", ) app = create_app( settings=settings, identity_repository=InMemoryIdentityRepository(), ) with TestClient(app) as client: tokens = client.post( "/api/v1/h5/auth/login", json={"mobile": "18800000001", "code": "147258"}, ).json()["data"]["tokens"] headers = {"Authorization": f"Bearer {tokens['access_token']}"} logout = client.post("/api/v1/h5/auth/logout", headers=headers) assert logout.status_code == 200 profile = client.get("/api/v1/h5/me", headers=headers) assert profile.status_code == 401 assert profile.json()["error"]["code"] == "AUTH_REQUIRED" def test_h5_refresh_cookie_restores_session_without_request_body() -> None: settings = Settings( app_env="test", jwt_access_secret="a" * 32, jwt_refresh_secret="b" * 32, field_encryption_key="c" * 32, dev_fixed_otp="147258", ) app = create_app( settings=settings, identity_repository=InMemoryIdentityRepository(), ) with TestClient(app) as client: login = client.post( "/api/v1/h5/auth/login", json={"mobile": "18800000001", "code": "147258"}, ) assert "zbt_h5_refresh=" in login.headers["set-cookie"] assert "HttpOnly" in login.headers["set-cookie"] refreshed = client.post("/api/v1/h5/auth/refresh") assert refreshed.status_code == 200 access_token = refreshed.json()["data"]["tokens"]["access_token"] logout = client.post( "/api/v1/h5/auth/logout", headers={"Authorization": f"Bearer {access_token}"}, ) assert logout.status_code == 200 assert "zbt_h5_refresh=" in logout.headers["set-cookie"] assert "Max-Age=0" in logout.headers["set-cookie"]