- Demo accounts: super_admin-only provisioning into isolated DEMO_ORG_ID tenant, 30-day UTC trial on first login, revocable, one-time credential delivery via optional SES/webhook (never persisted). Adds boto3 dependency. - Analytics/report/export/privacy: shared bounded scan budget across users/groups/ sessions, tenant-consistent session/user/group joins, scalar-only CSV export (no nested persisted-value stringification). - Ownership/tenant isolation: canonical owner-tenant predicate for list/read/chat; client sees is_owned only, never owner_user_id. - Lifecycle/races: status transition validation, analyzing is an in-progress gate (no duplicate reanalysis), structured-ready publication, stale-variant revalidation. - Auth/setup/consent/JWT/OAuth/config: fail-closed consent, bounded JWT lifetime, provider-subject atomic OAuth identity, repeated-secret rejection, strict Persona trait validation. - Chat/session/privacy: pre-seller opener redaction, corrupt-session recovery, role-aware completed-chat dashboard routing. - Frontend: Training→product→personas→practice flow, demo/role/demo guards, is_owned-based ownership display, 320×568 and 500×768 responsive E2E. - 8 independent exact-five-key review scopes passed; backend 509, frontend 26, production build 1775 modules, isolated E2E 15.
70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
"""Shared isolated fixtures for backend tests."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture()
|
|
def app(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
|
|
"""Create a Flask app against a fresh temporary data directory."""
|
|
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
|
monkeypatch.setenv("JWT_SECRET", "pytest-only-secret-0123456789abcdef")
|
|
monkeypatch.setenv("APP_ENV", "test")
|
|
monkeypatch.setenv("BOOTSTRAP_ADMIN_PASSWORD", "pytest-bootstrap-password")
|
|
monkeypatch.setenv("FLASK_DEBUG", "false")
|
|
|
|
from app.config import Config
|
|
|
|
Config.DATA_DIR = tmp_path
|
|
Config.SECRET_KEY = "pytest-only-secret-0123456789abcdef"
|
|
Config.APP_ENV = "test"
|
|
Config.BOOTSTRAP_ADMIN_PASSWORD = "pytest-bootstrap-password"
|
|
Config.FLASK_DEBUG = False
|
|
|
|
from app.factory import create_app
|
|
from app.services import rate_limit
|
|
|
|
rate_limit._mem.clear()
|
|
application = create_app()
|
|
application.config.update(TESTING=True)
|
|
return application
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(app):
|
|
return app.test_client()
|
|
|
|
|
|
@pytest.fixture()
|
|
def user_store(app):
|
|
store = app.extensions["user_store"]
|
|
original_create_user = store.create_user
|
|
|
|
def create_test_user(*args, **kwargs):
|
|
# Existing route tests create already-authorized identities directly;
|
|
# explicit accepted_terms=False remains available for consent tests.
|
|
kwargs.setdefault("accepted_terms", True)
|
|
return original_create_user(*args, **kwargs)
|
|
|
|
store.create_user = create_test_user
|
|
return store
|
|
|
|
|
|
def auth_headers(token: str) -> dict[str, str]:
|
|
"""Build an Authorization header without exposing the token in assertions."""
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
@pytest.fixture()
|
|
def login(client):
|
|
def _login(username: str, password: str) -> dict:
|
|
response = client.post(
|
|
"/api/auth/login",
|
|
json={"username": username, "password": password},
|
|
)
|
|
assert response.status_code == 200, response.get_json()
|
|
return response.get_json()
|
|
|
|
return _login |