109 lines
4.4 KiB
Markdown
109 lines
4.4 KiB
Markdown
|
|
# 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
|