From 309bc44d4a6556656bc68ec638b78c3458ba0752 Mon Sep 17 00:00:00 2001 From: User Date: Sat, 22 Aug 2026 19:37:37 +0200 Subject: [PATCH] docs: test logout visibility through real app --- ...2026-08-22-local-only-logout-visibility.md | 95 ++++++++++--------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md b/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md index bb85108e..9da09738 100644 --- a/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md +++ b/docs/superpowers/plans/2026-08-22-local-only-logout-visibility.md @@ -17,6 +17,8 @@ - Preserve the existing `POST /auth/logout` route, logout coordinator, authentication-state cleanup, query cleanup, and session/transcript isolation behavior. - Do not change backend, Compose, native `tht`, deployment, or packaging files. - Do not add dependencies or change the English UI string `Log out`. +- Visibility integration tests must render the real `App` and real `AppShell`; do not assert on + props or elements exposed only by the mocked `AppShell` in `AuthGate.test.tsx`. - Use strict TDD: add the focused regression tests, observe the intended RED, implement the minimum change, then run focused and full GREEN gates. - Preserve and do not stage the pre-existing changes in `.superpowers/sdd/progress.md`, `brain/index.md`, and `brain/codebase/psd-dwh-transport.md`. @@ -25,7 +27,7 @@ ### Task 1: Propagate the local-auth capability and conditionally render logout **Files:** -- Modify: `frontend/src/auth/AuthGate.test.tsx:10-17,63-68` +- Modify: `frontend/src/App.test.tsx:1-15` - Modify: `frontend/src/shell/AppShell.auth.test.tsx:11-28,45-89,90-153` - Modify: `frontend/src/auth/AuthGate.tsx:20-29,79-81` - Modify: `frontend/src/shell/AppShell.tsx:39-40,715-722` @@ -51,65 +53,68 @@ export function AppShell({ canLogout }: AppShellProps): JSX.Element; - [ ] **Step 1: Add RED tests for capability propagation and shell visibility** -In `frontend/src/auth/AuthGate.test.tsx`, replace the `AppShell` mock with a capability-observing -mock: +Replace `frontend/src/App.test.tsx` with a real `App` integration test that supplies all shell +requests and varies only the public authentication mode: ```tsx -vi.mock("../shell/AppShell", () => ({ - AppShell: ({ canLogout }: { canLogout: boolean }) => ( -
- Authenticated shell - {canLogout && ( - - )} -
- ), -})); -``` +import { render, screen } from "@testing-library/react"; +import { http, HttpResponse } from "msw"; +import { server } from "./test/msw"; +import { App } from "./App"; -Extend the existing local authenticated-shell test so it proves the positive contract: - -```tsx -test("renders the authenticated shell from the safe /me DTO", async () => { - render(); - - const shell = await screen.findByTestId("authenticated-shell"); - expect(shell).toHaveAttribute("data-can-logout", "true"); - expect(screen.getByRole("button", { name: "Log out" })).toBeInTheDocument(); - expect(screen.queryByRole("heading", { name: /sign in/i })).not.toBeInTheDocument(); -}); -``` - -Add an upstream regression beside it: - -```tsx -test("disables shell logout for trusted upstream authentication", async () => { +function registerAuthenticatedShell(mode: "local" | "upstream") { server.use( http.get("/api/auth/config", () => HttpResponse.json({ - mode: "upstream", localLogin: false, oidcLogin: false, + mode, + localLogin: mode === "local", + oidcLogin: false, })), http.get("/api/me", () => HttpResponse.json({ - issuer: "portal", + issuer: mode === "local" ? "local" : "portal", subject: "portal-user", displayName: "Portal user", roles: ["user"], permissions: ["session.use"], isAdmin: false, - csrfToken: null, + csrfToken: mode === "local" ? "c".repeat(43) : null, session: null, })), + http.get("/api/settings", () => HttpResponse.json({ + workspace: "psd", provider: "zai", model: "glm-5.2", thinking: "medium", + })), + http.get("/api/workspaces", () => HttpResponse.json([])), + http.get("/api/models", () => HttpResponse.json({ models: [] })), + http.get("/api/sessions", () => HttpResponse.json([])), + http.get("/api/workspace-registry/status", () => HttpResponse.json({ + branch: "main", ahead: 0, behind: 0, degraded: false, + })), ); +} - render(); +test("local authentication renders the standalone logout control", async () => { + registerAuthenticatedShell("local"); - const shell = await screen.findByTestId("authenticated-shell"); - expect(shell).toHaveAttribute("data-can-logout", "false"); + render(); + + expect(await screen.findByRole("button", { name: "New session" })).toBeInTheDocument(); + expect(screen.getByText("Portal user")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Log out" })).toBeInTheDocument(); +}); + +test("trusted upstream authentication keeps identity but omits ThothII logout", async () => { + registerAuthenticatedShell("upstream"); + + render(); + + expect(await screen.findByText("Portal user")).toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Log out" })).not.toBeInTheDocument(); }); ``` +Keep the existing `AppShell` mock in `frontend/src/auth/AuthGate.test.tsx` unchanged. That suite +continues to test gate state transitions; the new `App.test.tsx` cases own the real propagation +and rendered-visibility contract. + In `frontend/src/shell/AppShell.auth.test.tsx`, add a `canLogout` argument to the existing helper and make the keyed/direct local-auth renders explicit: @@ -169,11 +174,11 @@ test("hides logout outside local authentication while preserving the identity", Run from `frontend/`: ```bash -npx vitest run src/auth/AuthGate.test.tsx src/shell/AppShell.auth.test.tsx +npx vitest run src/App.test.tsx src/shell/AppShell.auth.test.tsx ``` -Expected: FAIL. The local `AuthGate` test receives no `canLogout` prop, and the real `AppShell` -still renders `Log out` when the new negative test passes `false`. +Expected: FAIL. The real `AppShell` still renders `Log out` in both the upstream integration and +the new negative shell test. - [ ] **Step 3: Implement the minimal explicit capability flow** @@ -272,7 +277,7 @@ Do not alter `signOut()`, `logoutUser()`, `frontend/src/api/auth.ts`, or any bac Run from `frontend/`: ```bash -npx vitest run src/auth/AuthGate.test.tsx src/shell/AppShell.auth.test.tsx +npx vitest run src/App.test.tsx src/shell/AppShell.auth.test.tsx ``` Expected: both test files PASS, including the new local-positive and upstream-negative cases. @@ -307,7 +312,7 @@ Run from the repository root: ```bash git diff --check git diff -- frontend/src/auth/AuthGate.tsx \ - frontend/src/auth/AuthGate.test.tsx \ + frontend/src/App.test.tsx \ frontend/src/shell/AppShell.tsx \ frontend/src/shell/AppShell.auth.test.tsx \ frontend/src/shell/AppShell.new-session.test.tsx \ @@ -324,7 +329,7 @@ Commit only the implementation files: ```bash git add frontend/src/auth/AuthGate.tsx \ - frontend/src/auth/AuthGate.test.tsx \ + frontend/src/App.test.tsx \ frontend/src/shell/AppShell.tsx \ frontend/src/shell/AppShell.auth.test.tsx \ frontend/src/shell/AppShell.new-session.test.tsx \