197 lines
6.8 KiB
JavaScript
197 lines
6.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { contextApi } from '../../resources/js/lib/investor-context.ts';
|
|
|
|
const success = { ok: true, json: async () => ({ saved: true }) };
|
|
await test('context handles compact CSRF cookies and rejects malformed writes before transport', async () => {
|
|
let calls = 0;
|
|
await withTransport(
|
|
async (_url, options) => {
|
|
calls++;
|
|
if (options.method !== 'GET')
|
|
assert.equal(
|
|
options.headers['X-XSRF-TOKEN'],
|
|
'synthetic/token',
|
|
);
|
|
return success;
|
|
},
|
|
async () => {
|
|
globalThis.document.cookie = 'other=x;XSRF-TOKEN=synthetic%2Ftoken';
|
|
await contextApi('/synthetic', {});
|
|
globalThis.document.cookie = 'XSRF-TOKEN=%broken';
|
|
await assert.rejects(
|
|
contextApi('/synthetic', {}, 'POST', true),
|
|
/sessão/,
|
|
);
|
|
assert.equal(calls, 1);
|
|
await contextApi('/synthetic');
|
|
assert.equal(calls, 2);
|
|
},
|
|
);
|
|
});
|
|
async function withTransport(fetch, callback) {
|
|
const previousFetch = globalThis.fetch;
|
|
const previousDocument = globalThis.document;
|
|
globalThis.fetch = fetch;
|
|
globalThis.document = { cookie: 'XSRF-TOKEN=synthetic%20token' };
|
|
try {
|
|
await callback();
|
|
} finally {
|
|
globalThis.fetch = previousFetch;
|
|
globalThis.document = previousDocument;
|
|
}
|
|
}
|
|
void test('context requests preserve Unicode payload and authentication while using byte-based keepalive eligibility', async () => {
|
|
const requests = [];
|
|
await withTransport(
|
|
async (_url, options) => {
|
|
requests.push(options);
|
|
return success;
|
|
},
|
|
async () => {
|
|
for (const text of [
|
|
'Preparação de investimento',
|
|
'ç'.repeat(33000),
|
|
'a'.repeat(65000),
|
|
]) {
|
|
const body = { declarations: { project_summary: text } };
|
|
assert.deepEqual(
|
|
await contextApi('/synthetic', body, 'POST', true),
|
|
{ saved: true },
|
|
);
|
|
const request = requests.at(-1);
|
|
assert.equal(request.body, JSON.stringify(body));
|
|
assert.equal(
|
|
request.keepalive,
|
|
new TextEncoder().encode(request.body).byteLength < 60000,
|
|
);
|
|
assert.equal(request.credentials, 'same-origin');
|
|
assert.equal(
|
|
request.headers['X-XSRF-TOKEN'],
|
|
'synthetic token',
|
|
);
|
|
}
|
|
assert.equal(requests[1].body.length < 60000, true);
|
|
assert.equal(requests[1].keepalive, false);
|
|
},
|
|
);
|
|
});
|
|
void test('concurrent context requests share the keepalive budget and release it after completion', async () => {
|
|
const requests = [];
|
|
let finish;
|
|
await withTransport(
|
|
(_url, options) => {
|
|
requests.push(options);
|
|
return requests.length === 1
|
|
? new Promise((resolve) => {
|
|
finish = resolve;
|
|
})
|
|
: Promise.resolve(success);
|
|
},
|
|
async () => {
|
|
const body = { text: 'a'.repeat(40000) };
|
|
const first = contextApi('/first', body, 'POST', true);
|
|
await contextApi('/second', body, 'POST', true);
|
|
assert.deepEqual(
|
|
requests.map((request) => request.keepalive),
|
|
[true, false],
|
|
);
|
|
finish(success);
|
|
await first;
|
|
await contextApi('/third', body, 'POST', true);
|
|
assert.equal(requests[2].keepalive, true);
|
|
},
|
|
);
|
|
});
|
|
void test('failed context transport releases the reserved budget without retrying a write', async () => {
|
|
const requests = [];
|
|
await withTransport(
|
|
async (_url, options) => {
|
|
requests.push(options);
|
|
if (requests.length === 1)
|
|
throw new Error('Synthetic transport error');
|
|
return success;
|
|
},
|
|
async () => {
|
|
const body = { text: 'a'.repeat(40000) };
|
|
await assert.rejects(
|
|
contextApi('/first', body, 'POST', true),
|
|
/Synthetic transport error/,
|
|
);
|
|
await contextApi('/second', body, 'POST', true);
|
|
assert.equal(requests.length, 2);
|
|
assert.equal(
|
|
requests.every((request) => request.keepalive),
|
|
true,
|
|
);
|
|
},
|
|
);
|
|
});
|
|
void test('ordinary context writes and reads retain their existing method and error behavior', async () => {
|
|
const requests = [];
|
|
await withTransport(
|
|
async (_url, options) => {
|
|
requests.push(options);
|
|
return requests.length === 3
|
|
? {
|
|
ok: false,
|
|
status: 409,
|
|
json: async () => ({
|
|
message: 'Synthetic version conflict',
|
|
}),
|
|
}
|
|
: success;
|
|
},
|
|
async () => {
|
|
await contextApi('/read');
|
|
await contextApi('/write', { value: 'ç' });
|
|
await assert.rejects(
|
|
contextApi('/conflict', { value: 'kept' }),
|
|
/Synthetic version conflict/,
|
|
);
|
|
assert.equal(requests[0].method, 'GET');
|
|
assert.equal(requests[0].body, undefined);
|
|
assert.equal(requests[0].headers['X-XSRF-TOKEN'], undefined);
|
|
assert.equal(requests[1].method, 'POST');
|
|
assert.equal(
|
|
requests.every((request) => request.keepalive === false),
|
|
true,
|
|
);
|
|
},
|
|
);
|
|
});
|
|
|
|
void test('invalid successful context response remains a failure and releases keepalive budget', async () => {
|
|
let calls = 0;
|
|
await withTransport(
|
|
async (_url, options) => {
|
|
calls++;
|
|
assert.equal(options.keepalive, true);
|
|
return calls === 1
|
|
? new Response('<html>unexpected</html>', { status: 200 })
|
|
: success;
|
|
},
|
|
async () => {
|
|
await assert.rejects(
|
|
contextApi(
|
|
'/synthetic',
|
|
{ text: 'a'.repeat(40000) },
|
|
'POST',
|
|
true,
|
|
),
|
|
/HTTP 200/,
|
|
);
|
|
assert.equal(calls, 1);
|
|
assert.deepEqual(
|
|
await contextApi(
|
|
'/synthetic',
|
|
{ text: 'a'.repeat(40000) },
|
|
'POST',
|
|
true,
|
|
),
|
|
{ saved: true },
|
|
);
|
|
},
|
|
);
|
|
});
|