Skip to content

Commit b3206c8

Browse files
committed
test(journal): cover individual limit and rename failure
1 parent 06a1bc9 commit b3206c8

1 file changed

Lines changed: 90 additions & 0 deletions

File tree

‎packages/session-journal/src/__tests__/session-event-log-window.test.ts‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,3 +500,93 @@ test('the active cap admits an exact UTF-8 line and refuses a larger line', asyn
500500
await flushSessionEventLogWrites(eventLogPath);
501501
assert.equal(fs.readFileSync(eventLogPath, 'utf8'), line);
502502
});
503+
504+
test('a single serialized line at 16 MiB persists and one byte more is refused', async () => {
505+
const dir = mkdtempForTestSync('event-single-byte-limit-');
506+
const eventLogPath = resolveSessionEventLogPath(dir);
507+
const logPath = path.join(dir, 'diagnostics.ndjson');
508+
const max = 16 * 1024 * 1024;
509+
const env = { AGENT_DEVICE_EVENT_LOG_MAX_BYTES: String(max * 2) };
510+
const makeEvent = (keySize: number) => ({
511+
kind: 'request.started' as const,
512+
ts: 'fixed',
513+
details: { ['x'.repeat(keySize)]: 1 },
514+
});
515+
const serialize = (event: ReturnType<typeof makeEvent>) =>
516+
`${JSON.stringify(Object.assign({ version: 1, ts: 'fixed', session: 'default' }, event))}\n`;
517+
const overhead = Buffer.byteLength(serialize(makeEvent(1))) - 1;
518+
const exact = makeEvent(max - overhead);
519+
const line = serialize(exact);
520+
assert.equal(Buffer.byteLength(line), max);
521+
await withDiagnosticsScope({ debug: true, logPath }, async () => {
522+
appendSessionEvent(eventLogPath, 'default', exact, env);
523+
await flushSessionEventLogWrites(eventLogPath);
524+
appendSessionEvent(eventLogPath, 'default', makeEvent(max - overhead + 1), env);
525+
await flushSessionEventLogWrites(eventLogPath);
526+
});
527+
assert.equal(fs.readFileSync(eventLogPath, 'utf8'), line);
528+
const diagnostics = fs
529+
.readFileSync(logPath, 'utf8')
530+
.trim()
531+
.split('\n')
532+
.map((entry) => JSON.parse(entry));
533+
assert.deepEqual(
534+
diagnostics
535+
.filter((event) => event.phase === 'session_event_log_write_failed')
536+
.map((event) => [event.data.reason, event.data.operation]),
537+
[['entry-too-large', 'admission']],
538+
);
539+
});
540+
541+
test('an active-file rename failure preserves bytes and a later event recovers the window', async () => {
542+
const dir = mkdtempForTestSync('event-active-rename-');
543+
const eventLogPath = resolveSessionEventLogPath(dir);
544+
const logPath = path.join(dir, 'diagnostics.ndjson');
545+
const env = { AGENT_DEVICE_EVENT_LOG_MAX_BYTES: '200' };
546+
const send = (summary: string) =>
547+
appendSessionEvent(
548+
eventLogPath,
549+
'default',
550+
{
551+
kind: 'request.started',
552+
ts: 'fixed',
553+
summary,
554+
},
555+
env,
556+
);
557+
send('first');
558+
send('second');
559+
await flushSessionEventLogWrites(eventLogPath);
560+
const before = fs.readFileSync(eventLogPath);
561+
const rename = fs.promises.rename.bind(fs.promises);
562+
const fault = vi.spyOn(fs.promises, 'rename').mockImplementation(async (source, destination) => {
563+
if (String(source) === eventLogPath)
564+
throw Object.assign(new Error('denied'), { code: 'EACCES' });
565+
return rename(source, destination);
566+
});
567+
await withDiagnosticsScope({ debug: true, logPath }, async () => {
568+
send('failed');
569+
await flushSessionEventLogWrites(eventLogPath);
570+
});
571+
assert.deepEqual(fs.readFileSync(eventLogPath), before);
572+
assert.ok(before.length <= 200);
573+
assert.equal(fs.existsSync(resolveRotatedSessionEventLogPath(eventLogPath)), false);
574+
const diagnostics = fs
575+
.readFileSync(logPath, 'utf8')
576+
.trim()
577+
.split('\n')
578+
.map((entry) => JSON.parse(entry));
579+
assert.deepEqual(
580+
diagnostics
581+
.filter((event) => event.phase === 'session_event_log_write_failed')
582+
.map((event) => [event.data.reason, event.data.operation, event.data.errno]),
583+
[['maintenance-failed', 'rotation', 'EACCES']],
584+
);
585+
fault.mockRestore();
586+
send('recovered');
587+
await flushSessionEventLogWrites(eventLogPath);
588+
assert.deepEqual(fs.readFileSync(resolveRotatedSessionEventLogPath(eventLogPath)), before);
589+
assert.ok(fs.statSync(eventLogPath).size <= 200);
590+
assert.deepEqual(summaries(eventLogPath, '0', 10), ['first', 'second', 'recovered']);
591+
assert.equal(readSessionEventLog(eventLogPath, { cursor: '2' }).endCursor, '3');
592+
});

0 commit comments

Comments
 (0)