T04: DMS plugin backend — folders + files + preview + OnlyOffice + shares + search + bulk — 106 tests, 97.90% coverage
This commit is contained in:
+94
-64
@@ -1,75 +1,105 @@
|
||||
# T09 Test Report — Coverage Improvement
|
||||
# Test Report — T04 DMS Plugin Coverage Improvement
|
||||
|
||||
## Date: 2026-06-29
|
||||
**Date**: 2026-06-29
|
||||
**Task**: T04 — DMS Plugin Coverage Improvement
|
||||
**Status**: ✅ COMPLETE
|
||||
|
||||
## Summary
|
||||
- **Previous**: 30 tests, 22 ACs passing, 45.37% coverage
|
||||
- **Current**: 135 T09 tests passing (105 new), 238 total regression passing, **84.12%** overall coverage
|
||||
- **Target**: 80%+ — **ACHIEVED**
|
||||
|
||||
## T09 Coverage Results
|
||||
- **Tests**: 106 total (27 existing + 38 error-path + 41 new coverage)
|
||||
- **Passed**: 106
|
||||
- **Failed**: 0
|
||||
- **Coverage**: 97.90% (target: ≥80%)
|
||||
- **Before**: 45.88%
|
||||
- **After**: 97.90%
|
||||
|
||||
```
|
||||
---------- coverage: platform linux, python 3.13.12-final-0 ----------
|
||||
Name Stmts Miss Branch BrPart Cover Missing
|
||||
---------------------------------------------------------------------------------
|
||||
app/ai/action_mapper.py 76 0 46 4 96.72%
|
||||
app/ai/llm_client.py 59 11 6 1 81.54%
|
||||
app/routes/ai_copilot.py 34 8 6 0 65.00%
|
||||
app/routes/workflows.py 92 25 24 0 62.93%
|
||||
app/services/ai_copilot_service.py 172 3 44 0 98.61%
|
||||
app/services/workflow_service.py 231 45 60 17 75.95%
|
||||
app/workflows/code/__init__.py 0 0 0 0 100.00%
|
||||
app/workflows/code/onboarding.py 24 2 4 2 85.71%
|
||||
app/workflows/engine.py 130 9 50 7 90.00%
|
||||
---------------------------------------------------------------------------------
|
||||
TOTAL 818 103 240 31 84.12%
|
||||
```
|
||||
## Verification Command
|
||||
|
||||
## Per-Module Coverage Improvement
|
||||
|
||||
| Module | Before | After | Delta |
|
||||
|--------|--------|-------|-------|
|
||||
| app/workflows/engine.py | 0% | 90.00% | +90.00% |
|
||||
| app/services/ai_copilot_service.py | 38.89% | 98.61% | +59.72% |
|
||||
| app/ai/action_mapper.py | 43.44% | 96.72% | +53.28% |
|
||||
| app/routes/workflows.py | 59.48% | 62.93% | +3.45% |
|
||||
| app/ai/llm_client.py | 64.62% | 81.54% | +16.92% |
|
||||
| app/routes/ai_copilot.py | 65% | 65.00% | +0.00% |
|
||||
| app/services/workflow_service.py | 62.54% | 75.95% | +13.41% |
|
||||
| **Overall** | **45.37%** | **84.12%** | **+38.75%** |
|
||||
|
||||
## New Tests Added (105 total)
|
||||
|
||||
### test_workflows.py (48 new tests)
|
||||
- **WorkflowEngine unit tests (22)**: action noop, create_notification, completion, approval step, notification step, condition eq/ne/gt/lt/contains (string+list), no-match advance, condition completion, unknown step type, workflow not found, step index beyond steps, handle_event (match + no-match), register_workflow_event_handlers
|
||||
- **Route error path tests (9)**: update/delete workflow 404, update/delete RBAC 403, create instance 404, advance/cancel 404, advance completed 400, cancel rejected 400
|
||||
- **Service edge case tests (9)**: check_timeout no_timeout_at, check_timeout completed status, auto_reject no initiated_by, find_workflows no match, start_instance no match, list_workflows active filter, update_workflow with steps, update/delete/get not found
|
||||
|
||||
### test_ai_copilot.py (57 new tests)
|
||||
- **ActionMapper unit tests (18)**: create/delete/update/list company, create/list contact, list/create workflow, help intent, unknown query, update with phone/email, update no fields, company list all pattern, empty query
|
||||
- **LLMClient unit tests (11)**: mock generate, mock no actions, mock with context, API mode init, api_base default, LLMResponse.to_dict, parse valid/invalid JSON, build system prompt (with/without context), get/reset singleton
|
||||
- **AI Copilot service tests (20)**: process_query (new/existing/invalid/empty conversation), execute_action (companies GET/POST/PATCH/delete + error paths, contacts GET/POST/unsupported, workflows GET/unsupported, unknown entity, invalid conversation, RBAC blocked), get_history (pagination/empty), _derive_rbac_from_path (5 entity paths), _safe_iso (3 cases), _get_attr (2 cases)
|
||||
- **Route error path tests (4)**: execute 404, execute RBAC 403, history/execute unauthenticated 401
|
||||
|
||||
## Test Commands
|
||||
|
||||
### T09 Coverage Suite
|
||||
```bash
|
||||
cd /a0/usr/workdir/dev-projects/leocrm && python -m pytest tests/test_ai_copilot.py tests/test_workflows.py --cov=app.ai --cov=app.workflows --cov=app.services.ai_copilot_service --cov=app.services.workflow_service --cov=app.routes.ai_copilot --cov=app.routes.workflows --cov-report=term-missing --tb=short -v
|
||||
cd /a0/usr/workdir/dev-projects/leocrm
|
||||
python -m pytest tests/test_dms.py tests/test_dms_errors.py tests/test_dms_coverage.py -v --tb=short --cov=app/plugins/builtins/dms --cov-report=term-missing
|
||||
```
|
||||
**Result**: 135 passed, coverage 84.12%
|
||||
|
||||
### Full Regression
|
||||
```bash
|
||||
cd /a0/usr/workdir/dev-projects/leocrm && python -m pytest tests/ -v --tb=short
|
||||
## Coverage Report
|
||||
|
||||
```
|
||||
Name Stmts Miss Branch BrPart Cover Missing
|
||||
-----------------------------------------------------------------------------------
|
||||
app/plugins/builtins/dms/__init__.py 2 0 0 0 100.00%
|
||||
app/plugins/builtins/dms/models.py 26 0 0 0 100.00%
|
||||
app/plugins/builtins/dms/plugin.py 5 0 0 0 100.00%
|
||||
app/plugins/builtins/dms/routes.py 364 6 128 6 97.56%
|
||||
app/plugins/builtins/dms/schemas.py 46 0 0 0 100.00%
|
||||
-----------------------------------------------------------------------------------
|
||||
TOTAL 443 6 128 6 97.90%
|
||||
```
|
||||
**Result**: 238 passed, 0 failed, 2 warnings (pre-existing deprecation warning)
|
||||
|
||||
## Smoke Test
|
||||
- T09 coverage suite: 135/135 tests pass
|
||||
- Full regression: 238/238 tests pass (133 existing + 105 new)
|
||||
- No production code modified — only test files updated
|
||||
- All 22 original ACs still pass
|
||||
nREPORT
|
||||
echo 'test_report.md created'
|
||||
## Remaining Uncovered Lines (6 statements, 4 branches)
|
||||
|
||||
- Line 100: `_build_path` orphaned folder_id lookup
|
||||
- Line 120→116: tree building branch (folder with parent not in map)
|
||||
- Line 184→199: create_folder parent_id set but parent_folder is None
|
||||
- Lines 279-282: `_is_descendant` loop traversal edge case
|
||||
- Line 299: path building `cur is None` break
|
||||
- Lines 639-640: `_stream()` generator body (coverage limitation with generators)
|
||||
- Line 902→901: shared-with-me perm_map branch
|
||||
|
||||
## Files Changed
|
||||
|
||||
1. `tests/test_dms_coverage.py` — NEW: 41 tests covering folder tree, cascade delete, file upload edge cases, preview missing on disk, share multi-user/groups, search special chars, shared-with-me with data, bulk mixed IDs, tenant isolation
|
||||
2. `pyproject.toml` — Added `concurrency = ["greenlet"]` to `[tool.coverage.run]` to fix async coverage tracking (was causing coverage to miss async function bodies)
|
||||
|
||||
## Key Fix: Coverage Concurrency Setting
|
||||
|
||||
The original coverage of 45.88% was misleadingly low because coverage.py was not tracking async function bodies with the default settings on Python 3.13. Adding `concurrency = ["greenlet"]` to `pyproject.toml` `[tool.coverage.run]` section fixed this, allowing proper tracking of async route handlers.
|
||||
|
||||
## Test Categories
|
||||
|
||||
### Folder Coverage (8 tests)
|
||||
- Deeply nested tree with path verification
|
||||
- Invalid parent_id in query params
|
||||
- 3-level nested folder creation
|
||||
- Rename + move in single request
|
||||
- Move to root via parent_id=null
|
||||
- Empty body update (no changes)
|
||||
- Invalid folder_id in PATCH
|
||||
- 3-level cascade delete with files in each folder
|
||||
- Tenant isolation (cross-tenant folder access)
|
||||
|
||||
### File Coverage (12 tests)
|
||||
- File too large (413) via mock patch
|
||||
- Empty file upload
|
||||
- Invalid folder_id in upload
|
||||
- Invalid file_id in delete/restore/update/preview/edit-session
|
||||
- Rename + move in single request
|
||||
- Move to root via folder_id=null
|
||||
- Empty body update (no changes)
|
||||
- Preview file missing on disk (404 file_missing)
|
||||
- Tenant isolation (cross-tenant file access)
|
||||
|
||||
### Share Coverage (8 tests)
|
||||
- Share with multiple users
|
||||
- Share with both users and groups
|
||||
- Invalid group_id (400)
|
||||
- Duplicate group share ignored
|
||||
- Remove user share
|
||||
- Remove share with no match (idempotent)
|
||||
- Invalid group_id in remove (400)
|
||||
- Remove both user and group shares
|
||||
|
||||
### Search Coverage (3 tests)
|
||||
- Special characters in filename
|
||||
- Partial name match
|
||||
- Tenant isolation
|
||||
|
||||
### Shared-with-me Coverage (3 tests)
|
||||
- Multiple shared files
|
||||
- Excludes soft-deleted files
|
||||
- Write access level display
|
||||
|
||||
### Bulk Coverage (5 tests)
|
||||
- Bulk move with mixed valid/invalid IDs
|
||||
- Bulk delete with mixed valid/invalid IDs
|
||||
- Invalid target_folder_id (400)
|
||||
- Bulk delete tenant isolation
|
||||
- Bulk move tenant isolation
|
||||
|
||||
Reference in New Issue
Block a user