Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
63 lines
2.8 KiB
TypeScript
63 lines
2.8 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readVaultNote } from './vaultNoteRead';
|
|
|
|
const note = { id: 'note-1', title: 'Runbook', content: '', createdAt: 1, updatedAt: 2 };
|
|
|
|
function read(...args: Parameters<typeof readVaultNote>) {
|
|
const result = readVaultNote(...args);
|
|
assert.ok('note' in result, JSON.stringify(result));
|
|
return result;
|
|
}
|
|
|
|
test('bounded pages reconstruct long Unicode notes without gaps or broken characters', () => {
|
|
const content = `${'a'.repeat(5999)}\u{1F408}中文\n`.repeat(20);
|
|
let offset: number | null = 0;
|
|
let collected = '';
|
|
while (offset !== null) {
|
|
const result = read({ ...note, content }, { offset, maxChars: 1_000_000, expectedUpdatedAt: 2 });
|
|
assert.ok(result.note);
|
|
assert.equal(result.offset, offset);
|
|
assert.ok(result.note.content.length <= 6000);
|
|
assert.doesNotMatch(result.note.content, /^[\uDC00-\uDFFF]|[\uD800-\uDBFF]$/);
|
|
collected += result.note.content;
|
|
offset = result.nextOffset;
|
|
}
|
|
assert.equal(collected, content);
|
|
});
|
|
|
|
test('small/empty notes and EOF are complete, and oversized metadata stays out', () => {
|
|
for (const content of ['', 'small note']) {
|
|
const result = read({ ...note, content, title: 'x'.repeat(100_000), tags: ['x'.repeat(100_000)] }, {});
|
|
assert.ok(result.note);
|
|
assert.equal(result.note.content, content);
|
|
assert.equal(result.nextOffset, null);
|
|
assert.ok(JSON.stringify(result).length < 1000);
|
|
}
|
|
assert.equal(read({ ...note, content: 'abc' }, { offset: 3 }).note?.content, '');
|
|
});
|
|
|
|
test('literal search locates later and overlapping hits without skipping nearby matches', () => {
|
|
const source = { ...note, content: `${'x'.repeat(9000)}ababa\u{1F408}tail` };
|
|
const first = read(source, { query: 'aba', maxChars: 4 });
|
|
assert.equal(first.matchOffset, 9000);
|
|
assert.equal(first.note?.content, 'abab');
|
|
const second = read(source, { query: 'aba', offset: first.nextOffset, expectedUpdatedAt: 2 });
|
|
assert.equal(second.matchOffset, 9002);
|
|
const missing = read(source, { query: 'aba', offset: second.nextOffset });
|
|
assert.equal(missing.matchOffset, null);
|
|
assert.equal(missing.nextOffset, null);
|
|
assert.equal(missing.note?.content, '');
|
|
assert.equal(read(source, { query: 'ABA' }).matchOffset, null);
|
|
assert.equal(read(source, { query: '.*' }).matchOffset, null);
|
|
});
|
|
|
|
test('invalid ranges, queries and changed notes fail explicitly', () => {
|
|
const source = { ...note, content: 'a\u{1F408}b' };
|
|
for (const params of [
|
|
{ offset: -1 }, { offset: 0.5 }, { offset: 9 }, { offset: 2 }, { offset: '1' },
|
|
{ maxChars: 0 }, { maxChars: NaN }, { maxChars: Infinity },
|
|
{ query: '' }, { query: 'a'.repeat(201) }, { query: 1 }, { expectedUpdatedAt: 1 },
|
|
]) assert.equal(readVaultNote(source, params).ok, false, JSON.stringify(params));
|
|
});
|