import { ProcessEventBuffer, RuntimeProcessEvent } from '../src/modules/netaclaw/runtime/process_events';

function event(input: Partial<RuntimeProcessEvent> = {}): RuntimeProcessEvent {
  return {
    version: 1,
    operationId: 'tool-call-1',
    targetType: 'tool',
    source: 'tool',
    status: 'running',
    level: 'info',
    message: 'working',
    timestamp: '2026-05-06T00:00:00.000Z',
    ...input,
  };
}

describe('ProcessEventBuffer', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('coalesces repeated non-terminal events until throttle flush', () => {
    const emitted: RuntimeProcessEvent[] = [];
    const buffer = new ProcessEventBuffer(item => emitted.push(item), { throttleMs: 100 });

    buffer.push(event({ current: 1, total: 3 }));
    buffer.push(event({ current: 2, total: 3 }));

    expect(emitted).toEqual([]);
    jest.advanceTimersByTime(100);

    expect(emitted).toEqual([
      expect.objectContaining({
        current: 2,
        payload: { repeatCount: 2 },
      }),
    ]);
  });

  it('flushes pending operation events before terminal events', () => {
    const emitted: RuntimeProcessEvent[] = [];
    const buffer = new ProcessEventBuffer(item => emitted.push(item), { throttleMs: 100 });

    buffer.push(event({ current: 1, total: 2 }));
    buffer.push(event({ status: 'failed', level: 'error', message: 'failed' }));

    expect(emitted).toEqual([
      expect.objectContaining({ current: 1 }),
      expect.objectContaining({ status: 'failed', message: 'failed' }),
    ]);
  });
});
