앱이 기대하는 base-backend 계약(envelope·JWT·/chat/stream SSE)을 그대로 구현. Django 는 로그인·세션 미러(SQLite/PG)·OpenCode 이벤트 번역만 맡고, 답변은 전용 OpenCode 인스턴스(opencode/ workspace, codeassist 에이전트)가 만듦. - accounts: 이메일 로그인, access 60분 / refresh 14일, entra/config 는 501 - chat: 세션 목록·검색·메시지·취소 + POST /chat/stream 어댑터(part.delta→token, idle→usage/done, 클라 끊겨도 턴 감시 태스크가 DB 마무리, 첫 이벤트 60초 타임아웃) - OpenCode 1.18 멀티 프로젝트라 모든 요청에 ?directory= 부착 - docs-lib 에 OpenCode SDK 1.18.6 타입 원본, pytest 32 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
88 lines
2.9 KiB
Python
88 lines
2.9 KiB
Python
"""인증 API — 프론트 auth.api.ts / types/api.ts 계약."""
|
|
|
|
TOKEN_KEYS = {
|
|
"token",
|
|
"tokenExpirationTime",
|
|
"refreshToken",
|
|
"refreshTokenExpirationTime",
|
|
"tokenType",
|
|
"user",
|
|
}
|
|
USER_KEYS = {"id", "email", "userName", "role", "employeeId", "department", "authProvider"}
|
|
|
|
|
|
def test_login_returns_token_response(api, user):
|
|
res = api.post("/api/v1/auth/login", {"email": "U@X.com", "password": "pw1234"}, format="json")
|
|
assert res.status_code == 200
|
|
data = res.json()["data"]
|
|
assert set(data) == TOKEN_KEYS
|
|
assert data["tokenType"] == "bearer"
|
|
assert set(data["user"]) == USER_KEYS
|
|
assert data["user"]["email"] == "u@x.com"
|
|
assert data["user"]["role"] == "USER"
|
|
assert data["user"]["authProvider"] == "local"
|
|
assert data["tokenExpirationTime"] < data["refreshTokenExpirationTime"]
|
|
|
|
|
|
def test_login_wrong_password_401_with_code(api, user):
|
|
res = api.post("/api/v1/auth/login", {"email": "u@x.com", "password": "nope"}, format="json")
|
|
assert res.status_code == 401
|
|
assert res.json()["code"] == "INVALID_CREDENTIALS"
|
|
|
|
|
|
def test_me_with_bearer(auth_api):
|
|
res = auth_api.get("/api/v1/users/me")
|
|
assert res.status_code == 200
|
|
data = res.json()["data"]
|
|
assert data["email"] == "u@x.com"
|
|
assert data["isActive"] is True
|
|
assert "createdAt" in data
|
|
|
|
|
|
def test_me_with_query_token(api, auth_api):
|
|
# EventSource 등 헤더 못 붙이는 곳용
|
|
plain = api.__class__()
|
|
res = plain.get(f"/api/v1/users/me?token={auth_api.tokens['token']}")
|
|
assert res.status_code == 200
|
|
|
|
|
|
def test_refresh_issues_new_access(auth_api):
|
|
old = auth_api.tokens
|
|
res = auth_api.post("/api/v1/auth/refresh", {"refreshToken": old["refreshToken"]}, format="json")
|
|
assert res.status_code == 200
|
|
data = res.json()["data"]
|
|
assert set(data) == TOKEN_KEYS
|
|
assert data["refreshToken"] == old["refreshToken"] # 회전 안 함
|
|
assert data["user"]["email"] == "u@x.com"
|
|
|
|
|
|
def test_refresh_missing_token(api, db):
|
|
res = api.post("/api/v1/auth/refresh", {}, format="json")
|
|
assert res.status_code == 401
|
|
assert res.json()["code"] == "REFRESH_TOKEN_MISSING"
|
|
|
|
|
|
def test_refresh_bad_token(api, db):
|
|
res = api.post("/api/v1/auth/refresh", {"refreshToken": "garbage"}, format="json")
|
|
assert res.status_code == 401
|
|
assert res.json()["code"] == "REFRESH_TOKEN_INVALID"
|
|
|
|
|
|
def test_logout_is_public_and_null(api, db):
|
|
res = api.post("/api/v1/auth/logout", format="json")
|
|
assert res.status_code == 200
|
|
assert res.json()["data"] is None
|
|
|
|
|
|
def test_entra_config_is_501(api, db):
|
|
res = api.get("/api/v1/auth/entra/config")
|
|
assert res.status_code == 501
|
|
assert res.json()["code"] == "NOT_IMPLEMENTED"
|
|
|
|
|
|
def test_seed_users_exist_after_migrate(db):
|
|
from apps.accounts.models import User
|
|
|
|
assert User.objects.filter(email="admin@codeassist.local", is_superuser=True).exists()
|
|
assert User.objects.filter(email="guest@codeassist.local").exists()
|