Files
agent-desk-ng/web/lib/api/session-isolation.test.mjs
mono 2f7a491666 feat: harden support, localize knowledge and add operator availability [skip ci]
Source-only publication of reviewed commit 64e96a7f423673dc29ad56485be9fc79bce8af1c.

Passed in isolation: focused frontend/backend tests, race checks, TypeScript typecheck, lint, Go compilation, Flowgram build, and deterministic SDK build/tests.

Not accepted as a production release: Next.js export/native assembly remains blocked by environment memory limits; browser interaction and MySQL staging acceptance remain unverified. No production server, database/schema, runtime configuration, deployment, release tag, or workflow execution was performed.

Expected source tree: 0a4a7528e9.
2026-10-16 15:21:42 +00:00

116 lines
4.4 KiB
JavaScript

import assert from "node:assert/strict"
import test from "node:test"
import { isSameSession } from "../session-request-guard.ts"
import { deferred, loadTypeScriptModule } from "../test-support/load-typescript-module.mjs"
function fixture() {
let session = { user: { id: 1 }, accessToken: "test-session-a" }
let expirations = 0
const response = deferred()
const client = loadTypeScriptModule(new URL("./client.ts", import.meta.url), {
"@/lib/auth": {
readSession: () => session,
expireSession: () => { session = null; expirations += 1 },
},
"@/lib/session-request-guard": { isSameSession },
"@/i18n/messages": { translateCurrentMessage: (key) => key },
}, { process: { env: {} }, Headers, FormData, fetch: () => response.promise })
return {
client, response,
get session() { return session },
set session(next) { session = next },
get expirations() { return expirations },
}
}
function expiredResponse() {
return new Response(JSON.stringify({ success: false, errorCode: 3002, message: "Expired" }), {
status: 401, headers: { "Content-Type": "application/json" },
})
}
for (const method of ["request", "requestBlob"]) {
test(`${method}: old auth failure cannot expire a newer login`, async () => {
const f = fixture()
const pending = f.client[method]("/test")
f.session = { user: { id: 2 }, accessToken: "test-session-b" }
f.response.resolve(expiredResponse())
await assert.rejects(pending, { errorCode: 3002 })
assert.equal(f.session.user.id, 2)
assert.equal(f.expirations, 0)
})
test(`${method}: matching auth failure expires only its own session`, async () => {
const f = fixture()
const pending = f.client[method]("/test")
f.response.resolve(expiredResponse())
await assert.rejects(pending, { errorCode: 3002 })
assert.equal(f.session, null)
assert.equal(f.expirations, 1)
})
test(`${method}: unauthenticated failures do not expire a dashboard session`, async () => {
const f = fixture()
const pending = f.client[method]("/test", { skipAuth: true })
f.response.resolve(expiredResponse())
await assert.rejects(pending, { errorCode: 3002 })
assert.equal(f.expirations, 0)
})
}
test("old logout and profile update cannot overwrite a new account", async () => {
for (const action of ["logout", "updateProfile"]) {
let session = { user: { id: 1 }, accessToken: "test-session-a" }
const response = deferred()
let writes = 0
const auth = loadTypeScriptModule(new URL("./auth.ts", import.meta.url), {
"@/lib/auth": {
readSession: () => session,
clearSession: () => { session = null; writes += 1 },
writeSession: (next) => { session = next; writes += 1 },
},
"@/lib/session-request-guard": { isSameSession },
"@/lib/api/client": { request: () => response.promise },
})
const pending = auth[action]({ nickname: "Updated" })
session = { user: { id: 2 }, accessToken: "test-session-b" }
response.resolve({ user: { id: 1 }, accessToken: "test-session-a" })
await pending
assert.equal(session.user.id, 2)
assert.equal(writes, 0)
}
})
for (const newerLogin of [true, false]) {
test(`logout navigation honors current session (newer login: ${newerLogin})`, async () => {
let session = null
const pendingLogout = deferred()
const redirects = []
const auth = loadTypeScriptModule(new URL("../../components/auth-provider.tsx", import.meta.url), {
react: {
createContext: () => ({ Provider: "context" }),
startTransition: (callback) => callback(),
useCallback: (callback) => callback,
useEffect: () => {},
},
"react/jsx-runtime": { jsx: (type, props) => ({ type, props }) },
"next/navigation": {
usePathname: () => "/dashboard",
useRouter: () => ({ replace: (url) => redirects.push(url) }),
},
"@/components/session-provider": {
SessionProvider: "session",
useSession: () => ({ session, ready: true, signOut: () => pendingLogout.promise }),
},
"@/lib/auth": { readSession: () => session },
})
const dashboardElement = auth.AuthProvider({ children: null }).props.children
const context = dashboardElement.type(dashboardElement.props)
const signOut = context.props.value.signOut()
if (newerLogin) session = { user: { id: 2 }, accessToken: "test-session-b" }
pendingLogout.resolve()
await signOut
assert.equal(redirects.length, newerLogin ? 0 : 1)
})
}