agent-ecosystem/test/main/services/team/TeamLogSourceTracker.test.ts

119 lines
4.1 KiB
TypeScript

import { mkdtemp, mkdir, rm, writeFile } from 'fs/promises';
import { tmpdir } from 'os';
import * as path from 'path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { TeamLogSourceTracker } from '../../../../src/main/services/team/TeamLogSourceTracker';
import type { TeamMemberLogsFinder } from '../../../../src/main/services/team/TeamMemberLogsFinder';
import type { TeamChangeEvent } from '../../../../src/shared/types';
describe('TeamLogSourceTracker', () => {
let tempDir: string | null = null;
afterEach(async () => {
if (tempDir) {
await rm(tempDir, { recursive: true, force: true });
tempDir = null;
}
});
it('emits task-log-change for matching runtime freshness signals without broad log-source-change', async () => {
tempDir = await mkdtemp(path.join(tmpdir(), 'team-log-source-tracker-'));
const logsFinder = {
getLogSourceWatchContext: vi.fn(async () => ({
projectDir: tempDir!,
sessionIds: [],
})),
} as unknown as TeamMemberLogsFinder;
const tracker = new TeamLogSourceTracker(logsFinder);
const emitter = vi.fn<(event: TeamChangeEvent) => void>();
tracker.setEmitter(emitter);
await tracker.enableTracking('demo', 'change_presence');
emitter.mockClear();
await new Promise((resolve) => setTimeout(resolve, 100));
const taskId = '123e4567-e89b-12d3-a456-426614174999';
const signalDir = path.join(tempDir, '.board-task-log-freshness');
await mkdir(signalDir, { recursive: true });
await writeFile(path.join(signalDir, `${encodeURIComponent(taskId)}.json`), '{"ok":true}');
await vi.waitFor(() => {
expect(emitter).toHaveBeenCalledWith({
type: 'task-log-change',
teamName: 'demo',
taskId,
});
});
expect(emitter.mock.calls.map(([event]) => event.type)).not.toContain('log-source-change');
await tracker.disableTracking('demo', 'change_presence');
});
it('keeps task-log tracking alive until the last consumer unsubscribes', async () => {
tempDir = await mkdtemp(path.join(tmpdir(), 'team-log-source-tracker-refcount-'));
const logsFinder = {
getLogSourceWatchContext: vi.fn(async () => ({
projectDir: tempDir!,
sessionIds: [],
})),
} as unknown as TeamMemberLogsFinder;
const tracker = new TeamLogSourceTracker(logsFinder);
const emitter = vi.fn<(event: TeamChangeEvent) => void>();
tracker.setEmitter(emitter);
await tracker.enableTracking('demo', 'task_log_stream');
await tracker.enableTracking('demo', 'task_log_stream');
emitter.mockClear();
await new Promise((resolve) => setTimeout(resolve, 100));
await tracker.disableTracking('demo', 'task_log_stream');
const taskId = '223e4567-e89b-12d3-a456-426614174999';
const signalDir = path.join(tempDir, '.board-task-log-freshness');
await mkdir(signalDir, { recursive: true });
await writeFile(path.join(signalDir, `${encodeURIComponent(taskId)}.json`), '{"ok":true}');
await vi.waitFor(() => {
expect(emitter).toHaveBeenCalledWith({
type: 'task-log-change',
teamName: 'demo',
taskId,
});
});
emitter.mockClear();
await tracker.disableTracking('demo', 'task_log_stream');
await writeFile(path.join(signalDir, `${encodeURIComponent(taskId)}.json`), '{"ok":false}');
await new Promise((resolve) => setTimeout(resolve, 350));
expect(emitter).not.toHaveBeenCalled();
});
it('does not reinitialize when another consumer joins an already tracked team', async () => {
tempDir = await mkdtemp(path.join(tmpdir(), 'team-log-source-tracker-init-'));
const logsFinder = {
getLogSourceWatchContext: vi.fn(async () => ({
projectDir: tempDir!,
sessionIds: [],
})),
} as unknown as TeamMemberLogsFinder;
const tracker = new TeamLogSourceTracker(logsFinder);
await tracker.enableTracking('demo', 'tool_activity');
await tracker.enableTracking('demo', 'task_log_stream');
expect(logsFinder.getLogSourceWatchContext).toHaveBeenCalledTimes(1);
await tracker.disableTracking('demo', 'task_log_stream');
await tracker.disableTracking('demo', 'tool_activity');
});
});