333 lines
9.0 KiB
TypeScript
333 lines
9.0 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import type { TaskWorklog } from './task-worklog';
|
|
import type { WorkActivity } from './work-activity';
|
|
import type { WorkItem } from './workspace-engine';
|
|
import { getWorkspaceDailyReport } from './workspace-daily-report';
|
|
|
|
const workItems: WorkItem[] = [
|
|
{
|
|
id: 'task-1',
|
|
type: 'devTask',
|
|
title: '实现登录接口',
|
|
status: 'in_progress',
|
|
completed: false,
|
|
productName: 'FTB',
|
|
projectName: '项目管理',
|
|
versionName: 'V1.0',
|
|
versionId: 'version-1',
|
|
extra: { actualStartAt: '2026-06-26T01:00:00.000Z' },
|
|
raw: {} as any,
|
|
},
|
|
{
|
|
id: 'task-2',
|
|
type: 'bug',
|
|
title: '修复菜单错位',
|
|
status: 'fixing',
|
|
completed: false,
|
|
productName: 'FTB',
|
|
projectName: '项目管理',
|
|
versionName: 'V1.0',
|
|
versionId: 'version-1',
|
|
raw: {} as any,
|
|
},
|
|
{
|
|
id: 'task-3',
|
|
type: 'devTask',
|
|
title: '跨天开发任务',
|
|
status: 'in_progress',
|
|
completed: false,
|
|
productName: 'FTB',
|
|
projectName: '项目管理',
|
|
versionName: 'V1.0',
|
|
versionId: 'version-1',
|
|
extra: { actualStartAt: '2026-06-25T02:00:00.000Z' },
|
|
raw: {} as any,
|
|
},
|
|
];
|
|
|
|
const worklogs: TaskWorklog[] = [
|
|
{
|
|
id: 'wl-1',
|
|
taskId: 'task-1',
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
hours: 1.5,
|
|
workContent: '完成登录接口联调',
|
|
createdAt: '2026-06-26T02:00:00.000Z',
|
|
},
|
|
{
|
|
id: 'wl-2',
|
|
taskId: 'task-2',
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
hours: 0.5,
|
|
workContent: '定位菜单错位原因',
|
|
createdAt: '2026-06-26T03:00:00.000Z',
|
|
},
|
|
{
|
|
id: 'wl-other-user',
|
|
taskId: 'task-1',
|
|
userId: '李四',
|
|
date: '2026-06-26',
|
|
hours: 8,
|
|
workContent: '其他人的日报',
|
|
createdAt: '2026-06-26T04:00:00.000Z',
|
|
},
|
|
{
|
|
id: 'wl-other-date',
|
|
taskId: 'task-1',
|
|
userId: '张三',
|
|
date: '2026-06-25',
|
|
hours: 8,
|
|
workContent: '昨天的日报',
|
|
createdAt: '2026-06-25T04:00:00.000Z',
|
|
},
|
|
];
|
|
|
|
const activities: WorkActivity[] = [
|
|
{
|
|
id: 'act-delivery',
|
|
actorId: '张三',
|
|
date: '2026-06-26',
|
|
occurredAt: '2026-06-26T06:00:00.000Z',
|
|
sourceType: 'dev_task',
|
|
sourceId: 'task-1',
|
|
action: 'dev_task_submitted',
|
|
category: 'delivery',
|
|
title: '实现登录接口',
|
|
summary: '已提测开发任务:实现登录接口',
|
|
},
|
|
{
|
|
id: 'act-risk',
|
|
actorId: '张三',
|
|
date: '2026-06-26',
|
|
occurredAt: '2026-06-26T05:00:00.000Z',
|
|
sourceType: 'bug',
|
|
sourceId: 'task-2',
|
|
action: 'bug_blocked',
|
|
category: 'risk',
|
|
title: '修复菜单错位',
|
|
summary: '标记阻塞:等待设计确认',
|
|
metadata: { blocker: '等待设计确认', helperId: '李四' },
|
|
},
|
|
{
|
|
id: 'act-note',
|
|
actorId: '张三',
|
|
date: '2026-06-26',
|
|
occurredAt: '2026-06-26T04:30:00.000Z',
|
|
sourceType: 'manual',
|
|
sourceId: 'task-1',
|
|
action: 'progress_note_added',
|
|
category: 'note',
|
|
title: '实现登录接口',
|
|
summary: '补充进展:完成接口联调',
|
|
metadata: { note: '完成接口联调' },
|
|
},
|
|
{
|
|
id: 'act-other-user',
|
|
actorId: '李四',
|
|
date: '2026-06-26',
|
|
occurredAt: '2026-06-26T07:00:00.000Z',
|
|
sourceType: 'dev_task',
|
|
sourceId: 'task-1',
|
|
action: 'dev_task_started',
|
|
category: 'progress',
|
|
title: '其他人的任务',
|
|
summary: '其他人的日报活动',
|
|
},
|
|
{
|
|
id: 'act-other-date',
|
|
actorId: '张三',
|
|
date: '2026-06-25',
|
|
occurredAt: '2026-06-25T07:00:00.000Z',
|
|
sourceType: 'dev_task',
|
|
sourceId: 'task-1',
|
|
action: 'dev_task_started',
|
|
category: 'progress',
|
|
title: '昨天的任务',
|
|
summary: '昨天的日报活动',
|
|
},
|
|
];
|
|
|
|
test('getWorkspaceDailyReport filters by current user and date', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
worklogs,
|
|
workItems,
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.equal(report.totalHours, 2);
|
|
assert.equal(report.totalCount, 2);
|
|
assert.deepEqual(report.items.map((item) => item.id), ['wl-2', 'wl-1']);
|
|
});
|
|
|
|
test('getWorkspaceDailyReport resolves task title and workspace context', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
worklogs,
|
|
workItems,
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.equal(report.items[0].taskTitle, '修复菜单错位');
|
|
assert.equal(report.items[0].productName, 'FTB');
|
|
assert.equal(report.items[0].projectName, '项目管理');
|
|
assert.equal(report.items[0].versionName, 'V1.0');
|
|
});
|
|
|
|
test('getWorkspaceDailyReport keeps logs whose task is no longer visible', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
worklogs: [
|
|
{
|
|
id: 'wl-missing',
|
|
taskId: 'deleted-task',
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
hours: 0.5,
|
|
workContent: '处理历史任务',
|
|
createdAt: '2026-06-26T01:00:00.000Z',
|
|
},
|
|
],
|
|
workItems: [],
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.equal(report.totalHours, 0.5);
|
|
assert.equal(report.totalCount, 1);
|
|
assert.equal(report.items[0].taskTitle, '未知任务');
|
|
assert.equal(report.items[0].workContent, '处理历史任务');
|
|
});
|
|
|
|
test('getWorkspaceDailyReport groups current user activities by category', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
activities,
|
|
worklogs,
|
|
workItems,
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.deepEqual(report.groups.delivery.map((item) => item.id), ['act-delivery']);
|
|
assert.deepEqual(report.groups.risk.map((item) => item.id), ['act-risk']);
|
|
assert.deepEqual(report.groups.note.map((item) => item.id), ['act-note']);
|
|
assert.equal(report.totalCount, 5);
|
|
assert.equal(report.groups.delivery[0].context, 'FTB / 项目管理 / V1.0');
|
|
});
|
|
|
|
test('getWorkspaceDailyReport counts actual hours from today activity sources', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
activities: [
|
|
{
|
|
id: 'act-started',
|
|
actorId: 'Alice',
|
|
date: '2026-06-26',
|
|
occurredAt: '2026-06-26T02:00:00.000Z',
|
|
sourceType: 'dev_task',
|
|
sourceId: 'task-activity-hours',
|
|
action: 'dev_task_started',
|
|
category: 'progress',
|
|
title: 'Daily report hours',
|
|
summary: 'Started development: Daily report hours',
|
|
},
|
|
{
|
|
id: 'act-submitted',
|
|
actorId: 'Alice',
|
|
date: '2026-06-26',
|
|
occurredAt: '2026-06-26T04:00:00.000Z',
|
|
sourceType: 'dev_task',
|
|
sourceId: 'task-activity-hours',
|
|
action: 'dev_task_submitted',
|
|
category: 'delivery',
|
|
title: 'Daily report hours',
|
|
summary: 'Submitted development task: Daily report hours',
|
|
},
|
|
],
|
|
worklogs: [],
|
|
workItems: [
|
|
{
|
|
id: 'task-activity-hours',
|
|
type: 'devTask',
|
|
title: 'Daily report hours',
|
|
status: 'submitted',
|
|
completed: true,
|
|
productName: 'FTB',
|
|
projectName: 'Project Management',
|
|
versionName: 'V1.0',
|
|
versionId: 'version-1',
|
|
extra: {
|
|
actualStartAt: '2026-06-26T02:00:00.000Z',
|
|
actualEndAt: '2026-06-26T04:00:00.000Z',
|
|
},
|
|
raw: {
|
|
id: 'task-activity-hours',
|
|
actualStartAt: '2026-06-26T02:00:00.000Z',
|
|
actualEndAt: '2026-06-26T04:00:00.000Z',
|
|
status: 'submitted',
|
|
updatedAt: '2026-06-26T04:00:00.000Z',
|
|
} as any,
|
|
},
|
|
],
|
|
userId: 'Alice',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.equal(report.totalHours, 2);
|
|
assert.equal(report.totalCount, 2);
|
|
});
|
|
|
|
test('getWorkspaceDailyReport rebuilds activity evidence from persisted work item timestamps', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
activities: [],
|
|
worklogs: [],
|
|
workItems: [
|
|
{
|
|
id: 'task-refresh-recovery',
|
|
type: 'devTask',
|
|
title: 'Refresh recovery',
|
|
status: 'submitted',
|
|
completed: true,
|
|
productName: 'FTB',
|
|
projectName: 'Project Management',
|
|
versionName: 'V1.0',
|
|
versionId: 'version-1',
|
|
extra: {
|
|
actualStartAt: '2026-06-26T02:00:00.000Z',
|
|
actualEndAt: '2026-06-26T04:00:00.000Z',
|
|
},
|
|
raw: {
|
|
id: 'task-refresh-recovery',
|
|
title: 'Refresh recovery',
|
|
actualStartAt: '2026-06-26T02:00:00.000Z',
|
|
actualEndAt: '2026-06-26T04:00:00.000Z',
|
|
status: 'submitted',
|
|
updatedAt: '2026-06-26T04:00:00.000Z',
|
|
} as any,
|
|
},
|
|
],
|
|
userId: 'Alice',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.deepEqual(report.groups.progress.map((item) => item.action), ['dev_task_started']);
|
|
assert.deepEqual(report.groups.delivery.map((item) => item.action), ['dev_task_submitted']);
|
|
assert.equal(report.totalHours, 2);
|
|
assert.equal(report.totalCount, 2);
|
|
});
|
|
|
|
test('getWorkspaceDailyReport flags multi-day in-progress items without today progress', () => {
|
|
const report = getWorkspaceDailyReport({
|
|
activities,
|
|
worklogs,
|
|
workItems,
|
|
userId: '张三',
|
|
date: '2026-06-26',
|
|
});
|
|
|
|
assert.deepEqual(report.needsProgressItems.map((item) => item.id), ['task-3']);
|
|
assert.equal(report.needsProgressItems[0].title, '跨天开发任务');
|
|
});
|