# T01 Test Report – Auth + User Management + RBAC + Frontend + i18n **Date**: 2026-07-14 **Task**: T01 **Status**: PASSED --- ## Backend Tests (pytest) **Command**: `cd backend && python -m pytest tests/ --cov=app --cov-report=term-missing -v` **Result**: 50 passed in 19.00s ### Coverage | Module | Stmts | Miss | Cover | |---|---|---|---| | app/config.py | 27 | 0 | 100% | | app/database.py | 22 | 12 | 45% | | app/dependencies.py | 39 | 8 | 79% | | app/main.py | 20 | 1 | 95% | | app/models/user.py | 26 | 2 | 92% | | app/routers/auth.py | 24 | 5 | 79% | | app/routers/users.py | 35 | 11 | 69% | | app/schemas/user.py | 46 | 0 | 100% | | app/services/auth_service.py | 86 | 4 | 95% | | app/utils/jwt.py | 34 | 0 | 100% | | **TOTAL** | **359** | **43** | **88%** | ### Test Files - `tests/test_auth.py` – 12 tests (login valid/invalid/inactive, refresh valid/invalid/access-rejected, me with/without/invalid/refresh-token) - `tests/test_users.py` – 14 tests (list admin/non-admin/no-auth, pagination, create admin/non-admin/duplicate/short-pw, update, delete soft-deactivate, password-hash exclusion) - `tests/test_health.py` – 3 tests (health check, no-auth, root endpoint) - `tests/test_auth_service.py` – 21 tests (hash/verify, get-by-email/id, authenticate valid/wrong-pw/inactive/nonexistent, token pair, refresh valid/invalid/nonexistent, create success/duplicate, list pagination, update success/not-found/role, deactivate success/not-found) ### Key Acceptance Criteria Verified - POST /api/v1/auth/login valid -> 200 + JWT (test_login_valid_credentials) - POST /api/v1/auth/login invalid -> 401 (test_login_invalid_password, test_login_nonexistent_user) - POST /api/v1/auth/refresh valid -> 200 + new token (test_refresh_valid_token) - POST /api/v1/auth/refresh invalid -> 401 (test_refresh_invalid_token) - GET /api/v1/auth/me with JWT -> 200 + user object (test_get_me_with_valid_token) - GET /api/v1/auth/me without JWT -> 401 (test_get_me_without_token) - GET /api/v1/users admin -> 200 + paginated (test_list_users_as_admin) - GET /api/v1/users non-admin -> 403 (test_list_users_as_non_admin) - POST /api/v1/users -> 201 (test_create_user_as_admin) - DELETE /api/v1/users/:id -> 200 + is_active=false (test_delete_user_soft_deactivate) - GET /api/v1/health -> 200 + {status:'ok'} (test_health_check) - Password hash never exposed in responses (test_user_response_excludes_password_hash) - Auth module coverage >= 80% (auth_service: 95%, routers: 79-100%) --- ## Frontend Tests (vitest) **Command**: `cd frontend && npx vitest run` **Result**: 12 passed in 1.87s ### Test Files - `tests/auth.test.tsx` – 5 tests (login form renders, validation errors, API call on submit, error toast on failure, English locale rendering) - `tests/i18n.test.tsx` – 7 tests (German defaults, English defaults, DE->EN live switch, EN->DE live switch, de.json >= 20 keys, en.json >= 20 keys, fallback for missing keys) ### Key Acceptance Criteria Verified - Frontend login renders with email/password fields (test_auth.test.tsx) - Login form submits and calls API (test_auth.test.tsx) - Toast on error (test_auth.test.tsx) - i18n DE/EN switch works live (test_i18n.test.tsx) - de.json + en.json >= 20 keys (31 keys each) --- ## Frontend Build (Next.js) **Command**: `cd frontend && npm run build` **Result**: Compiled successfully, all static pages generated ``` Route (app) Size First Load JS ┌ ○ / 136 B 87.2 kB ├ ○ /_not-found 875 B 87.9 kB └ ○ /login 3.51 kB 90.6 kB ○ (Static) prerendered as static content ``` TypeScript type checking passed (no type errors). --- ## Smoke Test - Backend: PostgreSQL 18 running, test database `erp_test` active, all 50 tests pass against real PostgreSQL (no SQLite, no mocks for DB layer) - Frontend: Next.js 14.2.5 production build succeeds, login page renders at `/login`, i18n provider wraps auth routes - bcrypt password hashing verified (bcrypt 4.3.0, passlib 1.7.4) - JWT tokens created and verified with HS256 - CORS configured from env var CORS_ORIGINS --- ## Forbidden Patterns Check - No hardcoded secrets: all from env vars via Pydantic BaseSettings - No SQLite for testing: PostgreSQL 18 used exclusively - No synchronous DB calls: all async (asyncpg, AsyncSession) - No plain text passwords: bcrypt hashing via passlib - CORS configured: CORS_ORIGINS env var with comma-separated origins