import { describe, expect, test, vi } from 'vitest';

import { abortManagedCodexProcess } from '../../src/codex-process';
import { teardownWorkspace } from '../../src/workspace-teardown';

describe('workspace teardown', () => {
  test('event failure does not skip abort, stop, removal, or release', async () => {
    const calls: string[] = [];
    const record = (name: string) => vi.fn(() => calls.push(name));

    const result = await teardownWorkspace({
      stopHeartbeat: record('heartbeat'),
      removeActive: record('remove'),
      appendEvent: vi.fn().mockRejectedValue(new Error('event failed')),
      abortAgent: record('abort'),
      markStopped: vi.fn().mockRejectedValue(new Error('mutation failed')),
      closeAgent: record('close'),
      stopContainer: vi.fn().mockRejectedValue(new Error('container failed')),
      release: record('release'),
    });

    expect(calls).toEqual(['heartbeat', 'remove', 'abort', 'close', 'release']);
    expect(result.errors).toHaveLength(3);
  });

  test('removes active state before asynchronous cancellation work', async () => {
    const active = new Set(['job-1']);
    let activeDuringAbort = true;

    await teardownWorkspace({
      stopHeartbeat: vi.fn(),
      removeActive: () => active.delete('job-1'),
      abortAgent: () => {
        activeDuringAbort = active.has('job-1');
      },
      release: vi.fn(),
    });

    expect(activeDuringAbort).toBe(false);
    expect(active.has('job-1')).toBe(false);
  });

  test('Codex cancellation invokes isolated kill and leaves no active workspace', async () => {
    const active = new Set(['job-1']);
    const execute = vi.fn().mockResolvedValue({ exitCode: 0, output: '' });

    await teardownWorkspace({
      stopHeartbeat: vi.fn(),
      removeActive: () => active.delete('job-1'),
      abortAgent: async () =>
        await abortManagedCodexProcess({
          jobId: 'job-1',
          pidFile: '/home/alice/.codex/job-1.pid',
          execute,
        }),
      release: vi.fn(),
    });

    expect(execute).toHaveBeenCalledOnce();
    expect(active.has('job-1')).toBe(false);
  });
});
