feat(demo): add auth flow UI service
This commit is contained in:
20
tests/test_debug_sms_client.py
Normal file
20
tests/test_debug_sms_client.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from gapido_auth.infrastructure.debug_sms_client import DebugSmsClient, debug_sms_key
|
||||
|
||||
|
||||
class FakeDebugStore:
|
||||
def __init__(self) -> None:
|
||||
self.values: dict[str, tuple[int, str]] = {}
|
||||
|
||||
async def setex(self, name: str, time: int, value: str) -> object:
|
||||
self.values[name] = (time, value)
|
||||
return True
|
||||
|
||||
|
||||
async def test_debug_sms_client_stores_latest_otp() -> None:
|
||||
store = FakeDebugStore()
|
||||
client = DebugSmsClient(store, ttl_seconds=300)
|
||||
|
||||
await client.send_otp("989120000000", "123456", "debug-otp")
|
||||
|
||||
assert store.values[debug_sms_key("989120000000")] == (300, "123456")
|
||||
|
||||
132
tests/test_demo_app.py
Normal file
132
tests/test_demo_app.py
Normal file
@@ -0,0 +1,132 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from gapido_auth.config import Settings
|
||||
from gapido_auth.demo.app import app, get_app_settings, get_auth_client, get_debug_redis
|
||||
from gapido_auth.demo.grpc_client import DemoTokenResponse
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class FakeAuthClient:
|
||||
last_user_token: str | None = None
|
||||
last_admin_token: str | None = None
|
||||
|
||||
async def request_otp(self, mobile: str, purpose: str) -> dict[str, bool]:
|
||||
assert mobile == "989120000000"
|
||||
assert purpose == "login"
|
||||
return {"accepted": True}
|
||||
|
||||
async def verify_otp(self, mobile: str, code: str, purpose: str) -> DemoTokenResponse:
|
||||
assert mobile == "989120000000"
|
||||
assert code == "123456"
|
||||
assert purpose == "login"
|
||||
return DemoTokenResponse(
|
||||
"access-token-value-123", "refresh-token-value-123", "Bearer", 900, "user"
|
||||
)
|
||||
|
||||
async def refresh_token(self, refresh_token: str) -> DemoTokenResponse:
|
||||
assert refresh_token == "refresh-token-value-123"
|
||||
return DemoTokenResponse(
|
||||
"new-access-token-value", "new-refresh-token-value", "Bearer", 900, "user"
|
||||
)
|
||||
|
||||
async def revoke_refresh_token(self, access_token: str, refresh_token: str) -> dict[str, bool]:
|
||||
assert access_token == "access-token-value-123"
|
||||
assert refresh_token == "refresh-token-value-123"
|
||||
return {"revoked": True}
|
||||
|
||||
async def public_ping(self) -> dict[str, str]:
|
||||
return {"message": "public ok"}
|
||||
|
||||
async def user_only(self, access_token: str) -> dict[str, str]:
|
||||
self.last_user_token = access_token
|
||||
return {"user_id": "1", "role": "user", "message": "authenticated user ok"}
|
||||
|
||||
async def admin_only(self, access_token: str) -> dict[str, str]:
|
||||
self.last_admin_token = access_token
|
||||
return {"user_id": "2", "role": "admin", "message": "admin ok"}
|
||||
|
||||
|
||||
class FakeDebugRedis:
|
||||
async def get(self, key: str) -> str | None:
|
||||
assert key == "debug:sms:last:989120000000"
|
||||
return "123456"
|
||||
|
||||
|
||||
def test_demo_auth_flow_forwards_to_grpc_client() -> None:
|
||||
fake_client = FakeAuthClient()
|
||||
app.dependency_overrides[get_auth_client] = lambda: fake_client
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
request = client.post(
|
||||
"/api/auth/request-otp", json={"mobile": "989120000000", "purpose": "login"}
|
||||
)
|
||||
assert request.status_code == 200
|
||||
assert request.json() == {"accepted": True}
|
||||
|
||||
verify = client.post(
|
||||
"/api/auth/verify-otp",
|
||||
json={"mobile": "989120000000", "code": "123456", "purpose": "login"},
|
||||
)
|
||||
assert verify.status_code == 200
|
||||
assert verify.json()["access_token"] == "access-token-value-123"
|
||||
|
||||
public = client.post("/api/demo/public")
|
||||
assert public.status_code == 200
|
||||
assert public.json() == {"message": "public ok"}
|
||||
|
||||
user = client.post("/api/demo/user", json={"access_token": "access-token-value-123"})
|
||||
assert user.status_code == 200
|
||||
assert fake_client.last_user_token == "access-token-value-123"
|
||||
|
||||
admin = client.post("/api/demo/admin", json={"access_token": "access-token-value-123"})
|
||||
assert admin.status_code == 200
|
||||
assert fake_client.last_admin_token == "access-token-value-123"
|
||||
|
||||
refresh = client.post(
|
||||
"/api/auth/refresh", json={"refresh_token": "refresh-token-value-123"}
|
||||
)
|
||||
assert refresh.status_code == 200
|
||||
assert refresh.json()["refresh_token"] == "new-refresh-token-value"
|
||||
|
||||
revoke = client.post(
|
||||
"/api/auth/revoke",
|
||||
json={
|
||||
"access_token": "access-token-value-123",
|
||||
"refresh_token": "refresh-token-value-123",
|
||||
},
|
||||
)
|
||||
assert revoke.status_code == 200
|
||||
assert revoke.json() == {"revoked": True}
|
||||
finally:
|
||||
client.close()
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_debug_otp_endpoint_reads_redis_when_enabled() -> None:
|
||||
app.dependency_overrides[get_app_settings] = lambda: Settings(demo_enable_debug_otp=True)
|
||||
app.dependency_overrides[get_debug_redis] = lambda: FakeDebugRedis()
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.get("/api/debug/otp?mobile=989120000000")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"mobile": "989120000000", "code": "123456"}
|
||||
finally:
|
||||
client.close()
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_debug_otp_endpoint_is_disabled() -> None:
|
||||
app.dependency_overrides[get_app_settings] = lambda: Settings(demo_enable_debug_otp=False)
|
||||
app.dependency_overrides[get_debug_redis] = lambda: None
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.get("/api/debug/otp?mobile=989120000000")
|
||||
assert response.status_code == 404
|
||||
finally:
|
||||
client.close()
|
||||
app.dependency_overrides.clear()
|
||||
Reference in New Issue
Block a user