import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import test from 'node:test' import ts from 'typescript' async function session(t, fetch_) { const previous = globalThis.fetch globalThis.fetch = fetch_ t.after(() => { globalThis.fetch = previous }) const source = await readFile(new URL('../src/services/session.ts', import.meta.url), 'utf8') const { outputText } = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext } }) return import(`data:text/javascript;base64,${Buffer.from(outputText).toString('base64')}#${t.name}`) } test('JSON login and binary export share Cookie/CSRF transport without parsing JSONL as JSON', async t => { const content = '{"record_type":"manifest"}\n{"query":"合成测试"}\n' const calls = [] const api = await session(t, async (path, init) => { calls.push([path, init]) if (path === '/api/auth/login') return Response.json({ csrf_token: 'fixture-csrf' }) return new Response(content, { headers: { 'Content-Disposition': 'attachment; filename="fixture.jsonl"', 'X-Records-SHA256': 'a'.repeat(64) } }) }) await api.apiRequest('/api/auth/login', { method: 'POST', body: '{}' }) const file = await api.apiDownload('/api/retrieval/exports', { method: 'POST', body: '{"run_ids":[]}' }, true) assert.equal(await file.blob.text(), content) assert.equal(file.filename, 'fixture.jsonl') assert.equal(file.recordsSha256, 'a'.repeat(64)) assert.equal(calls[1][1].credentials, 'include') assert.equal(calls[1][1].headers.get('X-CSRF-Token'), 'fixture-csrf') assert.equal(calls[1][1].headers.get('Content-Type'), 'application/json') assert.equal(calls[1][1].headers.get('Accept'), 'application/x-ndjson') assert.equal(calls[0][1].headers.get('Accept'), 'application/json') }) test('download refreshes missing session token and preserves server authorization errors', async t => { const calls = [] const api = await session(t, async (path, init) => { calls.push([path, init]) if (path === '/api/auth/me') return Response.json({ csrf_token: 'refreshed-csrf' }) return Response.json({ detail: '需要管理员权限' }, { status: 403 }) }) await assert.rejects(api.apiDownload('/api/retrieval/exports', { method: 'POST', body: '{}' }, true), error => { assert.ok(error instanceof api.ApiError) assert.equal(error.status, 403) assert.equal(error.message, '需要管理员权限') return true }) assert.equal(calls.length, 2) assert.equal(calls[0][0], '/api/auth/me') assert.equal(calls[1][1].headers.get('X-CSRF-Token'), 'refreshed-csrf') }) test('401 download clears stale token and unsafe filename is not propagated', async t => { let current = 'old-csrf' let failed = false const api = await session(t, async (path, init) => { if (path === '/api/auth/login' || path === '/api/auth/me') return Response.json({ csrf_token: current }) assert.equal(init.headers.get('X-CSRF-Token'), current) if (!failed) { failed = true; current = 'new-csrf' return Response.json({ detail: '请先登录' }, { status: 401 }) } return new Response('fixture', { headers: { 'Content-Disposition': 'attachment; filename="../../fixture.jsonl"' } }) }) await api.apiRequest('/api/auth/login', { method: 'POST', body: '{}' }) await assert.rejects(api.apiDownload('/api/retrieval/exports', { method: 'POST' }, true), error => error.status === 401) const file = await api.apiDownload('/api/retrieval/exports', { method: 'POST' }, true) assert.equal(file.filename, 'download.jsonl') assert.equal(file.recordsSha256, null) })