- branch-manager: beforeEach→beforeAll (all 12 tests are read-only) - worktree manager: clone template repo per test instead of full init - signal-manager: reduce setTimeout delay from 100ms to 30ms
180 lines
6.5 KiB
TypeScript
180 lines
6.5 KiB
TypeScript
/**
|
|
* SignalManager Tests — Verify atomic signal.json operations.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { mkdtemp, rm, writeFile, mkdir } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { FileSystemSignalManager } from './signal-manager.js';
|
|
|
|
describe('FileSystemSignalManager', () => {
|
|
let tempDir: string;
|
|
let agentWorkdir: string;
|
|
let signalManager: FileSystemSignalManager;
|
|
|
|
beforeEach(async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), 'signal-manager-test-'));
|
|
agentWorkdir = join(tempDir, 'agent-workdir');
|
|
await mkdir(join(agentWorkdir, '.cw', 'output'), { recursive: true });
|
|
signalManager = new FileSystemSignalManager();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rm(tempDir, { recursive: true, force: true });
|
|
});
|
|
|
|
describe('clearSignal', () => {
|
|
it('should remove existing signal.json file', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, JSON.stringify({ status: 'done' }));
|
|
|
|
await signalManager.clearSignal(agentWorkdir);
|
|
|
|
const exists = await signalManager.checkSignalExists(agentWorkdir);
|
|
expect(exists).toBe(false);
|
|
});
|
|
|
|
it('should not throw if signal.json does not exist', async () => {
|
|
await expect(signalManager.clearSignal(agentWorkdir)).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('checkSignalExists', () => {
|
|
it('should return true when signal.json exists', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, JSON.stringify({ status: 'done' }));
|
|
|
|
const exists = await signalManager.checkSignalExists(agentWorkdir);
|
|
expect(exists).toBe(true);
|
|
});
|
|
|
|
it('should return false when signal.json does not exist', async () => {
|
|
const exists = await signalManager.checkSignalExists(agentWorkdir);
|
|
expect(exists).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('readSignal', () => {
|
|
it('should read valid done signal', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
const expectedSignal = { status: 'done' };
|
|
await writeFile(signalPath, JSON.stringify(expectedSignal));
|
|
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toEqual(expectedSignal);
|
|
});
|
|
|
|
it('should read valid questions signal', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
const expectedSignal = {
|
|
status: 'questions',
|
|
questions: [{ id: '1', question: 'What to do?' }]
|
|
};
|
|
await writeFile(signalPath, JSON.stringify(expectedSignal));
|
|
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toEqual(expectedSignal);
|
|
});
|
|
|
|
it('should read valid error signal', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
const expectedSignal = { status: 'error', error: 'Something went wrong' };
|
|
await writeFile(signalPath, JSON.stringify(expectedSignal));
|
|
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toEqual(expectedSignal);
|
|
});
|
|
|
|
it('should return null for invalid JSON', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, '{ invalid json');
|
|
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toBeNull();
|
|
});
|
|
|
|
it('should return null for invalid status', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, JSON.stringify({ status: 'invalid' }));
|
|
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toBeNull();
|
|
});
|
|
|
|
it('should return null for empty file', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, '');
|
|
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toBeNull();
|
|
});
|
|
|
|
it('should return null when file does not exist', async () => {
|
|
const signal = await signalManager.readSignal(agentWorkdir);
|
|
expect(signal).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('waitForSignal', () => {
|
|
it('should return signal when file already exists', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
const expectedSignal = { status: 'done' };
|
|
await writeFile(signalPath, JSON.stringify(expectedSignal));
|
|
|
|
const signal = await signalManager.waitForSignal(agentWorkdir, 1000);
|
|
expect(signal).toEqual(expectedSignal);
|
|
});
|
|
|
|
it('should wait for signal to appear', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
const expectedSignal = { status: 'done' };
|
|
|
|
// Write signal after a delay
|
|
setTimeout(async () => {
|
|
await writeFile(signalPath, JSON.stringify(expectedSignal));
|
|
}, 30);
|
|
|
|
const signal = await signalManager.waitForSignal(agentWorkdir, 1000);
|
|
expect(signal).toEqual(expectedSignal);
|
|
});
|
|
|
|
it('should timeout if signal never appears', async () => {
|
|
const signal = await signalManager.waitForSignal(agentWorkdir, 100);
|
|
expect(signal).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('validateSignalFile', () => {
|
|
it('should return true for complete valid JSON file', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, JSON.stringify({ status: 'done' }));
|
|
|
|
const isValid = await signalManager.validateSignalFile(signalPath);
|
|
expect(isValid).toBe(true);
|
|
});
|
|
|
|
it('should return false for incomplete JSON', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, '{ "status": "don');
|
|
|
|
const isValid = await signalManager.validateSignalFile(signalPath);
|
|
expect(isValid).toBe(false);
|
|
});
|
|
|
|
it('should return false for empty file', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
await writeFile(signalPath, '');
|
|
|
|
const isValid = await signalManager.validateSignalFile(signalPath);
|
|
expect(isValid).toBe(false);
|
|
});
|
|
|
|
it('should return false when file does not exist', async () => {
|
|
const signalPath = join(agentWorkdir, '.cw/output/signal.json');
|
|
|
|
const isValid = await signalManager.validateSignalFile(signalPath);
|
|
expect(isValid).toBe(false);
|
|
});
|
|
});
|
|
}); |