Files
Codewalkers/apps/server/agent/lifecycle/signal-manager.test.ts
Lukas May 34578d39c6 refactor: Restructure monorepo to apps/server/ and apps/web/ layout
Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt
standard monorepo conventions (apps/ for runnable apps, packages/
for reusable libraries). Update all config files, shared package
imports, test fixtures, and documentation to reflect new paths.

Key fixes:
- Update workspace config to ["apps/*", "packages/*"]
- Update tsconfig.json rootDir/include for apps/server/
- Add apps/web/** to vitest exclude list
- Update drizzle.config.ts schema path
- Fix ensure-schema.ts migration path detection (3 levels up in dev,
  2 levels up in dist)
- Fix tests/integration/cli-server.test.ts import paths
- Update packages/shared imports to apps/server/ paths
- Update all docs/ files with new paths
2026-03-03 11:22:53 +01:00

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));
}, 100);
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);
});
});
});