From 5a7d5ea310373642288e4d3f511aef27173d8a00 Mon Sep 17 00:00:00 2001 From: 777genius Date: Sat, 18 Apr 2026 13:29:57 +0300 Subject: [PATCH] test(team): add real-jsonl coverage for task log fallback --- .../BoardTaskLogStreamIntegration.test.ts | 48 +++++++++++ .../TaskLogStreamSection.integration.test.ts | 85 ++++++++++++++++++- 2 files changed, 129 insertions(+), 4 deletions(-) diff --git a/test/main/services/team/BoardTaskLogStreamIntegration.test.ts b/test/main/services/team/BoardTaskLogStreamIntegration.test.ts index 40355e34..b401c12f 100644 --- a/test/main/services/team/BoardTaskLogStreamIntegration.test.ts +++ b/test/main/services/team/BoardTaskLogStreamIntegration.test.ts @@ -643,4 +643,52 @@ describe('BoardTaskLogStreamService integration', () => { expect(bashCommands).not.toContain('echo alien'); expect(rawMessages.some((message) => message.uuid === 'u-bash-alice-real')).toBe(false); }); + + it('falls back to createdAt/updatedAt time window when workIntervals are missing', async () => { + const dir = await mkdtemp(path.join(tmpdir(), 'task-log-stream-created-window-')); + tempDirs.push(dir); + const transcriptPath = path.join(dir, 'session.jsonl'); + const fixtureText = await readFile(REAL_FIXTURE_PATH, 'utf8'); + await writeFile(transcriptPath, fixtureText, 'utf8'); + + const task = createTask({ + owner: 'tom', + createdAt: '2026-04-12T15:35:50.000Z', + updatedAt: '2026-04-12T15:37:00.000Z', + workIntervals: undefined, + }); + + const recordSource = { + getTaskRecords: async () => buildRecordsFromTranscript(transcriptPath, task), + }; + const taskReader = { + getTasks: async () => [task], + getDeletedTasks: async () => [] as TeamTask[], + }; + const transcriptSourceLocator = { + getContext: async () => + ({ + transcriptFiles: [transcriptPath], + config: { + members: [{ name: 'team-lead', agentType: 'team-lead' }], + }, + }) as never, + }; + + const service = new BoardTaskLogStreamService( + recordSource as never, + undefined as never, + undefined as never, + undefined as never, + undefined as never, + taskReader as never, + transcriptSourceLocator as never, + ); + const response = await service.getTaskLogStream(TEAM_NAME, task.id); + const rawMessages = flattenRawMessages(response); + + expect(response.participants.map((participant) => participant.label)).toEqual(['tom']); + expect(rawMessages.some((message) => message.uuid === 'a-bash-real')).toBe(true); + expect(rawMessages.some((message) => message.uuid === 'u-bash-alice-real')).toBe(false); + }); }); diff --git a/test/renderer/components/team/taskLogs/TaskLogStreamSection.integration.test.ts b/test/renderer/components/team/taskLogs/TaskLogStreamSection.integration.test.ts index 732bc396..69f9bd60 100644 --- a/test/renderer/components/team/taskLogs/TaskLogStreamSection.integration.test.ts +++ b/test/renderer/components/team/taskLogs/TaskLogStreamSection.integration.test.ts @@ -1,4 +1,4 @@ -import { mkdtemp, rm, writeFile } from 'fs/promises'; +import { mkdtemp, readFile, rm, writeFile } from 'fs/promises'; import { tmpdir } from 'os'; import path from 'path'; import React, { act } from 'react'; @@ -14,6 +14,10 @@ import type { TeamTask } from '../../../../../src/shared/types'; const TEAM_NAME = 'beacon-desk-2'; const TASK_ID = 'c414cd52-470a-4b51-ae1e-e5250fff95d7'; +const REAL_FIXTURE_PATH = path.resolve( + process.cwd(), + 'test/fixtures/team/task-log-stream-fallback-real.jsonl', +); const apiState = { getTaskLogStream: vi.fn(), @@ -105,8 +109,7 @@ function createUserEntry(args: { }; } -async function buildStreamResponse(transcriptPath: string) { - const task = createTask(); +async function buildStreamResponse(transcriptPath: string, task: TeamTask = createTask()) { const transcriptReader = new BoardTaskActivityTranscriptReader(); const recordBuilder = new BoardTaskActivityRecordBuilder(); const messages = await transcriptReader.readFiles([transcriptPath]); @@ -119,8 +122,29 @@ async function buildStreamResponse(transcriptPath: string) { messages, }), }; + const taskReader = { + getTasks: async () => [task], + getDeletedTasks: async () => [] as TeamTask[], + }; + const transcriptSourceLocator = { + getContext: async () => + ({ + transcriptFiles: [transcriptPath], + config: { + members: [{ name: 'team-lead', agentType: 'team-lead' }], + }, + }) as never, + }; - const service = new BoardTaskLogStreamService(recordSource as never); + const service = new BoardTaskLogStreamService( + recordSource as never, + undefined as never, + undefined as never, + undefined as never, + undefined as never, + taskReader as never, + transcriptSourceLocator as never, + ); return service.getTaskLogStream(TEAM_NAME, task.id); } @@ -547,4 +571,57 @@ describe('TaskLogStreamSection integration', () => { await flushMicrotasks(); }); }); + + it('renders fallback worker logs from a real-format transcript fixture and hides unrelated participant logs', async () => { + vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true); + + const dir = await mkdtemp(path.join(tmpdir(), 'task-log-stream-render-real-')); + tempDirs.push(dir); + const transcriptPath = path.join(dir, 'session.jsonl'); + const fixtureText = await readFile(REAL_FIXTURE_PATH, 'utf8'); + await writeFile(transcriptPath, fixtureText, 'utf8'); + + apiState.getTaskLogStream.mockResolvedValueOnce( + await buildStreamResponse( + transcriptPath, + createTask({ + owner: 'tom', + workIntervals: [ + { + startedAt: '2026-04-12T15:36:00.000Z', + completedAt: '2026-04-12T15:40:00.000Z', + }, + ], + }), + ), + ); + + const host = document.createElement('div'); + document.body.appendChild(host); + const root = createRoot(host); + + await act(async () => { + root.render( + React.createElement( + TooltipProvider, + null, + React.createElement(TaskLogStreamSection, { teamName: TEAM_NAME, taskId: TASK_ID }), + ), + ); + await flushMicrotasks(); + await flushMicrotasks(); + }); + + const text = host.textContent ?? ''; + expect(text).toContain('Task Log Stream'); + expect(text).toContain('Bash'); + expect(text).toContain('Run targeted tests'); + expect(text).not.toContain('echo alien'); + expect(text).not.toContain('alice'); + + await act(async () => { + root.unmount(); + await flushMicrotasks(); + }); + }); });