import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { createEditorHydrationGate, decideHydrationApply, } from './editorHydrationGate'; describe('decideHydrationApply', () => { const token = { gen: 2, saveEpoch: 0, docId: 'doc_a' }; it('applies only when gen, doc, save epoch match and editor is closed', () => { assert.deepEqual( decideHydrationApply({ token, currentGen: 2, currentSaveEpoch: 0, activeDocId: 'doc_a', editorOpen: false, }), { apply: true, reason: 'ok' }, ); }); it('rejects a response for another document', () => { assert.deepEqual( decideHydrationApply({ token, currentGen: 2, currentSaveEpoch: 0, activeDocId: 'doc_b', editorOpen: false, }), { apply: false, reason: 'doc_mismatch' }, ); }); it('rejects a superseded generation', () => { assert.deepEqual( decideHydrationApply({ token, currentGen: 3, currentSaveEpoch: 0, activeDocId: 'doc_a', editorOpen: false, }), { apply: false, reason: 'stale_generation' }, ); }); }); describe('createEditorHydrationGate', () => { it('does not start a new hydrate while the editor is open (UI-06 fill-window GET)', () => { const gate = createEditorHydrationGate(); gate.setActiveDocId('doc_proj6140_intake'); const before = gate.beginHydration('doc_proj6140_intake'); assert.ok(before); gate.setEditorOpen(true); assert.equal(gate.beginHydration('doc_proj6140_intake'), null); assert.equal(gate.decide(before).reason, 'editor_open'); assert.equal(gate.decide(before).apply, false); }); it('deferred hydrate that resolves after the editor opens does not replace the session', async () => { const gate = createEditorHydrationGate(); gate.setActiveDocId('doc_proj6140_intake'); const token = gate.beginHydration('doc_proj6140_intake'); assert.ok(token); let resolveHydrate!: (ast: { blockId: string }) => void; const pending = new Promise<{ blockId: string }>((resolve) => { resolveHydrate = resolve; }); gate.setEditorOpen(true); resolveHydrate({ blockId: 'blk_rebuilt_during_fill' }); const rebuilt = await pending; const decision = gate.decide(token); assert.equal(rebuilt.blockId, 'blk_rebuilt_during_fill'); assert.equal(decision.apply, false); assert.equal(decision.reason, 'editor_open'); }); it('covers hydrate resolving between fill and save', async () => { const gate = createEditorHydrationGate(); gate.setActiveDocId('doc_proj6140_intake'); const token = gate.beginHydration('doc_proj6140_intake'); assert.ok(token); gate.setEditorOpen(true); let resolveDuringFill!: (value: 'hydrated') => void; const hydrateDuringFill = new Promise<'hydrated'>((resolve) => { resolveDuringFill = resolve; }); resolveDuringFill('hydrated'); await hydrateDuringFill; assert.equal(gate.decide(token).reason, 'editor_open'); gate.markSaved(); gate.setEditorOpen(false); assert.deepEqual(gate.decide(token), { apply: false, reason: 'superseded_by_save' }); }); it('asks for a hydrate resume after editor_open blocked the first apply', () => { const gate = createEditorHydrationGate(); gate.setActiveDocId('doc_a'); const token = gate.beginHydration('doc_a'); assert.ok(token); assert.equal(gate.takeHydrationResume(), false); gate.setEditorOpen(true); assert.equal(gate.decide(token).reason, 'editor_open'); gate.setEditorOpen(false); assert.equal(gate.takeHydrationResume(), true); assert.equal(gate.takeHydrationResume(), false); const retry = gate.beginHydration('doc_a'); assert.ok(retry); assert.deepEqual(gate.decide(retry), { apply: true, reason: 'ok' }); }); it('asks for a hydrate resume when beginHydration is skipped while editing', () => { const gate = createEditorHydrationGate(); gate.setActiveDocId('doc_a'); gate.setEditorOpen(true); assert.equal(gate.beginHydration('doc_a'), null); gate.setEditorOpen(false); assert.equal(gate.takeHydrationResume(), true); }); it('late hydrate after a successful PATCH does not roll back the new baseline', () => { const gate = createEditorHydrationGate(); gate.setActiveDocId('doc_a'); const stale = gate.beginHydration('doc_a'); assert.ok(stale); gate.setEditorOpen(true); gate.markSaved(); gate.setEditorOpen(false); assert.deepEqual(gate.decide(stale), { apply: false, reason: 'superseded_by_save' }); const fresh = gate.beginHydration('doc_a'); assert.ok(fresh); assert.deepEqual(gate.decide(fresh), { apply: true, reason: 'ok' }); }); });