The Astrolabe PHP→Vue settings refactor dropped three stable element ids
(#mcp-enable-background-button, #mcp-revoke-background-button,
#mcp-revoke-background-form) that the multi-user-basic integration suite
drives the background-sync enable/disable/revoke flows through. Their
absence timed out the 5s Playwright locators and failed four tests:
- test_astrolabe_multi_user_background_sync::test_multi_user_astrolabe_background_sync_enablement
- test_astrolabe_multi_user_background_sync::test_revoke_background_sync_access
- test_astrolabe_chunk_context::test_chunk_context_endpoint_uses_app_password
- test_astrolabe_plotly_visualization::test_astrolabe_plotly_visualization_with_basic_auth
(the latter two enable background sync via complete_astrolabe_authorization
before exercising the app-password / indexed-search paths).
Two-part fix:
1. Bump the astrolabe submodule to v0.20.1 (cbcoutinho/astrolabe#116),
which restores the three element ids on the refactored NcButtons.
2. Defense-in-depth in the test helpers: resolve the enable/revoke buttons
by their stable id first, falling back to the button's accessible name
so a future id rename degrades to a slower-but-working lookup instead of
a hard timeout. Avoids a combined `.or_()` locator, which would
strict-mode-violate (the id button also matches by text).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The remaining SonarCloud S5443 (publicly-writable directory) findings were the
hard-coded /tmp screenshot paths in revoke_background_sync_access, which became
"new code" once the surrounding function was edited. Replace every /tmp literal
in the file with tempfile.gettempdir() (which S5443 accepts), eliminating the
findings consistently rather than per-line.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SonarCloud Automatic Analysis does not honour # NOSONAR, so the S6418
(hard-coded token) and S5443 (publicly writable /tmp) findings in the new
tests persisted. Fix them by construction instead:
- test_login_flow: use a trivial poll-token value ("tok") in the rewrite test
(it asserts the URLs, not the token) so it no longer looks like a secret.
- test_astrolabe bg-sync: build the debug screenshot path from
tempfile.gettempdir() rather than a hard-coded /tmp literal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The new-code quality gate flagged test-only mock fixtures as security issues
(new_security_rating E):
- S2068 "hard-coded password" ×2: drop the unused "app_password" value from the
get_app_password_with_scopes mocks (the code under test only reads truthiness
+ "scopes").
- S6418 "hard-coded token": NOSONAR on the Login Flow v2 poll-token test fixture.
- S5443 "publicly writable directory": NOSONAR on the /tmp debug screenshot path
(matches this file's existing convention).
No behaviour change; all are test fixtures, not real credentials.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The OAuth provisioning tools (check_provisioning_status, revoke_nextcloud_
access) only consulted the refresh-token store + Astrolabe status, ignoring the
app_passwords store that Login Flow v2 (nc_auth_provision_access) and the
management API write to — the same store require_provisioning / get_client use
to grant tool access. Result: status reported "not provisioned" while tools
worked, and revoke said "nothing to revoke" while the credential persisted.
- _get_provisioning_status: also check storage.get_app_password_with_scopes,
reporting is_provisioned with credential_type=app_password,
flow_type=login_flow_v2.
- _revoke_nextcloud_access: when the credential is an app password, delete it
from storage + invalidate the scope cache (no IdP token to revoke);
refresh-token revocation via the Token Broker is unchanged.
- tests/unit/test_oauth_tools_app_password_provisioning.py: cover status +
revoke for the app-password path.
- bump astrolabe submodule (deprovision MCP on disable); fix a stale assertion
in the migrated bg-sync test (one-click flow has no separate app-password
generation step).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Astrolabe was refactored to mint session-derived JWTs (TokenGenerationRequest
Event) and a one-click background-indexing opt-in, dropping the OAuth
authorize/callback/refresh surface. Bump the submodule and bring the test
suite in line:
- New test_astrolabe_session_jwt_search.py: a logged-in user searches via the
minted JWT with no provisioning (replaces the obsolete login_flow_provisioning
OAuth-authorize test; token_refresh test deleted — refresh flow is gone).
- settings_buttons: assert the new revoke endpoint + that oauth/disconnect is
gone (404).
- multi_user_background_sync / plotly / chunk_context: drop the OAuth authorize
step; provision via the one-click "Enable background indexing" button
(#mcp-enable-background-button -> #mcp-revoke-background-button) instead of
generating + pasting an app password.
- docker-compose.yml: mount the astrolabe submodule into the app container.
- third_party/astrolabe: bump to the one-click opt-in commit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidate three independent RefreshTokenStorage lazy singletons into a
single lock-protected get_shared_storage() function, eliminating race
conditions on concurrent first-access. Remove blanket try/except in
_get_stored_scopes so storage errors propagate as proper MCP errors
instead of silently triggering "please provision" messages. Handle
declined/cancelled elicitation results in Login Flow tools by cleaning up
sessions and returning clear status. Add update_app_password_scopes() to
avoid unnecessary decrypt/re-encrypt when only scopes change. Add
unprovisioned-user early exit and no-op detection to nc_auth_update_scopes.
Remove four dead config fields and misleading NEXTCLOUD_PASSWORD deprecation
warning. Add periodic login flow session cleanup task. Generate separate
Fernet keys per service. Add board cleanup in deck integration test. Gate
CI unit tests on linting and skip Astrolabe build for single-user profile.
Fix test markers from oauth to multi_user_basic for astrolabe integration
tests. Update login_flow.py docstrings to document outbound HTTP calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move httpx import to top-level and use anyio task group for concurrent
validation in cleanup_invalid_app_passwords (storage.py)
- Respect Retry-After header for 429 responses, capped at 300s (oauth_sync.py)
- Soften pre-validation exceptions so transient failures don't crash the
background sync task (oauth_sync.py)
- Replace f-string SQL with blanket DELETE and add returncode checks (conftest.py)
- Extract clear_stale_test_state() helper to deduplicate cleanup logic
in astrolabe background sync tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The revoke test failed because it only completed Step 2 (app password) but
not Step 1 (OAuth authorization). In hybrid mode, Astrolabe requires both
steps for $isFullyConfigured=true, which gates the "Revoke Access" button.
Changes:
- Use complete_astrolabe_authorization() in revoke test for full two-step flow
- Add stale state cleanup (app passwords, bruteforce entries, Astrolabe prefs)
to both enablement and revoke tests
- Add startup cleanup of invalid app passwords in BasicAuth mode
- Pre-validate credentials before entering scanner loop to fail fast
- Handle 401/403/429 in scanner with proper backoff and circuit breaking
- Clean up app passwords in test_users_setup fixture teardown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add cross-system interface test annotations to the 5 astrolabe test files,
clarifying they test the MCP server's integration with the Astrolabe
Nextcloud app (installed from the app store, source now in a separate repo).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace Close button click with Escape key in app password dialog
(h2 element was intercepting pointer events)
- Make test_users_setup fixture idempotent by checking user existence
before creation and only tracking created users for cleanup
- Fix search results detection by removing wait for .app-content-wrapper
CSS class that doesn't exist in Astrolabe's Vue app
- Add progress logging during results polling
- Increase polling timeout to 30 seconds for search results
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The "Revoke Access" button in Astrolabe personal settings was failing
with "Unable to connect to server" error in multi-user basic auth mode.
Root cause: The JavaScript sends a POST request but the route was
configured to accept DELETE. Changed the route to:
- Use POST method (matching the JavaScript fetch call)
- Use /api/v1/background-sync/credentials/revoke path (avoiding
conflict with storeAppPassword which uses POST on the base URL)
Added integration test that verifies the complete revoke flow:
enable background sync → click revoke → verify credentials deleted.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes the Playwright-based integration test that verifies multi-user app
password provisioning for background sync in Astrolabe.
**Root Cause:**
The test was failing to extract the generated app password from Nextcloud's
"New app password" dialog due to overly specific CSS selectors that didn't
match the actual DOM structure.
**Changes:**
- Enhanced network response logging to capture HTTP status codes
- Simplified app password extraction logic:
* Wait for dialog heading using text selector
* Iterate through ALL text inputs on page
* Find password by pattern: contains dashes and length > 20
* Validate extracted password against expected format
- Added format validation with regex before returning password
- Added detailed debug logging for each extraction step
- Improved error messages with screenshot paths
**Testing:**
Test now successfully completes for both alice and bob test users:
- Logs in to Nextcloud
- Generates app password in Security settings
- Extracts password from dialog
- Navigates to Astrolabe settings
- Enters and saves app password
- Verifies "Active" badge appears
- Confirms credentials stored in database
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>