refactor: rename MERGE_CHECKLIST to ALL_TESTS + add per-service test files
- tests/MERGE_CHECKLIST.md → tests/ALL_TESTS.md (git rename, updated header + index of sub-files) - tests/backend_tests.md — §1–9, §18 (auth, users, admin, groups, appearance, service health, plugins, AI/doc settings, infra/security) - tests/frontend_tests.md — §19 (UI & routing) - tests/doc-service_tests.md — §10–16 (upload/processing, list/filtering, slide-over, sharing, categories, bulk actions, watch directory) - tests/ai-service_tests.md — §17 (AI queue & providers) - CLAUDE.md: updated merge checklist section, file tree, and self-update checkpoint with mandatory test-file update rule - settings.local.json: added docker inspect/ps, curl, lsof, git merge/branch/log/diff/status/config/mv permissions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# Backend Tests
|
||||
|
||||
Tests covering the FastAPI gateway (`backend:8000`): auth, user/profile management, admin (users/groups/appearance), service health, plugin system, AI settings, document settings, and infrastructure/security.
|
||||
|
||||
Full combined suite: `tests/ALL_TESTS.md`
|
||||
|
||||
**Test environment:** Feature stack at `http://localhost:$PORT` (see CLAUDE.md §Feature branch workflow).
|
||||
**Admin credentials:** any superuser account created during stack setup.
|
||||
**Regular user credentials:** a second non-admin account for permission boundary tests.
|
||||
|
||||
---
|
||||
|
||||
## Legend
|
||||
|
||||
| Symbol | Meaning |
|
||||
|--------|---------|
|
||||
| ✅ | Pass |
|
||||
| ❌ | Fail |
|
||||
| — | N/A for this change |
|
||||
|
||||
---
|
||||
|
||||
## 1. Authentication
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 1.1 | Register new account | `POST /api/auth/register` with valid email + strong password | 201; user row created; login works immediately |
|
||||
| 1.2 | Password policy — too short | Register with 7-char password | 422 with validation error |
|
||||
| 1.3 | Password policy — no uppercase | Register with all-lowercase password | 422 with validation error |
|
||||
| 1.4 | Password policy — no special char | Register without special character | 422 with validation error |
|
||||
| 1.5 | Password policy — common word | Register with password containing "password" | 422 with validation error |
|
||||
| 1.6 | Duplicate email | Register with an already-used email | 400 |
|
||||
| 1.7 | Login — valid credentials | `POST /api/auth/login` with correct email + password | 200; `access_token` returned |
|
||||
| 1.8 | Login — wrong password | `POST /api/auth/login` with wrong password | 401 |
|
||||
| 1.9 | Login — inactive account | Admin deactivates user; attempt login | 401 |
|
||||
| 1.10 | JWT expiry respected | Manually craft token with `exp` in the past; call any protected route | 401 |
|
||||
| 1.11 | Logout clears session | Click Logout in UI; try navigating to `/` | Redirected to `/login` |
|
||||
| 1.12 | Unauthenticated redirect | Open `/` without a token in `localStorage` | Redirected to `/login` |
|
||||
|
||||
---
|
||||
|
||||
## 2. User — Profile & Preferences
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 2.1 | Fetch own profile | `GET /api/profile/me` | 200; profile auto-created if first request |
|
||||
| 2.2 | Update profile fields | `PUT /api/profile/me` with full_name, phone, position, address, date_of_birth | 200; fields persisted; visible on `/profile` page |
|
||||
| 2.3 | Invalid phone format | `PUT /api/profile/me` with letters in phone field | 422 |
|
||||
| 2.4 | Future date of birth | `PUT /api/profile/me` with DOB = tomorrow | 422 |
|
||||
| 2.5 | DOB before 1900 | `PUT /api/profile/me` with DOB = 1899-12-31 | 422 |
|
||||
| 2.6 | Fetch dashboard preferences | `GET /api/users/me/preferences` | 200; `app_ids` list |
|
||||
| 2.7 | Pin an app | Dashboard → pencil button → press `+` on a card → save | Card appears in pinned grid on next load |
|
||||
| 2.8 | Unpin an app | Dashboard → pencil button → press `−` on a pinned card → save | Card removed from pinned grid |
|
||||
| 2.9 | Pin limit (50) | `PATCH /api/users/me/preferences` with 51 app IDs | 422 |
|
||||
| 2.10 | Color mode — user pref | User sets mode to "dark"; reload page | Dark theme applied; preference persists across sessions |
|
||||
| 2.11 | Color mode — system fallback | User has NULL color_mode; admin default_mode = "light" | Light theme applied |
|
||||
|
||||
---
|
||||
|
||||
## 3. Admin — Users
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 3.1 | List all users | Admin → `/admin/users` | All registered users shown in table |
|
||||
| 3.2 | Create user | Admin clicks "Create user"; fills form | 201; new user appears in list; can log in |
|
||||
| 3.3 | Toggle user active | Admin clicks toggle on active user | User deactivated; login returns 401 |
|
||||
| 3.4 | Delete user | Admin deletes a user | 204; user no longer in list; their documents remain (orphaned) |
|
||||
| 3.5 | Non-admin access | Regular user navigates to `/admin/users` | Redirected to `/login` |
|
||||
| 3.6 | Admin 404 semantics | Regular user calls `GET /api/admin/users` via curl | 404 (not 403) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Admin — Groups
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 4.1 | List groups | Admin → `/admin/groups` | All groups shown with member count |
|
||||
| 4.2 | Create group | Fill name + description → submit | Group appears in list; `{group_name}-admin` bootstrap group also exists (auto-created on service start) |
|
||||
| 4.3 | Edit group | Click edit on group → change name → save | Name updated |
|
||||
| 4.4 | Delete group | Delete group | 204; group gone; memberships cascade-deleted |
|
||||
| 4.5 | Add member | Open group → search user → add | 204; user appears in member list |
|
||||
| 4.6 | Remove member | Click remove on a member | User removed from group |
|
||||
| 4.7 | Duplicate group name | Create group with name that already exists | 400 / validation error shown |
|
||||
| 4.8 | Non-admin access | Regular user calls `GET /api/admin/groups` | 404 |
|
||||
| 4.9 | Set group admin role | Admin → group detail → tick "Group admin" checkbox on a member → save | `PATCH /api/admin/groups/{id}/members/{user_id}/admin` with `{"is_group_admin": true}` returns 200; badge shown in member list |
|
||||
| 4.10 | Unset group admin role | Admin unticks "Group admin" on an existing group admin member | Returns 200; badge removed; user loses group-admin privileges |
|
||||
|
||||
---
|
||||
|
||||
## 5. Admin — Appearance
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 5.1 | List themes | Admin → `/admin/appearance` | Built-in themes + any custom themes shown |
|
||||
| 5.2 | Switch active theme | Select a different theme → save | All users see the new theme on next load |
|
||||
| 5.3 | Create custom theme | Admin → create theme. Required fields: `id` (slug), `label`, `light` (CSS vars object), `dark` (CSS vars object) | 201; theme appears in selector; can be activated |
|
||||
| 5.4 | Edit custom theme | Admin edits colour values on a custom theme | Colours update live after activation |
|
||||
| 5.5 | Delete custom theme | Admin deletes a custom theme | 204; theme gone from selector; active theme reverts to default |
|
||||
| 5.6 | Set default mode | `PATCH /api/settings/appearance` with `{"theme": "<id>", "default_mode": "dark"}` (both fields required) | 200; new users without a personal preference see dark mode |
|
||||
|
||||
---
|
||||
|
||||
## 6. Service Health & Dashboard
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 6.1 | Services endpoint | `GET /api/services` (authenticated) | Returns health status for doc-service and ai-service |
|
||||
| 6.2 | Healthy service card | Both services running → `/apps` page. API response uses `healthy: true` (boolean), not `status: "healthy"` | Cards show "Available" badge; clicking navigates to the app |
|
||||
| 6.3 | Unhealthy service card | Stop doc-service container → wait 30s → `/apps` | Doc-service card dimmed, "Unavailable", not clickable |
|
||||
| 6.4 | Service recovery | Restart stopped container → wait 30s | Card returns to "Available" |
|
||||
| 6.5 | Dashboard pinned cards | Pin a service → go to `/` | Pinned card appears in home grid |
|
||||
| 6.6 | Customize mode | Click pencil on dashboard → toggle cards | Pinned list updates after save |
|
||||
|
||||
---
|
||||
|
||||
## 7. Plugin System
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 7.1 | List plugins | `GET /api/plugins` (authenticated) | Returns accessible plugins for current user |
|
||||
| 7.2 | Superuser sees all plugins | Log in as admin → `GET /api/plugins` | All registered service plugins returned |
|
||||
| 7.3 | Group member sees plugin | Add user to `doc-service-admin` group → `GET /api/plugins` | doc-service plugin returned |
|
||||
| 7.4 | Unpermitted user hidden | Regular user not in any admin group → `GET /api/plugins` | Empty list (plugins hidden, not 403) |
|
||||
| 7.5 | Manifest fetch | `GET /api/plugins/doc-service/manifest` as permitted user | JSON Schema + access rules returned |
|
||||
| 7.6 | Settings read | `GET /api/plugins/doc-service/settings` | Current doc-service plugin settings returned |
|
||||
| 7.7 | Settings write | `PATCH /api/plugins/doc-service/settings` with valid payload | 200; setting persisted to volume |
|
||||
| 7.8 | Unpermitted settings access | Regular user `GET /api/plugins/doc-service/settings` | 404 |
|
||||
|
||||
---
|
||||
|
||||
## 8. AI Service Settings
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 8.1 | Read AI config | Admin (or `ai-service-admin` member) → `GET /api/settings/ai` | Config returned; API keys masked |
|
||||
| 8.2 | Update provider | `PATCH /api/settings/ai` with provider = "anthropic" + valid key | 200; config persisted |
|
||||
| 8.3 | Test connection | `POST /api/settings/ai/test` with valid config | 200; success response from provider |
|
||||
| 8.4 | Test connection — bad key | `POST /api/settings/ai/test` with wrong API key | 502 or error detail |
|
||||
| 8.5 | Read system prompts | `GET /api/settings/system-prompts` | All registered service prompts returned |
|
||||
| 8.6 | Update system prompt | `PATCH /api/settings/system-prompts/doc-service` with new prompt text | 200; doc-service picks up new prompt within 30s |
|
||||
| 8.7 | Non-admin access | Regular user calls any `/api/settings/ai` endpoint | 404 |
|
||||
| 8.8 | `ai-service-admin` delegation | Non-superuser added to `ai-service-admin` group → accesses AI settings page | Page loads; can read and write settings |
|
||||
|
||||
---
|
||||
|
||||
## 9. Document Service Settings
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 9.1 | Read upload limits | `GET /api/settings/documents/limits` (admin or `doc-service-admin`) | `max_pdf_bytes` returned |
|
||||
| 9.2 | Update upload limit | `PATCH /api/settings/documents/limits` with new value | 200; upload of oversized PDF now rejected with 413 |
|
||||
| 9.3 | Non-admin access | Regular user calls `GET /api/settings/documents/limits` | 404 |
|
||||
| 9.4 | Settings page loads | Admin navigates to `/apps/documents/settings` | Upload limits section + watch directory config visible |
|
||||
| 9.5 | `doc-service-admin` delegation | Non-superuser added to `doc-service-admin` → navigates to settings page | Page loads; settings editable |
|
||||
|
||||
---
|
||||
|
||||
## 18. Infrastructure & Security
|
||||
|
||||
| # | Test | Steps | Expected |
|
||||
|---|------|-------|----------|
|
||||
| 18.1 | Non-root containers | `docker inspect <container> --format '{{.Config.User}}'` for each service | Returns `1001` (or `70` for db) |
|
||||
| 18.2 | No host ports in prod | `docker compose up --build -d` → `docker ps` | Only port 80 (frontend) exposed; no 8000/8001/8010/5432 |
|
||||
| 18.3 | backend-net isolation | `curl http://localhost:8000` from host in prod | Connection refused |
|
||||
| 18.4 | Pre-commit hook runs | Stage a file with `eval("x")` → `git commit` | Commit blocked; security_check.py output shown |
|
||||
| 18.5 | Pre-commit hook — clean code | Normal commit | Hook passes; commit succeeds |
|
||||
| 18.6 | JWT algorithm none rejected | Craft token with `"alg": "none"` → call protected route | 401 |
|
||||
| 18.7 | XSS — input sanitation | Enter `<script>alert(1)</script>` in title/name fields | Value stored as plain text; not executed in UI |
|
||||
| 18.8 | SQL injection attempt | Pass `'; DROP TABLE documents; --` as search param | 200 with empty results; no DB error |
|
||||
| 18.9 | CORS | `curl -H "Origin: http://evil.com" http://localhost/api/users/me` | Request blocked or `access-control-allow-origin` not set for that origin |
|
||||
| 18.10 | Config volume persistence | Restart all containers | AI provider config + doc limits survive restart |
|
||||
| 18.11 | Migration auto-apply | Start fresh stack | Both `alembic upgrade head` chains run without error; all tables created |
|
||||
Reference in New Issue
Block a user