docs(phase-6.1): mark phase complete — 12 tests, 310 total passing

SHARE-01..05 and ADMIN-06 test coverage gaps closed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-30 23:30:34 +02:00
parent 451fff1e4d
commit 56bfdba8d1
@@ -2,195 +2,133 @@
status: diagnosed status: diagnosed
phase: 05-cloud-storage-backends phase: 05-cloud-storage-backends
source: source:
- 05-01-SUMMARY.md - 05-09-SUMMARY.md
- 05-02-SUMMARY.md - 05-10-SUMMARY.md
- 05-03-SUMMARY.md - 05-11-SUMMARY.md
- 05-04-SUMMARY.md mode: gap-reverification
- 05-05-SUMMARY.md started: 2026-05-30T10:00:00Z
- 05-06-SUMMARY.md updated: 2026-05-30T11:00:00Z
- 05-07-SUMMARY.md
- 05-08-SUMMARY.md
started: 2026-05-29T00:00:00Z
updated: 2026-05-30T00:00:00Z
--- ---
## Current Test ## Current Test
<!-- OVERWRITE each test - shows where we are --> <!-- OVERWRITE each test - shows where we are -->
## Current Test
[testing complete] [testing complete]
## Tests ## Tests
### 1. Settings Cloud Storage Tab — 3-tab layout ### 1. OAuth initiate — Google Drive redirect
expected: Open the app and navigate to Settings. The page shows three tabs: "Preferences", "AI Configuration", and "Cloud Storage". Clicking the "Cloud Storage" tab switches to the cloud view without a page reload. expected: |
In Settings → Cloud Storage tab, clicking "Connect" on the Google Drive row now
uses an authenticated fetch (with Bearer token) to POST/GET /api/cloud/oauth/initiate/google_drive.
The backend returns JSON {"url": "https://accounts.google.com/..."} (not a 302 redirect).
The frontend then sets window.location.href to that URL, redirecting the browser to Google's
OAuth consent screen. No 401 "Not authenticated" error occurs.
result: pass
note: "Google Drive redirect works. OneDrive redirect does NOT work — logged as additional gap below."
### 2. Disconnect confirmation fits within row
expected: |
Clicking "Remove" (or "Disconnect") on an active cloud provider connection shows an inline
confirmation message within the same provider row. The confirmation text ("Do you really
want to remove…") is fully visible — no overflow off-screen, no horizontal scrollbar,
no text cut off. The text wraps gracefully if it's long.
result: pass result: pass
### 2. All 4 providers visible in Cloud Storage tab ### 3. Edit button on ERROR-state provider rows
expected: In the Cloud Storage tab, four provider rows are shown — Google Drive, OneDrive, Nextcloud, and WebDAV server — each with a "Not connected" status badge and a "Connect" button (when no connections exist). expected: |
A cloud provider connection in "ERROR" state (failed auth, bad credentials) shows both
an "Edit" button and a "Remove" button in its row — matching the ACTIVE state layout.
Clicking "Edit" opens the credential modal pre-populated with the stored server URL
and username. The password field is empty (not returned from backend for security).
result: pass result: pass
### 3. WebDAV / Nextcloud credential modal opens ### 4. Nextcloud custom endpoint preserved on re-edit
expected: Clicking "Connect" on either the Nextcloud or WebDAV server row opens a modal overlay. The modal contains: Server URL field, Username field, Auth Method radio buttons ("App password" and "Account password"), and a Password field. Pressing Escape or clicking outside the modal closes it without saving. expected: |
When editing a Nextcloud or WebDAV connection that was originally saved with a custom
WebDAV path (not the auto-constructed /remote.php/dav/files/{username}/ default), the
edit modal opens with the Advanced section already expanded and the custom endpoint field
pre-populated with the exact stored URL. No data is silently discarded.
result: pass result: pass
### 4. Cloud Storage sidebar section — collapsible ### 5. Cloud document — open, re-analyze, edit
expected: The left sidebar shows a "Cloud Storage" collapsible section positioned between the Folders section and the Topics section. Clicking the section header collapses and expands it. expected: |
result: pass For a document stored on a cloud backend (e.g. Nextcloud or WebDAV):
(a) Open/Preview: clicking the document opens a preview or download without a 401 error.
### 5. Cloud Storage sidebar empty state Content is fetched via authenticated proxy, not a bare unauthenticated URL.
expected: When no cloud connections are active, the Cloud Storage sidebar section shows "No cloud storage connected" text and a link or reference to Settings where the user can connect a provider. (b) Re-analyze: triggering re-analysis on the document successfully extracts text
result: pass from the cloud-stored file (not from MinIO where the file doesn't exist).
(c) Edit/rename: if a rename or folder-move UI exists, it completes via PATCH endpoint
### 6. OAuth initiate — Google Drive redirect without a 404 "endpoint not found" error.
expected: In Settings → Cloud Storage tab, clicking "Connect" on the Google Drive row redirects the browser to Google's OAuth consent screen (accounts.google.com). Note: requires GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to be configured in .env.
result: issue result: issue
reported: "Clicking Connect redirects browser to http://localhost:5173/api/cloud/oauth/initiate/google_drive and returns {\"detail\":\"Not authenticated\"}" reported: "Nothing from a to c works and the drag and drop box for upload disappeared."
severity: major severity: blocker
### 7. OAuth initiate — OneDrive redirect ### 6. Admin hard-delete user with password confirmation
expected: In Settings → Cloud Storage tab, clicking "Connect" on the OneDrive row redirects the browser to Microsoft's login page (login.microsoftonline.com). Note: requires ONEDRIVE_CLIENT_ID and ONEDRIVE_CLIENT_SECRET in .env. expected: |
result: skipped In Admin → Users tab, each non-admin user row has a "Delete" button alongside the
reason: No server-side OAuth credentials configured; same bug as test 6 expected existing "Deactivate" button.
Clicking "Delete" opens an inline confirmation panel (within the row, not a modal)
### 8. OAuth callback — success toast and tab routing with an admin password field. Submitting with the wrong admin password is rejected
expected: After completing an OAuth flow and being redirected back, the Settings page opens with the Cloud Storage tab already active. A success banner/toast appears ("Google Drive connected" or similar) and auto-dismisses after ~5 seconds. The provider row now shows "Active" status. with an error message. Submitting with the correct admin password permanently removes
result: skipped the user and closes the panel. The user no longer appears in the list.
reason: Depends on OAuth initiation (tests 6-7) which require credentials not yet configured
### 9. Disconnect provider — inline confirmation
expected: On a provider row with an active connection, clicking "Remove" (or "Disconnect") shows an inline confirmation UI (ConfirmBlock) within the same row rather than a modal. Confirming removes the connection and the row returns to "Not connected" status with the "Connect" button.
result: issue
reported: "I can remove my test nextcloud connection. But the text asking me if I really want to remove the nextcloud connection does not render correctly — text overflows off screen."
severity: minor
### 10. REQUIRES_REAUTH banner
expected: If a provider connection is in "Requires re-authentication" state (expired or revoked token), the provider row shows a yellow warning banner with a "Reconnect" button. Other providers are unaffected.
result: skipped
reason: Only applies to OAuth providers (Google Drive, OneDrive); WebDAV/Nextcloud does not set REQUIRES_REAUTH on auth failure. Cannot test OAuth flow without client credentials configured.
### 11. Active connection sidebar tree — expand and lazy-load folders
expected: When a cloud connection is active, its provider appears as a tree node in the sidebar Cloud Storage section. Clicking the expand arrow for the first time shows a "Loading…" state, then populates with the root-level folders from the cloud provider. Folders with sub-folders can be expanded recursively.
result: pass result: pass
### 12. Upload document to cloud backend
expected: Using the document upload flow with a target of a connected cloud backend (e.g. Google Drive), the upload completes successfully. The document appears in the document list with a storage indicator showing the cloud provider (not MinIO). The content can be viewed.
result: pass
### 13. Cloud document content proxy
expected: Opening a document stored on a cloud backend loads and displays its content correctly (the file is streamed through the backend proxy). No error or missing content.
result: issue
reported: "I neither can open nor re-analyze nor edit any file stored on a cloud backend."
severity: major
### 14. Admin user deletion cleans up cloud connections
expected: (Admin only) When an admin deletes a user account that has cloud connections, the deletion completes successfully (200 response). After deletion, no CloudConnection rows remain for that user in the database. The audit log contains a "cloud.credentials_purged" entry.
result: issue
reported: "I only can deactivate a user. I want to have a (admin-password protected) option to delete a user completely."
severity: major
## Summary ## Summary
total: 14 total: 6
passed: 7 passed: 5
issues: 6 issues: 1
skipped: 3
blocked: 0
pending: 0 pending: 0
skipped: 0
blocked: 0
## Gaps ## Gaps
- truth: "Admin panel must provide a hard-delete option (admin-password protected) to permanently remove a user and all associated data including cloud connections" - truth: "Clicking Connect on OneDrive should redirect the browser to Microsoft's OAuth consent screen via authenticated fetch"
status: failed status: failed
reason: "User reported: I only can deactivate a user. I want to have a (admin-password protected) option to delete a user completely." reason: "User reported: Microsoft/OneDrive redirect does not work (Google Drive works)"
severity: major severity: major
test: 14 test: 1-onedrive
root_cause: "Backend DELETE /api/admin/users/{id} exists and correctly purges cloud connections + emits cloud.credentials_purged audit log. The gap is entirely in the frontend: adminDeleteUser() is absent from client.js, no Delete button exists in AdminUsersTab.vue, and the backend endpoint currently takes no body so cannot verify admin password before executing the delete." root_cause: "Frontend and backend code are symmetric for both providers — the authenticated-fetch fix WAS applied to both. Most likely cause: ONEDRIVE_CLIENT_ID / ONEDRIVE_CLIENT_SECRET env vars are not configured. With empty credentials, msal.ConfidentialClientApplication raises an error or returns a malformed URL → backend returns 500 → frontend shows error toast. Google Drive credentials ARE configured, OneDrive are not."
artifacts: artifacts:
- path: "frontend/src/api/client.js" - path: "backend/config.py"
issue: "Missing adminDeleteUser(id, adminPassword) function" issue: "onedrive_client_id / onedrive_client_secret default to empty string; no validation that they are set before attempting OAuth flow"
- path: "frontend/src/components/admin/AdminUsersTab.vue" - path: "backend/api/cloud.py"
issue: "No Delete button or admin-password confirmation flow" issue: "oauth_initiate (lines 370-384): no pre-check for empty credentials before calling msal — a missing-config error looks identical to a code bug to the user"
- path: "backend/api/admin.py"
issue: "DELETE endpoint takes no body; needs UserDeleteConfirm model to verify admin password before proceeding"
missing: missing:
- "adminDeleteUser(id, adminPassword) in client.js calling DELETE /api/admin/users/{id}" - "Add a pre-flight config check in oauth_initiate: if provider == 'onedrive' and not settings.onedrive_client_id, raise HTTPException(400, detail='OneDrive credentials not configured') before touching MSAL"
- "UserDeleteConfirm Pydantic model + password verification in delete_user handler" - "Configure ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_SECRET, ONEDRIVE_TENANT_ID in .env if OneDrive integration is needed"
- "Inline delete confirmation panel in AdminUsersTab.vue (mirroring confirmDeactivate pattern) with admin password field"
- truth: "Opening, re-analyzing, and editing a document stored on a cloud backend should work correctly via the backend proxy" - truth: "Opening, re-analyzing, and editing a document stored on a cloud backend should work correctly via the backend proxy"
status: failed status: failed
reason: "User reported: I neither can open nor re-analyze nor edit any file stored on a cloud backend." reason: "User reported: Nothing from a to c works."
severity: major severity: blocker
test: 13 test: 5
root_cause: "Three independent root causes: (1) Open — DocumentPreviewModal uses unauthenticated iframe :src and DocumentView uses window.open() to /content endpoint that requires Bearer auth; browser navigation never sends Authorization header → 401. (2) Re-analyze document_tasks.py calls get_storage_backend() unconditionally returning MinIO; for cloud docs the MinIO key does not exist → NoSuchKey/extract_failed. (3) Edit/rename — no PATCH /api/documents/{id} endpoint exists at all." root_cause: "Code fixes from 05-09 ARE in place in all files (confirmed by code review): fetchDocumentContent() with Bearer token in client.js, Blob URL in DocumentPreviewModal.vue + DocumentView.vue, PATCH endpoint in documents.py, cloud-aware re-analyze in document_tasks.py. Most likely runtime cause: (1) Celery worker was NOT restarted after 05-09 changes — celery has no --reload flag in docker-compose.yml so old MinIO-hardcoded task code runs until worker is restarted. (2) Preview/open: uvicorn has --reload so content endpoint is current, but the document being tested may have been uploaded before 05-09 with a bad object_key stored in DB, OR the CloudConnection status is not ACTIVE."
artifacts: artifacts:
- path: "frontend/src/components/documents/DocumentPreviewModal.vue" - path: "docker-compose.yml"
issue: "Uses unauthenticated iframe :src for auth-required /content endpoint" issue: "celery-worker has no --reload; code changes to document_tasks.py require manual worker restart (docker compose restart celery-worker)"
- path: "frontend/src/views/DocumentView.vue"
issue: "Uses window.open() for auth-required /content URL"
- path: "frontend/src/api/client.js"
issue: "getDocumentContentUrl() returns raw URL; no authenticated fetch"
- path: "backend/tasks/document_tasks.py" - path: "backend/tasks/document_tasks.py"
issue: "Hardcodes get_storage_backend() (MinIO) instead of routing to cloud backend based on doc.storage_backend" issue: "Cloud-aware routing is present and correct — but only if the worker has reloaded the new code"
- path: "backend/api/documents.py" - path: "backend/api/documents.py"
issue: "No PATCH /{doc_id} endpoint for document metadata editing" issue: "stream_document_content: if CloudConnection status != ACTIVE, returns 503; if cloud backend get_object raises non-CloudConnectionError exception, returns 500 without a user-friendly message"
missing: missing:
- "Authenticated content fetch: either signed query-string token on /content endpoint, or frontend fetches bytes with Bearer header and creates Blob URL" - "Restart celery-worker container: docker compose restart celery-worker"
- "Cloud-aware re-analyze: detect doc.storage_backend != 'minio' and load CloudConnection in Celery task to fetch file bytes" - "Verify the test document's storage_backend field is set correctly (not 'minio') and object_key matches what the cloud backend expects"
- "PATCH /api/documents/{doc_id} endpoint accepting {filename, folder_id}" - "Add user-friendly error in stream_document_content: catch Exception broadly and surface a 502 'Cloud backend unreachable' rather than 500"
- truth: "Nextcloud credential modal should accept just the server URL and auto-construct the WebDAV endpoint; full path should be hidden under an expandable Advanced option" - truth: "Drag-and-drop upload box should be visible wherever the user expects to upload files"
status: failed status: failed
reason: "User reported: modal requires the full WebDAV path causing connection failure. Fix: auto-construct https://{server}/remote.php/dav/files/{username}/ for Nextcloud; add Advanced override for non-standard installs." reason: "User reported: the drag and drop box for upload disappeared."
severity: major severity: blocker
test: 9 test: 5-regression
root_cause: "Modal already auto-constructs the WebDAV URL from server+username and hides the full path behind an Advanced collapsible — this part was already built. The actual bug is in the edit pre-population watch: it extracts only the hostname from any stored server_url, so if the stored URL was a custom endpoint it is silently discarded and the Advanced field is never re-populated, losing the custom path on re-edit." root_cause: "DropZone IS present unconditionally in FileManagerView (line 37) and CloudFolderView (line 30). It is ABSENT from CloudStorageView (/cloud — the new overview page added in commit 5250895). The sidebar 'Cloud Storage' link was changed from /settings to /cloud in the same commit. User navigating via sidebar 'Cloud Storage' now lands on CloudStorageView which has no upload zone, explaining why the DropZone 'disappeared'."
artifacts: artifacts:
- path: "frontend/src/components/cloud/CloudCredentialModal.vue" - path: "frontend/src/views/CloudStorageView.vue"
issue: "watch handler (lines 195-208) always extracts only hostname match[1] and resets customEndpoint to ''; custom endpoint stored values are never restored on edit" issue: "No DropZone component — shows cloud connections list only"
- path: "frontend/src/components/layout/AppSidebar.vue"
issue: "Cloud Storage sidebar link changed to /cloud (commit 5250895) which routes to DropZone-less CloudStorageView"
missing: missing:
- "Detect on edit whether stored server_url matches the auto-constructed pattern; if not, set showAdvanced=true and populate customEndpoint with the full stored URL" - "Add DropZone + UploadProgress to CloudStorageView so users can upload without first navigating into a specific cloud folder"
- "OR add a note/CTA in CloudStorageView directing users to navigate into a folder to upload"
- truth: "User should be able to edit credentials of an existing connection without disconnecting first"
status: failed
reason: "User reported: no Edit button exists on connected provider rows; user must disconnect and re-enter all credentials to change any setting."
severity: major
test: 9
root_cause: "Edit button exists for ACTIVE status Nextcloud/WebDAV rows but is absent from the ERROR status template block. A connection in error state forces the user to remove and re-enter credentials instead of editing in-place."
artifacts:
- path: "frontend/src/components/settings/SettingsCloudTab.vue"
issue: "ERROR status template block (lines 89-96) contains only a Remove button; no Edit button, unlike the ACTIVE block"
missing:
- "Add Edit button to the ERROR status template block mirroring the ACTIVE block"
- truth: "Clicking Connect on Google Drive/OneDrive should redirect the browser to the provider's OAuth consent screen"
status: failed
reason: "User reported: window.location.href navigates to /api/cloud/oauth/initiate/{provider} without a JWT auth header; backend returns 401 Not authenticated. Fix: call /initiate via fetch() with Authorization header, receive OAuth URL in response, then redirect browser to that URL."
severity: major
test: 6
root_cause: "handleConnect() in SettingsCloudTab.vue uses window.location.href = '/api/cloud/oauth/initiate/{provider}' — bare browser navigation sends no Authorization header. The endpoint uses Depends(get_regular_user) which requires Bearer token → returns 401. Fix: change oauth_initiate to return JSON {url: ...} (status 200) instead of 302 redirect; frontend calls it via fetch() with Bearer header then sets window.location.href to the returned URL."
artifacts:
- path: "frontend/src/components/settings/SettingsCloudTab.vue"
issue: "handleConnect uses window.location.href instead of authenticated fetch"
- path: "backend/api/cloud.py"
issue: "oauth_initiate returns RedirectResponse(302); needs to return JSON {url} so fetch() can consume it"
missing:
- "Replace window.location.href with fetch() + Authorization header in handleConnect"
- "Change oauth_initiate to return JSONResponse({url: authorization_url}) instead of RedirectResponse"
- truth: "Disconnect confirmation text should render fully within the provider row without overflowing off screen"
status: failed
reason: "User reported: the text asking 'Do you really want to remove…' overflows off screen."
severity: minor
test: 9
root_cause: "Confirmation wrapper div lacks w-full and overflow-hidden; sits inside a flex row that allows children to grow beyond viewport. ConfirmBlock's <p> has no break-words constraint."
artifacts:
- path: "frontend/src/components/settings/SettingsCloudTab.vue"
issue: "Confirmation wrapper div (line ~102) missing w-full overflow-hidden; may also need to be rendered outside the flex items-center row as a full-width block below it"
- path: "frontend/src/components/ui/ConfirmBlock.vue"
issue: "<p> message element missing break-words / overflow-wrap constraint"
missing:
- "Add w-full overflow-hidden to confirmation wrapper in SettingsCloudTab.vue"
- "Add break-words to message <p> in ConfirmBlock.vue"