test: give the 10k-iteration sort-key property test its own timeout #318
@ -26,48 +26,56 @@ describe('sort-key helpers (issue #45)', () => {
|
|||||||
* pattern — repeatedly drop the last page between the first two — must never
|
* pattern — repeatedly drop the last page between the first two — must never
|
||||||
* collide and never overflow the key length, because the caller rebalances
|
* collide and never overflow the key length, because the caller rebalances
|
||||||
* when {@link nextKeyOrRebalance} returns null.
|
* when {@link nextKeyOrRebalance} returns null.
|
||||||
|
*
|
||||||
|
* Under parallel CI load the 10.000 iterations have repeatedly exceeded the
|
||||||
|
* default 5 s per-test timeout (runs 685, 699 — same code passed on rerun),
|
||||||
|
* so this test carries its own budget.
|
||||||
*/
|
*/
|
||||||
it('10.000 adversarial reorders never collide or overflow (rebalance verified)', () => {
|
it(
|
||||||
// Start with five pages in a fixed order.
|
'10.000 adversarial reorders never collide or overflow (rebalance verified)',
|
||||||
let order = evenlySpacedKeys(5).map((key, i) => ({ id: `p${i}`, key }));
|
{ timeout: 30_000 },
|
||||||
let rebalances = 0;
|
() => {
|
||||||
|
// Start with five pages in a fixed order.
|
||||||
|
let order = evenlySpacedKeys(5).map((key, i) => ({ id: `p${i}`, key }));
|
||||||
|
let rebalances = 0;
|
||||||
|
|
||||||
const rebalance = (): void => {
|
const rebalance = (): void => {
|
||||||
const keys = evenlySpacedKeys(order.length);
|
const keys = evenlySpacedKeys(order.length);
|
||||||
order = order.map((page, i) => ({ ...page, key: keys[i]! }));
|
order = order.map((page, i) => ({ ...page, key: keys[i]! }));
|
||||||
rebalances += 1;
|
rebalances += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let i = 0; i < 10_000; i += 1) {
|
for (let i = 0; i < 10_000; i += 1) {
|
||||||
// Move the last page to sit between the first and second — the tightest
|
// Move the last page to sit between the first and second — the tightest
|
||||||
// possible gap, which is what grows key length fastest.
|
// possible gap, which is what grows key length fastest.
|
||||||
const moved = order[order.length - 1]!;
|
const moved = order[order.length - 1]!;
|
||||||
const rest = order.slice(0, -1);
|
const rest = order.slice(0, -1);
|
||||||
const afterKey = rest[0]!.key;
|
const afterKey = rest[0]!.key;
|
||||||
const beforeKey = rest[1]!.key;
|
const beforeKey = rest[1]!.key;
|
||||||
|
|
||||||
const key = nextKeyOrRebalance(afterKey, beforeKey);
|
const key = nextKeyOrRebalance(afterKey, beforeKey);
|
||||||
if (key === null) {
|
if (key === null) {
|
||||||
// Rebalance keeps the CURRENT order, then retry the move once.
|
// Rebalance keeps the CURRENT order, then retry the move once.
|
||||||
rebalance();
|
rebalance();
|
||||||
const k2 = nextKeyOrRebalance(order[0]!.key, order[1]!.key);
|
const k2 = nextKeyOrRebalance(order[0]!.key, order[1]!.key);
|
||||||
expect(k2).not.toBeNull();
|
expect(k2).not.toBeNull();
|
||||||
order = [order[0]!, { ...moved, key: k2! }, ...order.slice(1)];
|
order = [order[0]!, { ...moved, key: k2! }, ...order.slice(1)];
|
||||||
} else {
|
} else {
|
||||||
order = [rest[0]!, { ...moved, key }, ...rest.slice(1)];
|
order = [rest[0]!, { ...moved, key }, ...rest.slice(1)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invariants after every move: keys unique, bounded, and consistent with
|
||||||
|
// the intended array order.
|
||||||
|
const keys = order.map((p) => p.key);
|
||||||
|
expect(new Set(keys).size).toBe(keys.length);
|
||||||
|
expect(Math.max(...keys.map((k) => k.length))).toBeLessThanOrEqual(MAX_SORT_KEY_LENGTH);
|
||||||
|
for (let j = 1; j < keys.length; j += 1) {
|
||||||
|
expect(keys[j - 1]! < keys[j]!).toBe(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invariants after every move: keys unique, bounded, and consistent with
|
// The adversarial pattern must have forced at least one rebalance.
|
||||||
// the intended array order.
|
expect(rebalances).toBeGreaterThan(0);
|
||||||
const keys = order.map((p) => p.key);
|
},
|
||||||
expect(new Set(keys).size).toBe(keys.length);
|
);
|
||||||
expect(Math.max(...keys.map((k) => k.length))).toBeLessThanOrEqual(MAX_SORT_KEY_LENGTH);
|
|
||||||
for (let j = 1; j < keys.length; j += 1) {
|
|
||||||
expect(keys[j - 1]! < keys[j]!).toBe(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The adversarial pattern must have forced at least one rebalance.
|
|
||||||
expect(rebalances).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user