diff --git a/.planning/phases/08-stack-upgrade-backend-decomposition/08-03-SUMMARY.md b/.planning/phases/08-stack-upgrade-backend-decomposition/08-03-SUMMARY.md new file mode 100644 index 0000000..90c31ce --- /dev/null +++ b/.planning/phases/08-stack-upgrade-backend-decomposition/08-03-SUMMARY.md @@ -0,0 +1,170 @@ +--- +phase: 08-stack-upgrade-backend-decomposition +plan: "03" +subsystem: frontend-toast-store +tags: [frontend, toast, pinia, session-revocation, xfail-promotion, cr-01, cr-02, cr-03, wave-1] +dependency_graph: + requires: + - "08-01: xfail stubs for CR-01/CR-02/CR-03 (decorators to remove)" + provides: + - "frontend/src/stores/toast.js: locked show(message, type, duration) contract for Phase 10" + - "SettingsAccountTab.vue: uses toastStore.show() in changePassword + disableTotp" + - "TotpEnrollment.vue: uses toastStore.show() in confirmEnrollment" + - "CR-01/CR-02/CR-03 tests passing strictly (not xfail)" + affects: + - "frontend/src/components/settings/SettingsAccountTab.vue — inline toast removed" + - "frontend/src/components/auth/TotpEnrollment.vue — inline toast removed" + - "backend/tests/test_auth.py — xfail decorators removed from 3 tests" +tech_stack: + added: [] + patterns: + - "setup-store Pinia pattern: defineStore('toast', () => { ... return { show } })" + - "vi.mock('../../stores/toast.js', ...) pattern for isolating toast calls in component tests" +key_files: + created: + - path: "frontend/src/stores/toast.js" + description: "Phase 7.1 stub — show(message, type='success', duration=4000) no-op; Phase 10 implements rendering" + modified: + - path: "frontend/src/components/settings/SettingsAccountTab.vue" + description: "Removed sessionRevokedToast ref + setTimeout + inline HTML; wired to toastStore.show()" + - path: "frontend/src/components/auth/TotpEnrollment.vue" + description: "Removed sessionRevokedToast ref + setTimeout + inline HTML; wired to toastStore.show()" + - path: "frontend/src/components/settings/__tests__/SettingsAccountTab.test.js" + description: "Replaced DOM text assertions with mockShow spy assertions" + - path: "frontend/src/components/auth/__tests__/TotpEnrollment.test.js" + description: "Replaced DOM text assertions with mockShow spy assertions" + - path: "backend/tests/test_auth.py" + description: "Removed @pytest.mark.xfail from 3 tests; all now PASSED" +decisions: + - "toast.js uses positional parameters only per UI-SPEC.md — object-argument shape (show({message, type})) explicitly forbidden" + - "Frontend tests updated to mock useToastStore and assert on show() spy rather than DOM text — the stub is a no-op so DOM assertions would always fail" + - "The node_modules symlink created during testing was removed before commit — only the worktree src/ files are modified" +metrics: + duration: "~6m" + completed: "2026-06-08" + tasks_completed: 3 + tasks_total: 3 + files_created: 1 + files_modified: 5 +--- + +# Phase 8 Plan 03: useToastStore Stub + CR Session-Revocation Wire-Up Summary + +**One-liner:** Pinia toast stub with locked show(message, type, duration) contract wired to session-revocation call sites in SettingsAccountTab + TotpEnrollment, with CR-01/CR-02/CR-03 backend tests promoted from xfail to strictly passing. + +## What Was Built + +### Task 1: useToastStore Pinia stub (commit e417b71) + +Created `frontend/src/stores/toast.js` as a setup-store Pinia stub: + +```js +export const useToastStore = defineStore('toast', () => { + function show(message, type = 'success', duration = 4000) { + // No-op stub — Phase 10 implements rendering. + } + return { show } +}) +``` + +The signature matches UI-SPEC.md exactly. Phase 10 (UX-10) must implement rendering without modifying any call site. + +### Task 2: Component refactor (commit 3b8e2c1) + +Removed from both components: +- `const sessionRevokedToast = ref(false)` declaration +- Inline `
` toast HTML block +- `setTimeout(() => { sessionRevokedToast.value = false }, 5000)` auto-dismiss pattern + +Added to both components: +- `import { useToastStore } from '../../stores/toast.js'` +- `const toastStore = useToastStore()` +- `toastStore.show('Other sessions have been terminated.', 'success')` in each relevant handler + +Updated frontend tests to mock `useToastStore` via `vi.mock` and assert on the `show` spy instead of DOM text (the stub is a no-op, so DOM text assertions would always fail after migration). + +### Task 3: xfail promotion (commit 44ec28d) + +Removed `@pytest.mark.xfail(reason="Wave 0 stub — promoted to passing in 08-03", strict=False)` from all three tests: + +``` +tests/test_auth.py::test_change_password_revokes_other_sessions PASSED +tests/test_auth.py::test_enable_totp_revokes_other_sessions PASSED +tests/test_auth.py::test_disable_totp_revokes_other_sessions PASSED +``` + +## Final Test Output + +### Backend — three CR tests (pytest -v) + +``` +tests/test_auth.py::test_change_password_revokes_other_sessions PASSED [ 33%] +tests/test_auth.py::test_enable_totp_revokes_other_sessions PASSED [ 66%] +tests/test_auth.py::test_disable_totp_revokes_other_sessions PASSED [100%] + +======================== 3 passed, 10 warnings in 2.51s ======================== +``` + +All three show PASSED (not XPASS, not XFAIL, not SKIPPED). + +### Frontend — component tests + +``` +Test Files 15 passed (15 directly relevant) +Tests 134 passed (component tests all pass) +2 pre-existing failures in tests/api.spec.js (testAiConnection) — unrelated to this plan, pre-existed before Plan 08-03 +``` + +## Components Not Touched + +No other components were modified. The only files changed are: +- `frontend/src/stores/toast.js` (new) +- `frontend/src/components/settings/SettingsAccountTab.vue` +- `frontend/src/components/auth/TotpEnrollment.vue` +- `frontend/src/components/settings/__tests__/SettingsAccountTab.test.js` +- `frontend/src/components/auth/__tests__/TotpEnrollment.test.js` +- `backend/tests/test_auth.py` + +## Forward Reference: Phase 10 Toast Contract (Locked) + +The `show(message, type, duration)` signature defined in `frontend/src/stores/toast.js` is now locked. Phase 10 (UX-10) must: + +1. Implement rendering in the same store without modifying the method signature +2. NOT change any call site — the 3 call sites in SettingsAccountTab and TotpEnrollment must remain unchanged +3. Honor `type='success'` for the sessions-revoked notification +4. Apply the visual spec from UI-SPEC.md §"Sessions-Revoked Notification" (fixed `top-4 right-4 z-50`, `border-green-200`, `duration=4000`) + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Frontend component tests assert on DOM text that becomes absent after toast migration** + +- **Found during:** Task 2 — identified before writing code +- **Issue:** Existing `SettingsAccountTab.test.js` and `TotpEnrollment.test.js` tests used `expect(wrapper.text()).toContain('Other sessions have been terminated.')` — assertions relying on the inline DOM block that was removed by the migration. After migration, the toast store is a no-op, so the text never appears in the DOM. +- **Fix:** Updated both test files to mock `useToastStore` via `vi.mock` and assert on the mock's `show` spy: `expect(mockShow).toHaveBeenCalledWith('Other sessions have been terminated.', 'success')`. +- **Files modified:** `SettingsAccountTab.test.js`, `TotpEnrollment.test.js` +- **Commits:** 3b8e2c1 + +## Threat Surface Scan + +No new network endpoints, auth paths, file access patterns, or schema changes introduced. The toast store is an in-process Pinia store with no I/O. + +## Known Stubs + +The `useToastStore.show()` method is intentionally a stub in this phase. This is documented as a forward reference to Phase 10 (UX-10). The stub does not prevent the plan's goal (wiring the call contract) — it only defers rendering. + +## Self-Check: PASSED + +- [x] `frontend/src/stores/toast.js` exists +- [x] `grep -c "export const useToastStore"` = 1 +- [x] `grep -c "defineStore('toast'"` = 1 +- [x] `grep -c "function show(message, type = 'success', duration = 4000)"` = 1 +- [x] `grep -c "sessionRevokedToast" frontend/src/` = 0 (clean) +- [x] `grep -c "toastStore.show('Other sessions have been terminated.', 'success')" SettingsAccountTab.vue` = 2 +- [x] `grep -c "toastStore.show('Other sessions have been terminated.', 'success')" TotpEnrollment.vue` = 1 +- [x] Commit e417b71 exists (toast store) +- [x] Commit 3b8e2c1 exists (component refactor) +- [x] Commit 44ec28d exists (xfail promotion) +- [x] Three backend tests show PASSED (not XPASS) +- [x] No xfail decorators on the three promoted tests diff --git a/backend/tests/test_auth.py b/backend/tests/test_auth.py index 613a80a..7271aba 100644 --- a/backend/tests/test_auth.py +++ b/backend/tests/test_auth.py @@ -169,7 +169,6 @@ async def _try_refresh( # ── Tests ───────────────────────────────────────────────────────────────────── -@pytest.mark.xfail(reason="Wave 0 stub — promoted to passing in 08-03", strict=False) @pytest.mark.asyncio async def test_change_password_revokes_other_sessions(revoke_client, db_session): """CR-01: change_password revokes other sessions; current session preserved. @@ -214,7 +213,6 @@ async def test_change_password_revokes_other_sessions(revoke_client, db_session) assert status_b == 401, f"Session B refresh should be revoked (401), got {status_b}" -@pytest.mark.xfail(reason="Wave 0 stub — promoted to passing in 08-03", strict=False) @pytest.mark.asyncio async def test_enable_totp_revokes_other_sessions(revoke_client, db_session): """CR-02: enable_totp revokes other sessions; current session preserved. @@ -268,7 +266,6 @@ async def test_enable_totp_revokes_other_sessions(revoke_client, db_session): assert status_b == 401, f"Session B refresh should be revoked (401), got {status_b}" -@pytest.mark.xfail(reason="Wave 0 stub — promoted to passing in 08-03", strict=False) @pytest.mark.asyncio async def test_disable_totp_revokes_other_sessions(revoke_client, db_session): """CR-03: disable_totp revokes other sessions; current session preserved. diff --git a/frontend/src/components/auth/TotpEnrollment.vue b/frontend/src/components/auth/TotpEnrollment.vue index 3220885..004e2c2 100644 --- a/frontend/src/components/auth/TotpEnrollment.vue +++ b/frontend/src/components/auth/TotpEnrollment.vue @@ -1,27 +1,6 @@