import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtempSync, rmSync, writeFileSync, readdirSync, existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { FileSystemMcpCompletionBridge } from '../../../../../../modules/claude-invocation/interface-adapters/gateways/mcpCompletion.fileSystem.gateway.js';
describe('FileSystemMcpCompletionBridge (cross-process bridge for SPEC-169 B3)', () => {
    let directory;
    let timers;
    let nextTimerId;
    function fakeSetInterval(handler) {
        nextTimerId += 1;
        const id = nextTimerId;
        timers.set(id, handler);
        return id;
    }
    function fakeClearInterval(timerId) {
        timers.delete(timerId);
    }
    function tick() {
        for (const handler of [...timers.values()]) {
            handler();
        }
    }
    beforeEach(() => {
        directory = mkdtempSync(join(tmpdir(), 'completion-bridge-'));
        timers = new Map();
        nextTimerId = 0;
    });
    afterEach(() => {
        rmSync(directory, { recursive: true, force: true });
    });
    function makeBridge() {
        return new FileSystemMcpCompletionBridge({
            directory,
            pollIntervalMs: 10,
            setIntervalImpl: fakeSetInterval,
            clearIntervalImpl: fakeClearInterval,
        });
    }
    it('publishes a completion event to the directory as an atomic JSON file', () => {
        const bridge = makeBridge();
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.publish('job-1', completion);
        const files = readdirSync(directory).filter(name => name.endsWith('.json'));
        expect(files).toEqual(['job-1.json']);
    });
    it('delivers a buffered event immediately on subscribe (file already on disk)', () => {
        const bridge = makeBridge();
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.publish('job-2', completion);
        const received = [];
        bridge.subscribe('job-2', value => {
            received.push(value);
        });
        expect(received).toEqual([completion]);
    });
    it('delivers a completion when the publisher writes after the subscriber polls', () => {
        const bridge = makeBridge();
        const received = [];
        bridge.subscribe('job-3', value => {
            received.push(value);
        });
        expect(received).toEqual([]);
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.publish('job-3', completion);
        tick();
        expect(received).toEqual([completion]);
    });
    it('cleans up the on-disk file once a listener consumes the event', () => {
        const bridge = makeBridge();
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.subscribe('job-4', () => { });
        bridge.publish('job-4', completion);
        tick();
        expect(existsSync(join(directory, 'job-4.json'))).toBe(false);
    });
    it('reports a synthetic failure when the event file is malformed JSON', () => {
        const bridge = makeBridge();
        writeFileSync(join(directory, 'job-5.json'), '{not valid json');
        const received = [];
        bridge.subscribe('job-5', value => {
            received.push(value);
        });
        tick();
        expect(received).toHaveLength(1);
        expect(received[0]?.source).toBe('mcp');
        expect(received[0]?.outcome).toBe('failed');
        expect(received[0]?.reason).toBe('completion-bridge-malformed');
    });
    it('removes pending file on unsubscribe so a future subscribe does not pick a stale event', () => {
        const bridge = makeBridge();
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.publish('job-6', completion);
        bridge.unsubscribe('job-6');
        expect(existsSync(join(directory, 'job-6.json'))).toBe(false);
    });
    it('replaces an existing subscription if subscribe is called twice for the same jobId', () => {
        const bridge = makeBridge();
        const received = [];
        bridge.subscribe('job-7', () => {
            received.push({ source: 'mcp', outcome: 'failed', reason: 'first-listener' });
        });
        bridge.subscribe('job-7', value => {
            received.push(value);
        });
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.publish('job-7', completion);
        tick();
        expect(received).toEqual([completion]);
    });
    it('sanitises hostile job ids so subscribe cannot escape the directory', () => {
        const bridge = makeBridge();
        const completion = { source: 'mcp', outcome: 'completed', reason: null };
        bridge.publish('../escape', completion);
        const files = readdirSync(directory);
        expect(files).toEqual(['.._escape.json']);
    });
});
//# sourceMappingURL=mcpCompletion.fileSystem.gateway.test.js.map