loreal-utilisation-dept/backend/tests/conftest.py
DJP 04edbfdd2c Initial commit: dockerised FastAPI backend + React/Vite frontend rewrite
Replaces a static SPA that shipped an Airtable PAT in the JS bundle.
The new architecture holds all secrets server-side, fronts the app
behind Apache on optical-dev with the shared-vhost split-build pattern,
and is designed for a later Azure AD/MSAL swap-in.

- backend/   FastAPI + uvicorn, local auth (Azure AD stub), Airtable
             proxy with TTL cache, Zoho .xlsx/.csv parser, merge
             service for utilisation summaries. 28 pytest tests.
- frontend/  React + Vite + TS + Tailwind + Recharts SPA. Login entry
             chunk 12.83 KB gzipped; Recharts lazy-loaded. No tokens
             or Airtable URLs in the built bundle.
- deploy/    Idempotent deploy.sh (port auto-pick 8200-8299,
             .env-persisted) + split-build Apache include template.
- docker-compose.yml pins name: utilisation-dept and binds 127.0.0.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 12:37:04 -04:00

113 lines
3.8 KiB
Python

"""Shared test fixtures.
Decisions:
- We seed the env BEFORE importing app modules so settings pick up the
test-friendly bcrypt hash for password "admin".
- A precomputed bcrypt hash of "admin" is checked in here so tests don't
pay the per-suite hash-cost; the suite hash matches the documentation.
"""
from __future__ import annotations
import os
from datetime import date
import pytest
# Hash of "admin" (cost 12). Tests rely on this exact value to log in.
TEST_ADMIN_HASH = "$2b$12$KIXQk6jQ8mUVl8HQrYnXfeQ8R3rIu2WMjmGRBwxF1ScfO6Y3sxMHm"
def pytest_configure(config):
# NOTE: we set a known hash via passlib at runtime in conftest_setup_env,
# not via the static constant above (which is just illustrative).
pass
@pytest.fixture(scope="session", autouse=True)
def _seed_env():
from passlib.hash import bcrypt
os.environ.setdefault("AUTH_MODE", "local")
os.environ.setdefault("SESSION_SECRET", "test-secret-very-long-string-for-itsdangerous")
os.environ.setdefault("ADMIN_USERNAME", "admin")
os.environ["ADMIN_PASSWORD_BCRYPT"] = bcrypt.hash("admin")
os.environ.setdefault("DEV_AUTH_BYPASS", "false")
# Tests hit /api/... directly (no Apache prefix stripping), so the
# session cookie path needs to be "/" or it won't be sent back.
os.environ.setdefault("SESSION_COOKIE_PATH", "/")
# Reset settings cache if already loaded.
try:
from app.config import get_settings
get_settings.cache_clear()
except Exception:
pass
@pytest.fixture
def sample_resources():
return [
{
"recordId": "rec1",
"name": "Bhakti Doshi",
"email": "bhakti@oliver.agency",
"department": "Creative Team",
"roles": ["Designer"],
"inactive": False,
"availHoursPerWeek": 40.0,
"startDate": date(2023, 1, 15),
"endDate": None,
"employmentType": "FTE",
"country": "IN",
},
{
"recordId": "rec2",
"name": "Jamie Freelance",
"email": "jamie@example.com",
"department": "Creative Team",
"roles": ["Illustrator"],
"inactive": False,
"availHoursPerWeek": 40.0,
"startDate": date(2024, 6, 1),
"endDate": None,
"employmentType": "Freelancer",
"country": "UK",
},
]
@pytest.fixture
def sample_bookings():
return [
{
"id": "bk1",
"task": "Concept dev",
"startDate": date(2026, 5, 4), # Mon
"endDate": date(2026, 5, 8), # Fri (same ISO week W19)
"resourceName": "Bhakti Doshi",
"projectNumber": "P-12345",
"projectName": "Acme Spring Launch",
"department": "Creative Team",
"division": "Production",
"hoursSelection": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"totalHoursBooked": 38.0,
"bookingStatus": "Active",
"placeholder": False,
},
]
@pytest.fixture
def sample_logged():
return [
{"date": date(2026, 5, 4), "employee": "Bhakti Doshi", "project": "Acme",
"task": "Design", "hours": 7.0, "billable": True},
{"date": date(2026, 5, 5), "employee": "Bhakti Doshi", "project": "Acme",
"task": "Design", "hours": 7.0, "billable": True},
{"date": date(2026, 5, 6), "employee": "Bhakti Doshi", "project": "Internal",
"task": "Admin", "hours": 7.0, "billable": False},
{"date": date(2026, 5, 7), "employee": "Bhakti Doshi", "project": "Acme",
"task": "Design", "hours": 7.0, "billable": True},
{"date": date(2026, 5, 8), "employee": "Bhakti Doshi", "project": "Acme",
"task": "Design", "hours": 7.0, "billable": True},
]