// @ts-expect-error Node strip-types runtime requires explicit .ts extension in ESM imports. import { API_TOKEN_RUNTIME_GLOBAL_KEY, downloadDocumentContentMarkdown, downloadDocumentFile, getDocumentPreviewBlob, getDocumentThumbnailBlob, setApiTokenResolver, setRuntimeApiToken, updateDocumentMetadata } from './api.ts'; /** * Throws when a test condition is false. */ function assert(condition: boolean, message: string): void { if (!condition) { throw new Error(message); } } /** * Verifies that async functions reject with an expected message fragment. */ async function assertRejects(action: () => Promise, expectedMessage: string): Promise { try { await action(); } catch (error) { const message = error instanceof Error ? error.message : String(error); assert(message.includes(expectedMessage), `Expected error containing "${expectedMessage}" but received "${message}"`); return; } throw new Error(`Expected rejection containing "${expectedMessage}"`); } /** * Converts fetch inputs into a URL string for assertions. */ function toRequestUrl(input: RequestInfo | URL): string { if (typeof input === 'string') { return input; } if (input instanceof URL) { return input.toString(); } return input.url; } /** * Creates a minimal session storage implementation for Node-based tests. */ function createMemorySessionStorage(): Storage { const values = new Map(); return { get length(): number { return values.size; }, clear(): void { values.clear(); }, getItem(key: string): string | null { return values.has(key) ? values.get(key) ?? null : null; }, key(index: number): string | null { return Array.from(values.keys())[index] ?? null; }, removeItem(key: string): void { values.delete(key); }, setItem(key: string, value: string): void { values.set(key, String(value)); }, }; } /** * Runs API helper tests for authenticated media and download flows. */ async function runApiTests(): Promise { const originalFetch = globalThis.fetch; const runtimeGlobalSource = globalThis as typeof globalThis & Record; const originalRuntimeGlobalToken = runtimeGlobalSource[API_TOKEN_RUNTIME_GLOBAL_KEY]; const sessionStorageDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'sessionStorage'); try { Object.defineProperty(globalThis, 'sessionStorage', { configurable: true, writable: true, value: createMemorySessionStorage(), }); setApiTokenResolver(null); setRuntimeApiToken(null); delete runtimeGlobalSource[API_TOKEN_RUNTIME_GLOBAL_KEY]; const requestUrls: string[] = []; const requestAuthHeaders: Array = []; globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit): Promise => { requestUrls.push(toRequestUrl(input)); requestAuthHeaders.push(new Headers(init?.headers).get('Authorization')); return new Response('preview-bytes', { status: 200 }); }) as typeof fetch; const thumbnail = await getDocumentThumbnailBlob('doc-1'); const preview = await getDocumentPreviewBlob('doc-1'); assert(await thumbnail.text() === 'preview-bytes', 'Thumbnail blob bytes mismatch'); assert(await preview.text() === 'preview-bytes', 'Preview blob bytes mismatch'); assert( requestUrls[0] === '/api/v1/documents/doc-1/thumbnail', `Unexpected thumbnail URL ${requestUrls[0]}`, ); assert( requestUrls[1] === '/api/v1/documents/doc-1/preview', `Unexpected preview URL ${requestUrls[1]}`, ); assert(requestAuthHeaders[0] === null, `Expected no auth header for thumbnail request, got "${requestAuthHeaders[0]}"`); assert(requestAuthHeaders[1] === null, `Expected no auth header for preview request, got "${requestAuthHeaders[1]}"`); setRuntimeApiToken('session-user-token'); globalThis.fetch = (async (_input: RequestInfo | URL, init?: RequestInit): Promise => { const authHeader = new Headers(init?.headers).get('Authorization'); assert(authHeader === 'Bearer session-user-token', `Expected session token auth header, got "${authHeader}"`); return new Response('preview-bytes', { status: 200 }); }) as typeof fetch; await getDocumentPreviewBlob('doc-session-auth'); setRuntimeApiToken('session-user-token'); runtimeGlobalSource[API_TOKEN_RUNTIME_GLOBAL_KEY] = 'runtime-global-token'; globalThis.fetch = (async (_input: RequestInfo | URL, init?: RequestInit): Promise => { const authHeader = new Headers(init?.headers).get('Authorization'); assert(authHeader === 'Bearer runtime-global-token', `Expected global runtime token auth header, got "${authHeader}"`); return new Response('preview-bytes', { status: 200 }); }) as typeof fetch; await getDocumentPreviewBlob('doc-global-auth'); setApiTokenResolver(() => 'resolver-token'); let mergedContentType: string | null = null; let mergedAuthorization: string | null = null; globalThis.fetch = (async (_input: RequestInfo | URL, init?: RequestInit): Promise => { const headers = new Headers(init?.headers); mergedContentType = headers.get('Content-Type'); mergedAuthorization = headers.get('Authorization'); return new Response('{}', { status: 200 }); }) as typeof fetch; await updateDocumentMetadata('doc-headers', { original_filename: 'renamed.pdf' }); assert(mergedContentType === 'application/json', `Expected JSON content type to be preserved, got "${mergedContentType}"`); assert(mergedAuthorization === 'Bearer resolver-token', `Expected resolver token auth header, got "${mergedAuthorization}"`); setApiTokenResolver(() => ' '); globalThis.fetch = (async (_input: RequestInfo | URL, init?: RequestInit): Promise => { const authHeader = new Headers(init?.headers).get('Authorization'); assert(authHeader === 'Bearer runtime-global-token', `Expected fallback runtime global token auth header, got "${authHeader}"`); return new Response('preview-bytes', { status: 200 }); }) as typeof fetch; await getDocumentPreviewBlob('doc-resolver-fallback'); setApiTokenResolver(null); setRuntimeApiToken(null); delete runtimeGlobalSource[API_TOKEN_RUNTIME_GLOBAL_KEY]; globalThis.fetch = (async (): Promise => { return new Response('file-bytes', { status: 200, headers: { 'content-disposition': 'attachment; filename="invoice.pdf"', }, }); }) as typeof fetch; const fileResult = await downloadDocumentFile('doc-2'); assert(fileResult.filename === 'invoice.pdf', `Unexpected download filename ${fileResult.filename}`); assert((await fileResult.blob.text()) === 'file-bytes', 'Original download bytes mismatch'); globalThis.fetch = (async (): Promise => { return new Response('# markdown', { status: 200 }); }) as typeof fetch; const markdownResult = await downloadDocumentContentMarkdown('doc-3'); assert(markdownResult.filename === 'document-content.md', `Unexpected markdown filename ${markdownResult.filename}`); assert((await markdownResult.blob.text()) === '# markdown', 'Markdown bytes mismatch'); globalThis.fetch = (async (): Promise => { return new Response('forbidden', { status: 401 }); }) as typeof fetch; await assertRejects(async () => downloadDocumentContentMarkdown('doc-4'), 'Failed to download document markdown'); } finally { setApiTokenResolver(null); setRuntimeApiToken(null); if (originalRuntimeGlobalToken === undefined) { delete runtimeGlobalSource[API_TOKEN_RUNTIME_GLOBAL_KEY]; } else { runtimeGlobalSource[API_TOKEN_RUNTIME_GLOBAL_KEY] = originalRuntimeGlobalToken; } if (sessionStorageDescriptor) { Object.defineProperty(globalThis, 'sessionStorage', sessionStorageDescriptor); } else { delete (globalThis as { sessionStorage?: Storage }).sessionStorage; } globalThis.fetch = originalFetch; } } await runApiTests();