Skip to content
72 changes: 72 additions & 0 deletions src/__tests__/main/cue/cue-activity-log.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Tests for the Cue activity log ring buffer.
*/

import { describe, it, expect } from 'vitest';
import { createCueActivityLog } from '../../../main/cue/cue-activity-log';
import type { CueRunResult } from '../../../main/cue/cue-types';

function makeResult(id: string): CueRunResult {
return {
runId: id,
sessionId: 'session-1',
sessionName: 'Test',
subscriptionName: 'sub',
event: { id: 'e1', type: 'time.heartbeat', timestamp: '', triggerName: 'sub', payload: {} },
status: 'completed',
stdout: '',
stderr: '',
exitCode: 0,
durationMs: 100,
startedAt: '',
endedAt: '',
};
}

describe('createCueActivityLog', () => {
it('stores and retrieves results', () => {
const log = createCueActivityLog();
log.push(makeResult('r1'));
log.push(makeResult('r2'));
expect(log.getAll()).toHaveLength(2);
expect(log.getAll()[0].runId).toBe('r1');
});

it('respects limit parameter on getAll', () => {
const log = createCueActivityLog();
log.push(makeResult('r1'));
log.push(makeResult('r2'));
log.push(makeResult('r3'));
const last2 = log.getAll(2);
expect(last2).toHaveLength(2);
expect(last2[0].runId).toBe('r2');
expect(last2[1].runId).toBe('r3');
});

it('evicts oldest entries when exceeding maxSize', () => {
const log = createCueActivityLog(3);
log.push(makeResult('r1'));
log.push(makeResult('r2'));
log.push(makeResult('r3'));
log.push(makeResult('r4'));
const all = log.getAll();
expect(all).toHaveLength(3);
expect(all[0].runId).toBe('r2');
expect(all[2].runId).toBe('r4');
});

it('clear empties the log', () => {
const log = createCueActivityLog();
log.push(makeResult('r1'));
log.clear();
expect(log.getAll()).toHaveLength(0);
});

it('returns a copy from getAll, not a reference', () => {
const log = createCueActivityLog();
log.push(makeResult('r1'));
const snapshot = log.getAll();
log.push(makeResult('r2'));
expect(snapshot).toHaveLength(1);
});
});
43 changes: 1 addition & 42 deletions src/__tests__/main/cue/cue-completion-chains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { CueConfig, CueEvent } from '../../../main/cue/cue-types';
import type { SessionInfo } from '../../../shared/types';

// Mock the yaml loader
const mockLoadCueConfig = vi.fn<(projectRoot: string) => CueConfig | null>();
Expand Down Expand Up @@ -52,47 +51,7 @@ vi.mock('crypto', () => ({
}));

import { CueEngine, type CueEngineDeps } from '../../../main/cue/cue-engine';

function createMockSession(overrides: Partial<SessionInfo> = {}): SessionInfo {
return {
id: 'session-1',
name: 'Test Session',
toolType: 'claude-code',
cwd: '/projects/test',
projectRoot: '/projects/test',
...overrides,
};
}

function createMockConfig(overrides: Partial<CueConfig> = {}): CueConfig {
return {
subscriptions: [],
settings: { timeout_minutes: 30, timeout_on_fail: 'break', max_concurrent: 1, queue_size: 10 },
...overrides,
};
}

function createMockDeps(overrides: Partial<CueEngineDeps> = {}): CueEngineDeps {
return {
getSessions: vi.fn(() => [createMockSession()]),
onCueRun: vi.fn(async () => ({
runId: 'run-1',
sessionId: 'session-1',
sessionName: 'Test Session',
subscriptionName: 'test',
event: {} as CueEvent,
status: 'completed' as const,
stdout: 'output',
stderr: '',
exitCode: 0,
durationMs: 100,
startedAt: new Date().toISOString(),
endedAt: new Date().toISOString(),
})) as CueEngineDeps['onCueRun'],
onLog: vi.fn(),
...overrides,
};
}
import { createMockSession, createMockConfig, createMockDeps } from './cue-test-helpers';

describe('CueEngine completion chains', () => {
beforeEach(() => {
Expand Down
48 changes: 1 addition & 47 deletions src/__tests__/main/cue/cue-concurrency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { CueConfig, CueEvent, CueRunResult } from '../../../main/cue/cue-types';
import type { SessionInfo } from '../../../shared/types';

// Mock the yaml loader
const mockLoadCueConfig = vi.fn<(projectRoot: string) => CueConfig | null>();
Expand All @@ -35,52 +34,7 @@ vi.mock('crypto', () => ({
}));

import { CueEngine, type CueEngineDeps } from '../../../main/cue/cue-engine';

function createMockSession(overrides: Partial<SessionInfo> = {}): SessionInfo {
return {
id: 'session-1',
name: 'Test Session',
toolType: 'claude-code',
cwd: '/projects/test',
projectRoot: '/projects/test',
...overrides,
};
}

function createMockConfig(overrides: Partial<CueConfig> = {}): CueConfig {
return {
subscriptions: [],
settings: {
timeout_minutes: 30,
timeout_on_fail: 'break',
max_concurrent: 1,
queue_size: 10,
},
...overrides,
};
}

function createMockDeps(overrides: Partial<CueEngineDeps> = {}): CueEngineDeps {
return {
getSessions: vi.fn(() => [createMockSession()]),
onCueRun: vi.fn(async () => ({
runId: 'run-1',
sessionId: 'session-1',
sessionName: 'Test Session',
subscriptionName: 'test',
event: {} as CueEvent,
status: 'completed' as const,
stdout: 'output',
stderr: '',
exitCode: 0,
durationMs: 100,
startedAt: new Date().toISOString(),
endedAt: new Date().toISOString(),
})),
onLog: vi.fn(),
...overrides,
};
}
import { createMockSession, createMockConfig, createMockDeps } from './cue-test-helpers';

describe('CueEngine Concurrency Control', () => {
let yamlWatcherCleanup: ReturnType<typeof vi.fn>;
Expand Down
77 changes: 34 additions & 43 deletions src/__tests__/main/cue/cue-engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { CueConfig, CueEvent, CueRunResult } from '../../../main/cue/cue-types';
import type { SessionInfo } from '../../../shared/types';

// Mock the yaml loader
const mockLoadCueConfig = vi.fn<(projectRoot: string) => CueConfig | null>();
Expand Down Expand Up @@ -53,48 +52,7 @@ import {
calculateNextScheduledTime,
type CueEngineDeps,
} from '../../../main/cue/cue-engine';

function createMockSession(overrides: Partial<SessionInfo> = {}): SessionInfo {
return {
id: 'session-1',
name: 'Test Session',
toolType: 'claude-code',
cwd: '/projects/test',
projectRoot: '/projects/test',
...overrides,
};
}

function createMockConfig(overrides: Partial<CueConfig> = {}): CueConfig {
return {
subscriptions: [],
settings: { timeout_minutes: 30, timeout_on_fail: 'break', max_concurrent: 1, queue_size: 10 },
...overrides,
};
}

function createMockDeps(overrides: Partial<CueEngineDeps> = {}): CueEngineDeps {
return {
getSessions: vi.fn(() => [createMockSession()]),
onCueRun: vi.fn(async (request: Parameters<CueEngineDeps['onCueRun']>[0]) => ({
runId: 'run-1',
sessionId: 'session-1',
sessionName: 'Test Session',
subscriptionName: request.subscriptionName,
event: request.event,
status: 'completed' as const,
stdout: 'output',
stderr: '',
exitCode: 0,
durationMs: 100,
startedAt: new Date().toISOString(),
endedAt: new Date().toISOString(),
})),
onStopCueRun: vi.fn(() => true),
onLog: vi.fn(),
...overrides,
};
}
import { createMockSession, createMockConfig, createMockDeps } from './cue-test-helpers';

describe('CueEngine', () => {
let yamlWatcherCleanup: ReturnType<typeof vi.fn>;
Expand Down Expand Up @@ -930,6 +888,39 @@ describe('CueEngine', () => {
engine.stop();
});

it('stopRun adds the stopped run to the activity log', async () => {
const deps = createMockDeps({
onCueRun: vi.fn(() => new Promise<CueRunResult>(() => {})),
});
const config = createMockConfig({
subscriptions: [
{
name: 'timer',
event: 'time.heartbeat',
enabled: true,
prompt: 'test',
interval_minutes: 60,
},
],
});
mockLoadCueConfig.mockReturnValue(config);
const engine = new CueEngine(deps);
engine.start();

await vi.advanceTimersByTimeAsync(10);

const activeRun = engine.getActiveRuns()[0];
expect(activeRun).toBeDefined();
engine.stopRun(activeRun.runId);

const log = engine.getActivityLog();
expect(log).toHaveLength(1);
expect(log[0].runId).toBe(activeRun.runId);
expect(log[0].status).toBe('stopped');

engine.stop();
});

it('stopAll clears all active runs', async () => {
// Use a slow-resolving onCueRun to keep runs active
const deps = createMockDeps({
Expand Down
43 changes: 1 addition & 42 deletions src/__tests__/main/cue/cue-multi-hop-chains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { CueConfig, CueEvent, CueRunResult } from '../../../main/cue/cue-types';
import type { SessionInfo } from '../../../shared/types';

// Mock the yaml loader
const mockLoadCueConfig = vi.fn<(projectRoot: string) => CueConfig | null>();
Expand Down Expand Up @@ -50,47 +49,7 @@ vi.mock('crypto', () => ({
}));

import { CueEngine, type CueEngineDeps } from '../../../main/cue/cue-engine';

function createMockSession(overrides: Partial<SessionInfo> = {}): SessionInfo {
return {
id: 'session-1',
name: 'Test Session',
toolType: 'claude-code',
cwd: '/projects/test',
projectRoot: '/projects/test',
...overrides,
};
}

function createMockConfig(overrides: Partial<CueConfig> = {}): CueConfig {
return {
subscriptions: [],
settings: { timeout_minutes: 30, timeout_on_fail: 'break', max_concurrent: 1, queue_size: 10 },
...overrides,
};
}

function createMockDeps(overrides: Partial<CueEngineDeps> = {}): CueEngineDeps {
return {
getSessions: vi.fn(() => [createMockSession()]),
onCueRun: vi.fn(async () => ({
runId: 'run-1',
sessionId: 'session-1',
sessionName: 'Test Session',
subscriptionName: 'test',
event: {} as CueEvent,
status: 'completed' as const,
stdout: 'output',
stderr: '',
exitCode: 0,
durationMs: 100,
startedAt: new Date().toISOString(),
endedAt: new Date().toISOString(),
})) as CueEngineDeps['onCueRun'],
onLog: vi.fn(),
...overrides,
};
}
import { createMockSession, createMockConfig, createMockDeps } from './cue-test-helpers';

describe('CueEngine multi-hop completion chains', () => {
beforeEach(() => {
Expand Down
Loading