7.9 KiB
phase, plan, subsystem, status, tags, dependency_graph, tech_stack, key_files, decisions, metrics
| phase | plan | subsystem | status | tags | dependency_graph | tech_stack | key_files | decisions | metrics | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 13 | 05 | cloud-operations | complete |
|
|
|
|
|
|
Phase 13 Plan 05: Upload Mechanics Slice Summary
One-liner: Provider-normalized upload conflict/retry/reauth typed mechanics with keep_both_name() helper for D-03/D-05 queue-level collision resolution.
Tasks Completed
| Task | Name | Commit | Key Files |
|---|---|---|---|
| 1 (RED) | Add failing upload conflict and keep-both naming tests | f47e36d |
test_cloud_backends.py |
| 1 (GREEN) | Implement keep_both_name() helper | e1ce4cb |
services/cloud_operations.py |
| 2 | Add typed upload route mechanics tests | 3ddf4da |
test_cloud_mutations.py |
What Was Built
Task 1: Provider-normalized upload contract tests + keep_both_name() helper
RED phase: Added 23 tests (16 provider behavioral + 7 keep-both naming) to test_cloud_backends.py:
TestUploadConflictSemantics:
test_google_drive_upload_success_returns_typed_result— kind='uploaded' with provider_item_id, name, size; no credentials in resulttest_google_drive_upload_transient_error_returns_offline_kind— HTTP 503 → kind='offline' (retryable)test_google_drive_upload_auth_error_returns_reauth_kind— HTTP 401 → kind='reauth_required'test_onedrive_upload_success_returns_typed_result— resumable session success → kind='uploaded'test_onedrive_upload_conflict_returns_typed_conflict_kind— conflictBehavior=fail 409 → kind='conflict', reason='name_collision'test_onedrive_upload_never_writes_cloud_items— source scan confirms no cloud_items importtest_webdav_upload_success_returns_typed_result— PUT success → kind='uploaded' with correct pathtest_webdav_upload_provider_error_returns_error_kind— connection timeout → kind='offline'test_upload_result_never_contains_credentials— parametrized over 4 providers; no access_token/refresh_token in resulttest_upload_result_kind_in_known_kinds— parametrized over 4 providers; kind must be in MUT_KINDS constant set
TestKeepBothNaming:
test_keep_both_naming_module_exists— services.cloud_operations exposes keep_both_nametest_keep_both_basic_pdf— Report.pdf + 1 → Report (1).pdftest_keep_both_counter_increments— Report.pdf + 2 → Report (2).pdftest_keep_both_no_extension— README + 1 → README (1)test_keep_both_compound_extension— archive.tar.gz + 1 → archive (1).tar.gztest_keep_both_preserves_existing_counter— Report (1).pdf + 2 → Report (1) (2).pdftest_keep_both_spaces_in_name— My Document.docx + 1 → My Document (1).docx
GREEN phase: Implemented keep_both_name(filename, counter) in services/cloud_operations.py:
def keep_both_name(filename: str, counter: int) -> str:
"""Insert collision counter before first file extension.
Report.pdf + 1 → Report (1).pdf
archive.tar.gz + 1 → archive (1).tar.gz
README + 1 → README (1)
"""
The counter is inserted before the first dot (not last), so compound extensions are preserved intact.
Task 2: Typed upload route mechanics tests
Added 7 tests to test_cloud_mutations.py verifying Task 2 behavioral contract:
test_upload_success_returns_typed_uploaded_body— success body has kind/provider_item_id/name/sizetest_upload_provider_retryable_error_returns_offline_kind— offline kind from provider error, no bare 500test_upload_provider_conflict_returns_conflict_kind— provider-detected conflict when cache missestest_upload_reauth_result_is_typed_and_not_500— CONN-02 token expiry surfaces as typed bodytest_upload_queue_control_semantics_are_backend_authored— all 4 required queue fields presenttest_upload_keep_both_name_format— service utility accessible to route layertest_upload_foreign_user_blocked— IDOR protection on upload endpoint
All 7 tests pass because the upload route in operations.py (Plan 04) already implements the typed result dispatch correctly. Plan 05 adds the test coverage that was missing.
Deviations from Plan
Auto-fixed Issues
None. The plan stated providers should "implement D-03 and D-04 inside the provider layer" but providers already had complete upload_file implementations from Plan 03. Plan 05 correctly adds behavioral tests and the naming utility that were genuinely missing.
Note on scope: The providers already normalized upload results into typed kinds from Plan 03. Plan 05's contribution is:
- Behavioral tests that prove the normalization is correct
- The
keep_both_name()service helper that the upload queue needs for conflict resolution
Known Stubs
None. The upload mechanics are fully functional. Reconcile and audit follow-through are intentionally deferred to Plans 06+.
Threat Flags
No new security surfaces introduced. Upload route is covered by T-13-16 (typed conflict bodies forbid silent overwrite), T-13-17 (no provider URLs/tokens in response), T-13-18 (WebDAV SSRF guard preserved via init).
Self-Check: PASSED
- FOUND:
backend/services/cloud_operations.pywithkeep_both_name()function - FOUND:
backend/tests/test_cloud_backends.py(modified — 23 new tests) - FOUND:
backend/tests/test_cloud_mutations.py(modified — 7 new tests) - FOUND commit:
f47e36d(Task 1 RED) - FOUND commit:
e1ce4cb(Task 1 GREEN — keep_both_name) - FOUND commit:
3ddf4da(Task 2 — route mechanics tests) - Full suite: 741 passed, 17 skipped, 4 deselected, 13 xfailed