125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
classifyTaskProgressTouch,
|
|
getTaskCommentForActivityRecord,
|
|
} from '../../../../../src/main/services/team/stallMonitor/TaskProgressSignalClassifier';
|
|
|
|
import type { BoardTaskActivityRecord } from '../../../../../src/main/services/team/taskLogs/activity/BoardTaskActivityRecord';
|
|
import type { TeamTask } from '../../../../../src/shared/types';
|
|
|
|
function createTask(commentText?: string): TeamTask {
|
|
return {
|
|
id: 'task-a',
|
|
displayId: 'abcd1234',
|
|
subject: 'Task A',
|
|
status: 'in_progress',
|
|
comments:
|
|
commentText == null
|
|
? []
|
|
: [
|
|
{
|
|
id: 'comment-a',
|
|
author: 'alice',
|
|
text: commentText,
|
|
createdAt: '2026-04-19T12:00:00.000Z',
|
|
type: 'regular',
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
function createCommentRecord(commentId: string | null = 'comment-a'): BoardTaskActivityRecord {
|
|
return {
|
|
id: 'record-a',
|
|
timestamp: '2026-04-19T12:00:00.000Z',
|
|
task: {
|
|
locator: { ref: 'task-a', refKind: 'canonical', canonicalId: 'task-a' },
|
|
resolution: 'resolved',
|
|
taskRef: { taskId: 'task-a', displayId: 'abcd1234', teamName: 'demo' },
|
|
},
|
|
linkKind: 'board_action',
|
|
targetRole: 'subject',
|
|
actor: {
|
|
memberName: 'alice',
|
|
role: 'member',
|
|
sessionId: 'session-a',
|
|
isSidechain: true,
|
|
},
|
|
actorContext: { relation: 'same_task' },
|
|
action: {
|
|
canonicalToolName: 'task_add_comment',
|
|
category: 'comment',
|
|
toolUseId: 'tool-a',
|
|
details: commentId ? { commentId } : {},
|
|
},
|
|
source: {
|
|
messageUuid: 'msg-a',
|
|
filePath: '/tmp/session.jsonl',
|
|
toolUseId: 'tool-a',
|
|
sourceOrder: 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
describe('TaskProgressSignalClassifier', () => {
|
|
it.each([
|
|
'Начинаю работу.',
|
|
'Приступаю.',
|
|
'Беру в работу.',
|
|
'Проверю.',
|
|
'Посмотрю.',
|
|
'Will start.',
|
|
'Starting work.',
|
|
'Taking this.',
|
|
])(
|
|
'classifies start-only comment as weak: %s',
|
|
(text) => {
|
|
expect(
|
|
classifyTaskProgressTouch({
|
|
task: createTask(text),
|
|
record: createCommentRecord(),
|
|
})
|
|
).toMatchObject({ signal: 'weak_start_only' });
|
|
}
|
|
);
|
|
|
|
it.each([
|
|
'Found the failing test in src/app.ts and reproduced it with pnpm test.',
|
|
'Проверил src/main.ts - причина в stale runtime metadata.',
|
|
'Blocked: нет доступа к проекту.',
|
|
'Нужно уточнение: какой файл менять?',
|
|
'Tests failed with EADDRINUSE, next step is to isolate the server port.',
|
|
])('does not classify substantive, blocker, or question comments as weak: %s', (text) => {
|
|
const classification = classifyTaskProgressTouch({
|
|
task: createTask(text),
|
|
record: createCommentRecord(),
|
|
});
|
|
|
|
expect(classification.signal).not.toBe('weak_start_only');
|
|
});
|
|
|
|
it('returns unknown when commentId is missing', () => {
|
|
expect(
|
|
classifyTaskProgressTouch({
|
|
task: createTask('Начинаю работу.'),
|
|
record: createCommentRecord(null),
|
|
})
|
|
).toMatchObject({ signal: 'unknown' });
|
|
});
|
|
|
|
it('returns unknown when comment text is unavailable', () => {
|
|
expect(
|
|
classifyTaskProgressTouch({
|
|
task: createTask(),
|
|
record: createCommentRecord(),
|
|
})
|
|
).toMatchObject({ signal: 'unknown' });
|
|
});
|
|
|
|
it('returns the matching task comment for an activity record', () => {
|
|
const task = createTask('Начинаю работу.');
|
|
|
|
expect(getTaskCommentForActivityRecord(task, createCommentRecord())?.id).toBe('comment-a');
|
|
});
|
|
});
|