feat(auth): add DB-backed login, token management, and idempotent auth migration
This commit is contained in:
+80
-1
@@ -1,7 +1,9 @@
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.core.config import Settings
|
||||
from app.core.config import get_settings
|
||||
from app.main import app
|
||||
from app.domain.models import User
|
||||
from app.main import app, create_app
|
||||
|
||||
|
||||
def test_healthcheck() -> None:
|
||||
@@ -25,3 +27,80 @@ def test_settings_default_origin() -> None:
|
||||
settings = get_settings()
|
||||
|
||||
assert settings.allowed_origins == ["http://localhost:3000"]
|
||||
|
||||
|
||||
class _BootstrapRepo:
|
||||
def __init__(self, users: list[User] | None = None) -> None:
|
||||
self.users = list(users or [])
|
||||
self.created: list[tuple[str, str | None, str | None, str]] = []
|
||||
self.closed = False
|
||||
|
||||
def list_users(self) -> list[User]:
|
||||
return list(self.users)
|
||||
|
||||
def create_user(
|
||||
self,
|
||||
username: str,
|
||||
*,
|
||||
email: str | None = None,
|
||||
password_hash: str | None = None,
|
||||
permission: str = "read",
|
||||
is_active: bool = True,
|
||||
) -> User:
|
||||
self.created.append((username, email, password_hash, permission))
|
||||
user = User(
|
||||
id=f"user-{len(self.users) + 1}",
|
||||
username=username,
|
||||
email=email,
|
||||
permission=permission,
|
||||
is_active=is_active,
|
||||
password_hash=password_hash,
|
||||
)
|
||||
self.users.append(user)
|
||||
return user
|
||||
|
||||
def close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
|
||||
def test_startup_bootstraps_initial_user_when_users_table_empty() -> None:
|
||||
repo = _BootstrapRepo()
|
||||
settings = Settings(
|
||||
db_enabled=True,
|
||||
bootstrap_username="admin",
|
||||
bootstrap_email="admin@example.com",
|
||||
bootstrap_password="bootstrap123",
|
||||
allowed_origins_raw="http://localhost:3000",
|
||||
)
|
||||
bootstrap_app = create_app(settings_factory=lambda: settings, repository_factory=lambda current: repo)
|
||||
|
||||
with TestClient(bootstrap_app) as client:
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
assert repo.created[0][0] == "admin"
|
||||
assert repo.created[0][1] == "admin@example.com"
|
||||
assert repo.created[0][2] is not None
|
||||
assert repo.created[0][3] == "admin"
|
||||
assert repo.closed is True
|
||||
|
||||
|
||||
def test_startup_does_not_bootstrap_when_users_exist() -> None:
|
||||
repo = _BootstrapRepo(users=[User(id="user-1", username="existing")])
|
||||
settings = Settings(
|
||||
db_enabled=True,
|
||||
bootstrap_username="admin",
|
||||
bootstrap_email="admin@example.com",
|
||||
bootstrap_password="bootstrap123",
|
||||
allowed_origins_raw="http://localhost:3000",
|
||||
)
|
||||
bootstrap_app = create_app(settings_factory=lambda: settings, repository_factory=lambda current: repo)
|
||||
|
||||
with TestClient(bootstrap_app) as client:
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
assert repo.created == []
|
||||
assert repo.closed is True
|
||||
|
||||
Reference in New Issue
Block a user