a4d0f0c35d
Check Cross-Plugin Imports / check (push) Has been cancelled
- D-GEN: RestoreRegistry mit RestoreConfig (model_class, restore_permission, excluded_fields, special_handler) - D-HOOK: history_hooks.py mit register_history_hooks() für after_create/update/delete - D-CORE: Company create+update record_history in companies.py - D-PLUG: Task/Calendar/DMS record_history in services/routes - D-SOFT: Alle registrierten Entitäten haben deleted_at + un-delete via Registry - D-MAIL: Mail special_handler (IMAP Trash-Move, Folder-Verify) + record_history in delete/move - D-TRASH: GET /entity-history/trash (filterbar, paginiert) + Frontend Trash.tsx - D-TOAST: UndoToast.tsx (5s Auto-Dismiss, useUndoToast Hook) - D-HIST-UI: HistoryPanel.tsx (Timeline, Diff-View, Restore-Button) - D-BULK: POST /entity-history/bulk-restore mit partial_success Semantik - D-RET: POST /entity-history/retention/archive (GDPR hard-delete >90 Tage) - D-TEST: 26 Tests in test_restore_registry.py, alle grün - D-DOC: test-strategy.md + security_kernel.md aktualisiert Backend: 10 Dateien, Frontend: 7 Dateien, Tests: 1 Datei, Docs: 3 Dateien 26/26 Tests passed, TSC 0 errors, App import 492 routes
298 lines
13 KiB
Markdown
298 lines
13 KiB
Markdown
# LeoCRM Test-Strategie
|
||
|
||
> **Wichtig:** Dieses Dokument muss nach jeder größeren Änderung am Codebase
|
||
> (neue Plugins, neue Module, Refactoring, Security-Änderungen) überarbeitet
|
||
> werden. Siehe `leocrm-test-strategy.promptinclude.md`.
|
||
|
||
---
|
||
|
||
## 1. Übersicht
|
||
|
||
LeoCRM verwendet eine mehrschichtige Test-Strategie um Funktionalität,
|
||
Sicherheit und Stabilität sicherzustellen.
|
||
|
||
### Test-Pyramide
|
||
|
||
```
|
||
┌──────────┐
|
||
│ E2E │ ← Browser-Tests (geplant, noch nicht implementiert)
|
||
├──────────┤
|
||
│Integration│ ← pytest mit echter PostgreSQL/Redis Test-DB
|
||
├──────────┤
|
||
│ Unit │ ← pytest mit Mocks (teilweise)
|
||
└──────────┘
|
||
```
|
||
|
||
### Aktuelle Abdeckung
|
||
|
||
| Ebene | Tool | Status | Abdeckung |
|
||
|-------|------|--------|-----------|
|
||
| Backend-Tests | pytest | ✅ aktiv | 69 Testdateien, ~500 Tests |
|
||
| Frontend-Tests | vitest | ⚠️ geplant | 0 Tests (54k Zeilen ungetestet) |
|
||
| E2E-Tests | Playwright/Cypress | ⚠️ geplant | 0 Tests |
|
||
| Security-Tests | bandit, pip-audit | ⚠️ geplant | nicht implementiert |
|
||
| CI-Pipeline | scripts/ci_pipeline.sh | ✅ aktiv | 15+ Checks |
|
||
|
||
---
|
||
|
||
## 2. Backend-Tests (pytest)
|
||
|
||
### Architektur
|
||
|
||
- **Test-DB:** PostgreSQL `leocrm_test` (localhost:5432)
|
||
- **Redis:** localhost:6379/0 (wird vor jedem Test geflushed)
|
||
- **Fixture-Strategie:** Function-scoped (jeder Test bekommt frische DB)
|
||
- **Schema-Erstellung:** `Base.metadata.create_all` (keine Alembic-Migrationen)
|
||
- **Plugin-Aktivierung:** In-Memory-Registry muss pro Fixture gesetzt werden
|
||
|
||
### Bekannte Einschränkungen
|
||
|
||
1. **RLS (Row Level Security) nicht testbar:**
|
||
- Die Test-DB verwendet `create_all` statt Alembic-Migrationen
|
||
- RLS-Policies werden normalerweise durch Migrationen erstellt
|
||
- RLS-Tests wurden ausgebaut (bringt nichts wenn es sich nicht testen lässt)
|
||
- **Lösung:** Alembic-Migrationen in Test-DB ausführen (Roadmap)
|
||
|
||
2. **LLM-API-Tests blockieren:**
|
||
- Tests die externe LLM-APIs aufrufen (Ollama Cloud, OpenRouter) blockieren
|
||
- Die komplette Suite hängt bei ~46% wenn LLM-Calls nicht gemockt sind
|
||
- **Lösung:** LLM-Calls in Tests mocken (Roadmap)
|
||
|
||
3. **Fixture-Overhead (~2,5s pro Test):**
|
||
- `seed_tenant_and_users` (1,27s) + `create_app` (0,73s) + `login` (0,34s)
|
||
- Wird pro Test ausgeführt (Function-Scope)
|
||
- Session-Scope ist nicht möglich weil ~30 Testdateien `seed_tenant_and_users` direkt aufrufen
|
||
- **Lösung:** Seed-Daten session-scopen + clean_tables anpassen (Roadmap)
|
||
|
||
4. **Keine Parallelisierung möglich:**
|
||
- `pytest-xdist` funktioniert nicht weil alle Worker dieselbe Test-DB teilen
|
||
- **Lösung:** Pro-Worker Datenbank (Roadmap)
|
||
|
||
### Test-Kategorien
|
||
|
||
| Kategorie | Beschreibung | Beispiele |
|
||
|-----------|-------------|----------|
|
||
| **Funktionale Tests** | Testet ob Features funktionieren | test_calendar, test_tags, test_dms |
|
||
| **Permission-Tests** | Testet ABAC/Permission-System | test_permissions, test_entity_permissions |
|
||
| **Plugin-Tests** | Testet Plugin-Lifecycle und -Funktionen | test_plugins, test_entity_links |
|
||
| **Cross-Tenant-Tests** | Testet Tenant-Isolation | test_cross_tenant_security |
|
||
| **AI-Tests** | Testet AI-Proactive, Copilot, GraphRAG | test_ai_proactive, test_ai_copilot |
|
||
| **Integration-Tests** | Testet Modul-übergreifend | test_unified_search, test_outbox |
|
||
|
||
### Konventionen
|
||
|
||
1. **Test-Dateien:** `tests/test_<modul>.py`
|
||
2. **Fixtures:** In `tests/conftest.py` definiert
|
||
3. **Plugin-Aktivierung:** Jede Plugin-Test-Datei muss `init_permission_registry(active_plugin_names={...})` aufrufen
|
||
4. **Entity-Typen:** Verwende korrekte ENTITY_MODELS-Keys (z.B. `file` nicht `dms_file`, `mail_account` nicht `mailbox`)
|
||
5. **URLs:** Verwende korrekte API-Pfade (z.B. `/api/v1/entity-links/` nicht `/api/v1/dms/`)
|
||
6. **Dedup-Tests:** Verwende unterschiedlichen Dateiinhalt pro Upload um Dedup-Logik nicht zu triggern
|
||
7. **Keine zufälligen UUIDs:** Verwende echte Entity-IDs aus der DB, nicht `uuid.uuid4()`
|
||
|
||
---
|
||
|
||
## 3. Frontend-Tests (geplant)
|
||
|
||
### Aktuell
|
||
- **0 Tests** für 54.000 Zeilen TSX/TypeScript
|
||
- Frontend-Bugs werden nur manuell im Browser gefunden
|
||
|
||
### Roadmap
|
||
- **Unit-Tests:** vitest für React-Komponenten
|
||
- **Integration-Tests:** Testing Library für Komponenten-Interaktionen
|
||
- **E2E-Tests:** Playwright für kritische User-Flows (Login, Kontakt erstellen, Kalender)
|
||
|
||
---
|
||
|
||
## 4. Security-Testing (geplant)
|
||
|
||
### Aktuell
|
||
- Keine automatisierten Security-Tests
|
||
- Security-Bugs wurden durch manuelle Code-Review gefunden (siehe Bugfix-Session 2026-08-12)
|
||
|
||
### Bekannte Security-Lücken (behoben am 2026-08-12)
|
||
|
||
| Bug | Fix | Status |
|
||
|-----|-----|--------|
|
||
| `MAIL_ENCRYPTION_KEY` hatte Default-Wert | RuntimeError wenn nicht gesetzt | ✅ |
|
||
| `revoke_permission` ohne Owner-Check | `check_single_entity_access` hinzugefügt | ✅ |
|
||
| `is_active=True` hart codiert im DB-Fallback | User-Status aus DB laden | ✅ |
|
||
| Plugin-Gate allow bei fehlendem Tenant | TODO - bricht Tests, muss in Produktion anders gelöst werden | ⚠️ |
|
||
| Public Share URL falsch | URL korrigiert | ✅ |
|
||
| Logout nur in Redis | Auch PostgreSQL invalidieren | ✅ |
|
||
| Rate-Limit nur auf IP | Token-Hash für Bearer-Auth | ✅ |
|
||
| RLS-Commit statt flush | Alle Commits durch flush ersetzt | ✅ |
|
||
| Webhook ohne Tenant-Context | `set_tenant_context` hinzugefügt | ✅ |
|
||
| `npm ci \|\| npm install` Fallback | Nur `npm ci` | ✅ |
|
||
|
||
### Roadmap
|
||
- **bandit:** Python Security-Scanner in CI-Pipeline
|
||
- **pip-audit:** Dependency-Scanning
|
||
- **npm audit:** Frontend-Dependency-Scanning
|
||
- **OWASP ZAP:** Web-Application-Scanner gegen Test-Instanz
|
||
- **Security-Test-Suite:** Eigene pytest-Tests für Security-Szenarien
|
||
|
||
---
|
||
|
||
## 5. CI/CD Pipeline
|
||
|
||
### Aktuelle Checks (scripts/ci_pipeline.sh)
|
||
|
||
1. Python Compile Check
|
||
2. Cross-Plugin Import Check
|
||
3. Alembic Revision Graph
|
||
4. Alembic Migration Test (wenn DATABASE_URL gesetzt)
|
||
5. Migration Hash Check
|
||
6. TypeScript Type Check
|
||
7. Frontend Build
|
||
8. Test Collection
|
||
9. Backend Tests
|
||
10. Frontend Tests
|
||
11. SQL Injection Check
|
||
12. npm ci strict mode
|
||
|
||
### Bekannte CI-Lücken
|
||
|
||
- **Smoke-Test gegen Build:** Tests laufen gegen `leocrm_test` DB, nicht gegen den aktuellen Build
|
||
- **Frontend-Tests:** vitest ist konfiguriert aber hat 0 Tests
|
||
- **Security-Scanning:** bandit/pip-audit nicht in Pipeline
|
||
- **E2E-Tests:** Nicht in Pipeline
|
||
|
||
---
|
||
|
||
## 6. Was getestet wird und was nicht
|
||
|
||
### ✅ Wird getestet
|
||
- API-Endpunkte (CRUD, Validierung, Permissions)
|
||
- Plugin-Lifecycle (Install, Activate, Deactivate)
|
||
- ABAC/Permission-System
|
||
- Cross-Tenant-Isolation (ohne RLS)
|
||
- AI-Proactive/GraphRAG (mit Mocks)
|
||
- Outbox/Event-System
|
||
- Backup/Restore
|
||
- Auth/Login/Logout
|
||
- Tags, Calendar, DMS, Mail, Contacts, Tasks
|
||
|
||
### ❌ Wird NICHT getestet
|
||
- **Frontend** (54k Zeilen, 0 Tests)
|
||
- **RLS-Policies** (Test-DB hat keine RLS)
|
||
- **LLM-APIs** (blockieren Suite, nicht gemockt)
|
||
- **Race Conditions** (keine Last-Tests)
|
||
- **Security-Edge-Cases** (SQL Injection nur oberflächlich)
|
||
- **Dockerfile/Deployment** (nur Code, nicht Infrastruktur)
|
||
- **CI-Scripts selbst** (Shell-Scripts nicht getestet)
|
||
- **Produktions-Logs** (keine Log-Analyse)
|
||
|
||
---
|
||
|
||
## 7. Roadmap
|
||
|
||
| Priorität | Maßnahme | Aufwand | Nutzen |
|
||
|-----------|---------|--------|-------|
|
||
| 🔴 Hoch | Frontend Unit-Tests (vitest) | mittel | 54k Zeilen abgedeckt |
|
||
| 🔴 Hoch | LLM-API-Calls mocken | gering | Suite läuft komplett durch |
|
||
| 🟡 Mittel | Security-Test-Suite | mittel | Security-Bugs automatisch gefunden |
|
||
| 🟡 Mittel | E2E-Tests (Playwright) | hoch | Kritische User-Flows getestet |
|
||
| 🟡 Mittel | Alembic-Migrationen in Test-DB | mittel | RLS testbar |
|
||
| 🟡 Mittel | Fixture-Optimierung (Session-Scope) | hoch | Suite 3x schneller |
|
||
| 🟢 Niedrig | bandit/pip-audit in CI | gering | Automatisches Security-Scanning |
|
||
| 🟢 Niedrig | Pro-Worker Test-DB | mittel | Parallelisierung möglich |
|
||
| 🟢 Niedrig | Log-Analyse Pipeline | gering | Produktions-Fehler erkannt |
|
||
|
||
---
|
||
|
||
## 8. Wann muss dieses Dokument aktualisiert werden?
|
||
|
||
Dieses Dokument MUSS aktualisiert werden bei:
|
||
|
||
1. **Neue Plugins oder Module** → Test-Kategorien und Abdeckung aktualisieren
|
||
2. **Security-Änderungen** → Security-Lücken und Fixes dokumentieren
|
||
3. **Neue Test-Infrastruktur** (z. B. vitest, Playwright) → Abschnitt hinzufügen
|
||
4. **CI-Pipeline-Änderungen** → Checks und Lücken aktualisieren
|
||
5. **Größere Refactoring** → Konventionen und Einschränkungen überprüfen
|
||
6. **Nach jeder Bugfix-Session** → Bekannte Lücken und Fixes dokumentieren
|
||
|
||
**Verantwortlich:** Agent/Entwickler der die Änderung durchführt.
|
||
|
||
---
|
||
|
||
## 9. Historie
|
||
|
||
| Datum | Ereignis |
|
||
|-------|---------|
|
||
| 2026-08-12 | Test-Strategie erstellt nach Bugfix-Session (14 Security-Bugs, ~170 Testfehler behoben) |
|
||
| 2026-08-13 | Phase A Verifikation: 8-Check-Pipeline verbindlich, A-TEST Ergebnisse dokumentiert, RLS- und Test-Isolations-Probleme bestätigt |
|
||
|
||
---
|
||
|
||
## 10. Verbindliche Test-Pipeline (8 Checks)
|
||
|
||
Diese Pipeline ist verbindlich für Phase-Gate-Reviews und muss vor jedem Phasenabschluss grün sein.
|
||
|
||
| # | Check | Kommando | Wann |
|
||
|---|-------|----------|------|
|
||
| 1 | Backend Tests | `python -m pytest -v --tb=short` | Einzel-Task + Block + Phase-Gate |
|
||
| 2 | Frontend Tests | `cd frontend && npx vitest run --reporter=verbose` | Einzel-Task + Block + Phase-Gate |
|
||
| 3 | TypeScript Check | `cd frontend && npx tsc --noEmit` | Einzel-Task + Block + Phase-Gate |
|
||
| 4 | E2E Tests | `cd frontend && npx playwright test` (kritische Flows) | Block + Phase-Gate |
|
||
| 5 | Frontend Build | `cd frontend && npm run build` | Block + Phase-Gate |
|
||
| 6 | Health Check | `curl /api/v1/health` → 200 | Phase-Gate (deployed) |
|
||
| 7 | Login Check | Login → 200 | Phase-Gate (deployed) |
|
||
| 8 | Cross-Tenant Test | `python -m pytest tests/test_cross_tenant_security.py` | Block + Phase-Gate |
|
||
|
||
### Staffelung
|
||
|
||
- **Einzel-Task:** relevante Unit-/Integration-/Frontend-Tests + Typecheck/Build soweit betroffen
|
||
- **Größerer Block:** Checks 1–5 + 8
|
||
- **Phase-Gate:** alle 8 Checks inklusive Deploy/Health/Login
|
||
|
||
### Phase A Verifikationsergebnisse (2026-08-13)
|
||
|
||
| Check | Ergebnis | Hinweis |
|
||
|-------|----------|---------|
|
||
| 1. Backend Tests | ✅ 145/145 (Auth/Resilience/Hooks/Contacts/Companies/Plugins) | ⚠️ test_tenant.py: 15 Batch-Failures (DB-Lock-Konflikte, Einzeltests passen) |
|
||
| 2. Frontend Tests | ✅ 744/751 passed | 7 Worker-Crashes (Resource-Limits im Container, nicht Test-Failures) |
|
||
| 3. TypeScript Check | ✅ 0 errors | |
|
||
| 4. E2E Tests | ⏳ Nicht ausgeführt | Playwright nicht in dieser Umgebung verfügbar |
|
||
| 5. Frontend Build | ✅ 3.5s, 90 precache entries | |
|
||
| 6. Health Check | ✅ 200 (Production: 33-74ms avg ~45ms) | |
|
||
| 7. Login Check | ✅ 200 (Production: 22-63ms avg ~48ms) | |
|
||
| 8. Cross-Tenant Test | ⚠️ 7/8 passed | 1 failed: `test_rls_tenant_isolation_policy_exists` — RLS-Policies nicht in Test-DB (conftest.py nutzt `create_all` statt Alembic) |
|
||
|
||
### Bekannte Test-Infrastruktur-Probleme (Phase A bestätigt)
|
||
|
||
1. **RLS nicht testbar** — `conftest.py` nutzt `Base.metadata.create_all` statt Alembic-Migrationen. RLS-Policies aus Migration 0004/0078 werden nicht erstellt. `test_rls_tenant_isolation_policy_exists` schlägt fehl. **Lösung:** T-RLS Task (Alembic-Migrationen in Test-DB).
|
||
|
||
2. **Test-Isolation** — `test_tenant.py` hat 15 Failures im Batch (DB-Lock-Konflikte bei TRUNCATE). Einzeltests passen. **Lösung:** Pro-Worker Datenbank (T-PARALLEL) oder Serial-Only-Mode.
|
||
|
||
3. **Vitest Worker-Crashes** — 7 von 96 Test-Files crashen mit „Worker exited unexpectedly". Resource-Limits im Container. **Lösung:** `--pool=forks` oder Memory-Limit erhöhen.
|
||
|
||
4. **Vollständiger pytest-Lauf dauert >15min** — 1401 Tests mit DB-Setup. **Lösung:** T-PARALLEL (pytest-xdist mit pro-Worker DB).
|
||
|
||
---
|
||
|
||
## Phase D — Undo/Restore Test-Ergebnisse
|
||
|
||
### Neue Test-Datei: `tests/test_restore_registry.py` (26 Tests)
|
||
|
||
| Test-Gruppe | Tests | Status |
|
||
|-------------|-------|--------|
|
||
| RestoreRegistry (Singleton, Register, Get, List, Overwrite, Excluded Fields) | 6 | ✅ |
|
||
| RestoreFromHistory (Unsupported type, History not found) | 2 | ✅ |
|
||
| HistoryHooks (extract_entity_id, register, hook fires record_history) | 6 | ✅ |
|
||
| TrashList (Returns delete entries, Entity type filter) | 2 | ✅ |
|
||
| BulkRestore (All success, Partial failure, All fail) | 3 | ✅ |
|
||
| Retention (Returns count, Zero when none) | 2 | ✅ |
|
||
| SensitiveFieldsExclusion (Default, Contact, DMS, Mail, All registered) | 5 | ✅ |
|
||
|
||
### Verifizierte Aspekte
|
||
|
||
- ✅ RestoreRegistry: Nur explizit registrierte Entity-Typen können restored werden
|
||
- ✅ Excluded Fields: id, tenant_id, timestamps, search_tsv, embedding werden nie restored
|
||
- ✅ Entity-spezifische Exclusions: Contact (relationship IDs), DMS (storage_path, content_hash), Mail (message_id, raw_path)
|
||
- ✅ Mail Special Handler: IMAP-Semantik (Trash-Move, Folder-Verify, kein falscher lokaler Status)
|
||
- ✅ Bulk Restore: Partial-Failure-Semantik (`partial_success` Flag, per-item results)
|
||
- ✅ Retention: GDPR-Hard-Delete nach konfigurierbarer Aufbewahrungsfrist
|
||
- ✅ Hook-based History: `do_action('entity.after_create/update/delete')` → `record_history()`
|
||
- ✅ Dynamic Permission Checks: Restore-Permission aus RestoreConfig, nicht hardcoded
|