docs(05): add UAT, UI-SPEC, deferred items, debug notes; refine plans 09-11
Plan refinements: Vitest tests added to 09/10 must-haves, explicit mock_flow two-tuple pattern in 10, test_admin_api.py fixture usage in 11. New artifacts: UAT checklist, UI-SPEC, deferred-items, debug investigation for cloud-doc-operations-fail. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
---
|
||||
status: diagnosed
|
||||
phase: 05-cloud-storage-backends
|
||||
source:
|
||||
- 05-01-SUMMARY.md
|
||||
- 05-02-SUMMARY.md
|
||||
- 05-03-SUMMARY.md
|
||||
- 05-04-SUMMARY.md
|
||||
- 05-05-SUMMARY.md
|
||||
- 05-06-SUMMARY.md
|
||||
- 05-07-SUMMARY.md
|
||||
- 05-08-SUMMARY.md
|
||||
started: 2026-05-29T00:00:00Z
|
||||
updated: 2026-05-30T00:00:00Z
|
||||
---
|
||||
|
||||
## Current Test
|
||||
<!-- OVERWRITE each test - shows where we are -->
|
||||
|
||||
## Current Test
|
||||
|
||||
[testing complete]
|
||||
|
||||
## Tests
|
||||
|
||||
### 1. Settings Cloud Storage Tab — 3-tab layout
|
||||
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.
|
||||
result: pass
|
||||
|
||||
### 2. All 4 providers visible in Cloud Storage tab
|
||||
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).
|
||||
result: pass
|
||||
|
||||
### 3. WebDAV / Nextcloud credential modal opens
|
||||
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.
|
||||
result: pass
|
||||
|
||||
### 4. Cloud Storage sidebar section — collapsible
|
||||
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.
|
||||
result: pass
|
||||
|
||||
### 5. Cloud Storage sidebar empty state
|
||||
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.
|
||||
result: pass
|
||||
|
||||
### 6. OAuth initiate — Google Drive redirect
|
||||
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
|
||||
reported: "Clicking Connect redirects browser to http://localhost:5173/api/cloud/oauth/initiate/google_drive and returns {\"detail\":\"Not authenticated\"}"
|
||||
severity: major
|
||||
|
||||
### 7. OAuth initiate — OneDrive redirect
|
||||
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.
|
||||
result: skipped
|
||||
reason: No server-side OAuth credentials configured; same bug as test 6 expected
|
||||
|
||||
### 8. OAuth callback — success toast and tab routing
|
||||
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.
|
||||
result: skipped
|
||||
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
|
||||
|
||||
### 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
|
||||
|
||||
total: 14
|
||||
passed: 7
|
||||
issues: 6
|
||||
skipped: 3
|
||||
blocked: 0
|
||||
pending: 0
|
||||
|
||||
## 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"
|
||||
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."
|
||||
severity: major
|
||||
test: 14
|
||||
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."
|
||||
artifacts:
|
||||
- path: "frontend/src/api/client.js"
|
||||
issue: "Missing adminDeleteUser(id, adminPassword) function"
|
||||
- path: "frontend/src/components/admin/AdminUsersTab.vue"
|
||||
issue: "No Delete button or admin-password confirmation flow"
|
||||
- path: "backend/api/admin.py"
|
||||
issue: "DELETE endpoint takes no body; needs UserDeleteConfirm model to verify admin password before proceeding"
|
||||
missing:
|
||||
- "adminDeleteUser(id, adminPassword) in client.js calling DELETE /api/admin/users/{id}"
|
||||
- "UserDeleteConfirm Pydantic model + password verification in delete_user handler"
|
||||
- "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"
|
||||
status: failed
|
||||
reason: "User reported: I neither can open nor re-analyze nor edit any file stored on a cloud backend."
|
||||
severity: major
|
||||
test: 13
|
||||
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."
|
||||
artifacts:
|
||||
- path: "frontend/src/components/documents/DocumentPreviewModal.vue"
|
||||
issue: "Uses unauthenticated iframe :src for auth-required /content endpoint"
|
||||
- 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"
|
||||
issue: "Hardcodes get_storage_backend() (MinIO) instead of routing to cloud backend based on doc.storage_backend"
|
||||
- path: "backend/api/documents.py"
|
||||
issue: "No PATCH /{doc_id} endpoint for document metadata editing"
|
||||
missing:
|
||||
- "Authenticated content fetch: either signed query-string token on /content endpoint, or frontend fetches bytes with Bearer header and creates Blob URL"
|
||||
- "Cloud-aware re-analyze: detect doc.storage_backend != 'minio' and load CloudConnection in Celery task to fetch file bytes"
|
||||
- "PATCH /api/documents/{doc_id} endpoint accepting {filename, folder_id}"
|
||||
|
||||
- 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"
|
||||
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."
|
||||
severity: major
|
||||
test: 9
|
||||
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."
|
||||
artifacts:
|
||||
- path: "frontend/src/components/cloud/CloudCredentialModal.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"
|
||||
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"
|
||||
|
||||
- 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"
|
||||
Reference in New Issue
Block a user