import { test } from 'node:test'; import assert from 'node:assert/strict'; import { ASCIILexicalTable, orderKeys } from './ASCIILexicalTable'; const a = 'a'.charCodeAt(0); const z = 'z'.charCodeAt(0); const makeLex = (maxWidth = 4) => new ASCIILexicalTable(a, z, maxWidth); const isStrictlyIncreasing = (keys: string[]): boolean => keys.every((key, i) => i === 0 || keys[i - 1] < key); test('orderKeys returns an empty array for empty input', () => { const lex = makeLex(); assert.deepEqual(orderKeys(lex, []), []); }); test('orderKeys preserves all existing keys when none are missing', () => { const lex = makeLex(); assert.deepEqual(orderKeys(lex, ['a', 'b', 'c']), ['a', 'b', 'c']); }); test('orderKeys keeps existing keys and fills a single interior gap', () => { const lex = makeLex(); const result = orderKeys(lex, ['b', undefined, 'd']); assert.deepEqual(result, ['b', 'c', 'd']); }); test('orderKeys output length always matches input length', () => { const lex = makeLex(); const inputs: Array> = [ [undefined], [undefined, undefined], [undefined, undefined, undefined], ['b', undefined, undefined, 'y'], ]; inputs.forEach((input) => { const result = orderKeys(lex, input); assert.ok(result, 'expected a defined result'); assert.equal(result?.length, input.length); }); }); test('orderKeys produces strictly increasing, valid keys', () => { const lex = makeLex(); const result = orderKeys(lex, [undefined, undefined, undefined, undefined]); assert.ok(result); assert.equal(result?.length, 4); assert.ok(isStrictlyIncreasing(result as string[])); assert.ok((result as string[]).every((key) => lex.has(key))); }); test('orderKeys keeps fixed keys at their positions when filling gaps', () => { const lex = makeLex(); const result = orderKeys(lex, ['b', undefined, undefined, 'y']) as string[]; assert.equal(result[0], 'b'); assert.equal(result[3], 'y'); assert.ok(isStrictlyIncreasing(result)); }); test('orderKeys is deterministic for the same input', () => { const lex = makeLex(); const first = orderKeys(lex, [undefined, undefined, undefined]); const second = orderKeys(lex, [undefined, undefined, undefined]); assert.deepEqual(first, second); }); test('orderKeys handles a leading gap before an existing key', () => { const lex = makeLex(); const result = orderKeys(lex, [undefined, 'm']) as string[]; assert.equal(result.length, 2); assert.equal(result[1], 'm'); assert.ok(result[0] < 'm'); }); test('orderKeys handles a trailing gap after an existing key', () => { const lex = makeLex(); const result = orderKeys(lex, ['m', undefined]) as string[]; assert.equal(result.length, 2); assert.equal(result[0], 'm'); assert.ok(result[1] > 'm'); }); test('orderKeys works with a tiny table', () => { const tiny = new ASCIILexicalTable('a'.charCodeAt(0), 'b'.charCodeAt(0), 2); const result = orderKeys(tiny, [undefined, undefined]) as string[]; assert.equal(result.length, 2); assert.ok(isStrictlyIncreasing(result)); assert.ok(result.every((key) => tiny.has(key))); });