import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { newFixture, type Fixture } from '../helpers/fixture.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); }); afterEach(async () => { await fixture.cleanup(); }); async function setup() { const api = await import('../../src/lib/server/hosts/index.js'); const { db } = await import('../../src/lib/server/db/index.js'); const host = api.createHost({ name: 'Log host' }); const operation = api.queueHostOperation(host.id, 'logs.read', { path: 'logs/games_sp.log' }); return { ...api, host, operation, db }; } describe('transient log results', () => { it.each([ { lines: ['private log body'], size: 17, mtime: '2026-10-08T12:00:00Z', truncated: false }, { contentBase64: Buffer.from('private log body').toString('base64'), size: 17, reset: false, cursor: { offset: 17 } } ])('delivers full replies to current readers while storing only metadata', async (result) => { const api = await setup(); const first = api.waitHostOperationStatus(api.operation.id); const second = api.waitHostOperation(api.operation.id); const receipt = api.reportHostOperationStatus(api.host.id, api.operation.id, { state: 'completed', result }); expect((await first).result).toEqual(result); expect((await second).result).toEqual(result); expect(receipt.result).toMatchObject({ retained: false, size: 17 }); expect(api.getHostOperation(api.operation.id)?.result).toEqual(receipt.result); const stored = api .db() .prepare('SELECT result FROM ec_host_operations WHERE id=?') .get(api.operation.id); expect(JSON.stringify(stored)).not.toMatch(/private log body|contentBase64|cursor|"lines"/); expect( api .db() .prepare( "SELECT * FROM file_blob_references WHERE owner_type='operation-result' AND owner_id=?" ) .all(api.operation.id) ).toEqual([]); // Completed reads cannot serve a stale body, including duplicate receipts. await expect(api.waitHostOperationStatus(api.operation.id)).rejects.toThrow( 'no longer available' ); api.reportHostOperationStatus(api.host.id, api.operation.id, { state: 'completed', result }); await expect(api.waitHostOperationStatus(api.operation.id)).rejects.toThrow( 'no longer available' ); }); it('preserves missing-log results and operation failures', async () => { const api = await setup(); const missing = api.waitHostOperationStatus(api.operation.id); api.reportHostOperationStatus(api.host.id, api.operation.id, { state: 'completed', result: null }); expect((await missing).result).toBeNull(); const failed = api.queueHostOperation(api.host.id, 'logs.read', {}); const failure = api.waitHostOperationStatus(failed.id); api.reportHostOperationStatus(api.host.id, failed.id, { state: 'failed', error: 'Read failed' }); expect(await failure).toMatchObject({ state: 'failed', error: 'Read failed' }); }); it('discards bodies arriving after the reader has timed out', async () => { const api = await setup(); await expect(api.waitHostOperationStatus(api.operation.id, 0)).rejects.toThrow(); api.reportHostOperationStatus(api.host.id, api.operation.id, { state: 'completed', result: { lines: ['late body'] } }); expect(api.getHostOperation(api.operation.id)?.result).toEqual({ retained: false, lineCount: 1 }); await expect(api.waitHostOperationStatus(api.operation.id)).rejects.toThrow( 'no longer available' ); }); });