Files
brain/tools/judge-watermark.test.mjs
T

53 lines
2.4 KiB
JavaScript

// tools/judge-watermark.test.mjs
import { describe, it, expect } from 'vitest';
import { readWatermark, bumpWatermark, checkNoRollback } from './judge-watermark.mjs';
function memChain() {
const store = new Map(); // account -> value
return { store,
get: ({ account }) => (store.has(account) ? store.get(account) : null),
set: ({ account, value }) => store.set(account, String(value)) };
}
const kc = (c) => ({ keychainGet: c.get, keychainSet: c.set });
describe('judge-watermark (K6 anti-rollback, per-session)', () => {
it('fresh session reads genesis 0 (no mark yet → not a rollback)', () => {
const c = memChain();
expect(readWatermark({ sessionId: 'S', ...kc(c) })).toBe(0);
});
it('bump only increases; lower bump is ignored (monotonic)', () => {
const c = memChain();
bumpWatermark({ sessionId: 'S', value: 5, ...kc(c) });
expect(readWatermark({ sessionId: 'S', ...kc(c) })).toBe(5);
bumpWatermark({ sessionId: 'S', value: 3, ...kc(c) }); // lower → ignored
expect(readWatermark({ sessionId: 'S', ...kc(c) })).toBe(5);
bumpWatermark({ sessionId: 'S', value: 9, ...kc(c) });
expect(readWatermark({ sessionId: 'S', ...kc(c) })).toBe(9);
});
it('checkNoRollback: current >= mark → ok', () => {
const c = memChain();
bumpWatermark({ sessionId: 'S', value: 7, ...kc(c) });
expect(checkNoRollback({ sessionId: 'S', current: 7, ...kc(c) })).toEqual({ ok: true, rolledBack: false, seen: 7 });
expect(checkNoRollback({ sessionId: 'S', current: 8, ...kc(c) }).ok).toBe(true);
});
it('checkNoRollback: current < mark → ROLLBACK detected (block)', () => {
const c = memChain();
bumpWatermark({ sessionId: 'S', value: 10, ...kc(c) });
const r = checkNoRollback({ sessionId: 'S', current: 4, ...kc(c) });
expect(r.ok).toBe(false);
expect(r.rolledBack).toBe(true);
expect(r.seen).toBe(10);
});
it('watermark is per-session (different sessionId independent)', () => {
const c = memChain();
bumpWatermark({ sessionId: 'A', value: 9, ...kc(c) });
expect(readWatermark({ sessionId: 'B', ...kc(c) })).toBe(0); // B fresh genesis
expect(checkNoRollback({ sessionId: 'B', current: 0, ...kc(c) }).ok).toBe(true);
});
it('corrupt/non-numeric stored value → treated as genesis 0 (fail-safe read)', () => {
const c = memChain();
c.store.set('wm:S', 'garbage');
expect(readWatermark({ sessionId: 'S', ...kc(c) })).toBe(0);
});
});