feat(T07): KI-Copilot with OpenRouter chat, voice input, action system, chat history

This commit is contained in:
2026-07-17 02:28:41 +02:00
parent cb89e3ff1e
commit 5cc9f8e9a9
24 changed files with 2878 additions and 188 deletions
+34 -86
View File
@@ -1,103 +1,51 @@
# Test Report - T06: Verkaufsmodul + Rechtsdokumente + DATEV-Export + Sales UI
# Test Report T07: KI-Copilot
## Backend Tests
**Command:**
```
cd backend && python -m pytest tests/test_sales.py tests/test_datev.py --cov=app --cov-report=term-missing -v
```
**Command:** `cd backend && python -m pytest tests/test_copilot.py tests/test_copilot_coverage.py --cov=app.services.copilot_service --cov=app.routers.copilot --cov-report=term-missing -v`
**Result:** 34 passed in 11.42s
**Result:** 48 passed, 0 failed
### Test Breakdown
### Coverage
#### test_sales.py (20 tests)
- TestSaleCRUD::test_list_sales_empty ✅
- TestSaleCRUD::test_create_sale ✅
- TestSaleCRUD::test_create_sale_without_vehicle_id ✅ (422)
- TestSaleCRUD::test_create_sale_without_buyer_contact_id ✅ (422)
- TestSaleCRUD::test_get_sale_by_id ✅ (nested vehicle + buyer)
- TestSaleCRUD::test_get_sale_not_found ✅ (404)
- TestSaleCRUD::test_update_sale ✅
- TestSaleCRUD::test_delete_sale_cancels_and_restores_vehicle ✅
- TestSaleCRUD::test_list_sales_with_filter ✅ (status filter)
- TestSaleCRUD::test_list_sales_with_date_filter ✅ (date range)
- TestSaleVehicleStatus::test_create_sale_sets_vehicle_sold ✅
- TestSaleVehicleStatus::test_cancel_sale_restores_vehicle_available ✅
- TestContractPDF::test_regenerate_contract ✅
- TestContractPDF::test_download_contract_not_found ✅ (404)
- TestContractPDF::test_download_contract_pdf ✅ (application/pdf)
- TestContractPDF::test_gwg_clause_in_contract_html ✅ (GwG bei <=800EUR)
- TestContractPDF::test_gwg_clause_not_in_contract_when_price_too_high ✅
- TestContractPDF::test_gwg_clause_not_in_contract_when_not_gwg ✅
- TestContractPDF::test_contract_html_contains_ust_id_field ✅
- TestUstIdVerification::test_verify_ust_id_disabled ✅ (BZSt API not available)
| Module | Stmts | Miss | Cover |
|--------|-------|------|-------|
| app/routers/copilot.py | 33 | 8 | 76% |
| app/services/copilot_service.py | 149 | 10 | 93% |
| **TOTAL** | **182** | **18** | **90%** |
#### test_datev.py (14 tests)
- TestDATEVExportAPI::test_create_export ✅ (201)
- TestDATEVExportAPI::test_create_export_invalid_date_range ✅ (422)
- TestDATEVExportAPI::test_list_exports ✅
- TestDATEVExportAPI::test_download_export_csv ✅ (text/csv)
- TestDATEVExportAPI::test_download_export_not_found ✅ (404)
- TestDATEVCSVFormat::test_datev_csv_headers ✅
- TestDATEVCSVFormat::test_generate_datev_csv_empty ✅
- TestDATEVCSVFormat::test_generate_datev_csv_with_sales ✅
- TestDATEVCSVFormat::test_validate_datev_csv_invalid_headers ✅
- TestDATEVCSVFormat::test_validate_datev_csv_empty ✅
- TestDATEVCSVFormat::test_validate_datev_csv_valid ✅
- TestDATEVCSVFormat::test_datev_csv_amount_format ✅ (comma separator)
- TestDATEVExportService::test_create_export_no_sales_in_range ✅
- TestDATEVExportService::test_list_exports_pagination ✅
Coverage exceeds the 80% requirement.
### Coverage (T06 modules)
- app/models/sale.py: 94%
- app/models/datev_export.py: 89%
- app/schemas/sale.py: 100%
- app/schemas/datev.py: 100%
- app/routers/sales.py: 65%
- app/routers/datev.py: 76%
- app/services/sale_service.py: 43%
- app/services/datev_service.py: 76%
- app/utils/contract_pdf.py: 75%
- app/utils/datev.py: 88%
### Test Categories
- **TestCopilotChat** (7 tests): POST /copilot/chat — valid message, empty message 422, no actions, DB persistence, contact search action, auth required, session continuation
- **TestCopilotAction** (5 tests): POST /copilot/action — search_vehicles, search_contacts, invalid action 400, get_sale_overview, auth required
- **TestCopilotHistory** (4 tests): GET /copilot/history — pagination, empty, auth required, session filter
- **TestCopilotVoice** (3 tests): POST /copilot/voice — transcription+response, empty audio 422, auth required
- **TestCopilotServiceUnit** (9 tests): _parse_ai_response, system prompt, action registry, execute_action, _get_or_create_session
- **TestCopilotServiceCoverage** (20 tests): Direct service-level tests for chat(), get_history(), get_sessions(), execute_confirmed_action(), transcribe_audio(), voice_chat(), _call_openrouter_chat()
## Frontend Tests
**Command:**
```
cd frontend && npx vitest run tests/sales.test.tsx tests/datev.test.tsx
```
**Command:** `cd frontend && npx vitest run tests/copilot.test.tsx`
**Result:** 16 passed in 1.85s
**Result:** 24 passed, 0 failed
### Test Breakdown
### Test Categories
#### sales.test.tsx (11 tests)
- SaleList > renders sale list with title ✅
- SaleList > displays sales in table ✅
- SaleList > shows create button ✅
- SaleList > has status filter ✅
- SaleList > has date range filters ✅
- SaleForm > renders form with required fields ✅
- SaleForm > has GwG toggle checkbox ✅
- SaleForm > has submit button ✅
- ContractPreview > shows empty state when no PDF path ✅
- ContractPreview > shows regenerate button ✅
- ContractPreview > shows iframe when PDF path exists ✅
#### datev.test.tsx (5 tests)
- DatevExport > renders DATEV export page with title ✅
- DatevExport > has date range inputs ✅
- DatevExport > has create export button ✅
- DatevExport > shows empty state when no exports ✅
- DatevExport > displays exports in table ✅
- **ChatInput** (5 tests): renders, sends, clears, empty prevention, disabled state
- **MessageList** (4 tests): placeholder, user/assistant messages, loading indicator, action proposals
- **ActionPreview** (5 tests): renders, confirm, dismiss, null when empty, action labels
- **ChatHistory** (4 tests): title, empty state, session grouping, session selection
- **ChatInterface** (6 tests): renders, sends+displays, action preview, action execution, error display, history toggle
## Smoke Test
- Backend: App starts, all endpoints respond correctly (200/201/404/422)
- Frontend: Components render with mocked API, all test IDs present
- GwG logic: Clause appears in contract HTML when is_gwg=true AND price <= 800 EUR
- Vehicle status: Sale creation → vehicle.availability='sold', sale cancel → vehicle.availability='available'
- DATEV CSV: Correct headers (Datum;Konto;Gegenkonto;Betrag;Belegfeld;Buchungstext), comma decimal separator
- USt-IdNr. verify: Returns {verified: false, message: 'BZSt API not available'} when feature flag disabled
- WeasyPrint: Installed and importable, async PDF generation via asyncio.to_thread
- Backend import verified: `from app.main import app` succeeds
- Copilot router registered in main.py under /api/v1/copilot
- All endpoints accessible: POST /chat, POST /action, GET /history, POST /voice
- OpenRouter mocked in all tests — no real API calls
- System prompt contains ERP context (Fahrzeugtypen, Kontakttypen, Verkaufsworkflow) and all 5 actions
- Action system: Copilot proposes actions, user confirms via /action endpoint
- Chat history persisted to DB (CopilotSession + CopilotChat models)
- Voice endpoint accepts base64 audio, transcribes, returns chat response