Files
proffesor-for-testing__agen…/tests/unit/workers/daemon.test.ts
Profa 2253d05618 feat: flatten project structure — archive v2, promote v3 to root
Remove the nested v3/ subdirectory by moving all active source code,
tests, configs, and assets directly to the project root. Archive v2/
(dead code, 847MB) to v2-archive.tar.gz.

Key changes:
- Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/
- Move v3/tsconfig.json, v3/vitest.config.ts to root
- Merge v3/package.json into root package.json (ESM, scripts, deps, exports)
- Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/
- Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes
- Fix all init installer path resolution (3-level-up → 2-level-up)
- Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe"
- Update settings.json ADR/DDD directories to docs/implementation/
- Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks)
- Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces
- Update 15 guidance shards: v3/src/ → src/ path references
- Fix sync interfaces: remove v3/ and v2/ path references
- Fix test path calculations (process.cwd() + '..' no longer needed)
- Fix flaky perf test threshold (500ms → 1000ms for CI environments)
- Fix complexity-analyzer test to match ADR-051 keyword-based eligibility

Build passes, 17,901 tests pass, CLI and MCP bundles verified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:35:41 +00:00

292 lines
9.0 KiB
TypeScript

/**
* Agentic QE v3 - Daemon Unit Tests
* ADR-014: Background Workers for QE Monitoring
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import {
QEDaemon,
createDaemon,
getDaemon,
resetDaemon,
} from '../../../src/workers/daemon';
describe('QEDaemon', () => {
let daemon: QEDaemon;
beforeEach(() => {
vi.useFakeTimers();
resetDaemon();
});
afterEach(async () => {
if (daemon?.running) {
await daemon.stop();
}
vi.useRealTimers();
resetDaemon();
});
describe('createDaemon', () => {
it('should create a daemon with default config', () => {
daemon = createDaemon();
expect(daemon).toBeDefined();
expect(daemon.running).toBe(false);
});
it('should create a daemon with custom config', () => {
daemon = createDaemon({
autoStart: false,
logLevel: 'debug',
});
expect(daemon).toBeDefined();
});
});
describe('getDaemon', () => {
it('should return singleton instance', () => {
const daemon1 = getDaemon();
const daemon2 = getDaemon();
expect(daemon1).toBe(daemon2);
});
it('should create new instance after reset', () => {
const daemon1 = getDaemon();
resetDaemon();
const daemon2 = getDaemon();
expect(daemon1).not.toBe(daemon2);
});
});
describe('start', () => {
it('should start the daemon', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
expect(daemon.running).toBe(true);
});
it('should register all workers by default', async () => {
// Use autoStart: false to just check registration without running workers
// Workers now throw errors when domain APIs are unavailable, so we can't
// actually auto-start them in unit tests without proper domain setup
daemon = createDaemon({ autoStart: false });
await daemon.start();
const manager = daemon.getWorkerManager();
const workers = manager.list();
// All workers should have been registered
expect(workers.length).toBe(10); // All 10 QE workers
});
it('should not auto-start workers when disabled', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
const manager = daemon.getWorkerManager();
const workers = manager.list();
// Workers registered but not started
workers.forEach(worker => {
expect(worker.lastRunAt).toBeUndefined();
});
});
it('should handle multiple start calls gracefully', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
await daemon.start();
expect(daemon.running).toBe(true);
});
});
describe('stop', () => {
it('should stop the daemon', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
await daemon.stop();
expect(daemon.running).toBe(false);
});
it('should handle stop when not running', async () => {
daemon = createDaemon();
await daemon.stop();
expect(daemon.running).toBe(false);
});
});
describe('restart', () => {
it('should restart the daemon', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
await daemon.restart();
expect(daemon.running).toBe(true);
});
});
describe('uptime', () => {
it('should return 0 when not started', () => {
daemon = createDaemon();
expect(daemon.uptime).toBe(0);
});
it('should track uptime', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
vi.advanceTimersByTime(5000);
expect(daemon.uptime).toBe(5);
vi.advanceTimersByTime(10000);
expect(daemon.uptime).toBe(15);
});
it('should reset uptime after restart', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
vi.advanceTimersByTime(5000);
expect(daemon.uptime).toBe(5);
await daemon.restart();
expect(daemon.uptime).toBe(0);
});
});
describe('getStatus', () => {
it('should return status when not running', () => {
daemon = createDaemon();
const status = daemon.getStatus();
expect(status.running).toBe(false);
expect(status.pid).toBe(process.pid);
expect(status.uptime).toBe(0);
expect(status.startedAt).toBeUndefined();
});
it('should return status when running', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
const status = daemon.getStatus();
expect(status.running).toBe(true);
expect(status.startedAt).toBeDefined();
expect(status.workerManager).toBeDefined();
});
it('should include worker manager health', async () => {
daemon = createDaemon({ autoStart: false });
await daemon.start();
const status = daemon.getStatus();
expect(status.workerManager.totalWorkers).toBe(10);
});
});
describe('getWorkerManager', () => {
it('should return the worker manager', () => {
daemon = createDaemon();
const manager = daemon.getWorkerManager();
expect(manager).toBeDefined();
expect(manager.list).toBeDefined();
});
});
describe('runWorker', () => {
it('should attempt to run a specific worker and fail without domain setup', async () => {
// Use real timers for this test since the worker uses async retries
vi.useRealTimers();
daemon = createDaemon({ autoStart: false });
await daemon.start();
// Workers now properly throw errors when domain APIs are unavailable
// instead of silently returning empty metrics
const consoleSpy = vi.spyOn(console, 'warn');
// Run the worker - it will fail after retries
await daemon.runWorker('test-health');
// After all retries fail, the worker should log warnings about the failure
// The warning message contains both the worker name and the error
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringMatching(/\[test-health\].*domain not available/)
);
consoleSpy.mockRestore();
// Restore fake timers for other tests
vi.useFakeTimers();
}, 30000); // Allow 30 seconds for retries
});
describe('enabledWorkers config', () => {
it('should only register enabled workers', () => {
daemon = createDaemon({
enabledWorkers: ['test-health', 'coverage-tracker'],
});
const manager = daemon.getWorkerManager();
const workers = manager.list();
expect(workers).toHaveLength(2);
expect(workers.map(w => w.config.id)).toContain('test-health');
expect(workers.map(w => w.config.id)).toContain('coverage-tracker');
});
});
describe('health checks', () => {
it('should perform periodic health checks', async () => {
const consoleSpy = vi.spyOn(console, 'warn');
daemon = createDaemon({
autoStart: false,
healthCheckIntervalMs: 1000,
});
await daemon.start();
// Advance past health check interval
vi.advanceTimersByTime(2000);
// Health check should have run (no warnings if healthy)
// We just verify it doesn't throw
expect(daemon.running).toBe(true);
consoleSpy.mockRestore();
});
});
});
describe('WORKER_REGISTRY', () => {
it('should export all 10 workers in registry', async () => {
const { WORKER_REGISTRY } = await import('../../../src/workers');
expect(Object.keys(WORKER_REGISTRY)).toHaveLength(10);
expect(WORKER_REGISTRY['test-health']).toBeDefined();
expect(WORKER_REGISTRY['coverage-tracker']).toBeDefined();
expect(WORKER_REGISTRY['flaky-detector']).toBeDefined();
expect(WORKER_REGISTRY['security-scan']).toBeDefined();
expect(WORKER_REGISTRY['quality-gate']).toBeDefined();
expect(WORKER_REGISTRY['learning-consolidation']).toBeDefined();
expect(WORKER_REGISTRY['defect-predictor']).toBeDefined();
expect(WORKER_REGISTRY['regression-monitor']).toBeDefined();
expect(WORKER_REGISTRY['performance-baseline']).toBeDefined();
expect(WORKER_REGISTRY['compliance-checker']).toBeDefined();
});
it('should have correct intervals for each worker', async () => {
const { WORKER_REGISTRY } = await import('../../../src/workers');
expect(WORKER_REGISTRY['test-health'].intervalMs).toBe(5 * 60 * 1000);
expect(WORKER_REGISTRY['coverage-tracker'].intervalMs).toBe(10 * 60 * 1000);
expect(WORKER_REGISTRY['flaky-detector'].intervalMs).toBe(15 * 60 * 1000);
expect(WORKER_REGISTRY['security-scan'].intervalMs).toBe(30 * 60 * 1000);
expect(WORKER_REGISTRY['quality-gate'].intervalMs).toBe(5 * 60 * 1000);
expect(WORKER_REGISTRY['learning-consolidation'].intervalMs).toBe(30 * 60 * 1000);
expect(WORKER_REGISTRY['defect-predictor'].intervalMs).toBe(15 * 60 * 1000);
expect(WORKER_REGISTRY['regression-monitor'].intervalMs).toBe(10 * 60 * 1000);
expect(WORKER_REGISTRY['performance-baseline'].intervalMs).toBe(60 * 60 * 1000);
expect(WORKER_REGISTRY['compliance-checker'].intervalMs).toBe(30 * 60 * 1000);
});
});