Files
ftb-project-management/apps/web/lib/dev-task-workflow.test.ts
2026-06-25 14:04:45 +08:00

82 lines
2.4 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import type { DevTask } from './dev-task';
import { applyDevTaskTransition, normalizeDevTaskOnCreate } from './dev-task-workflow';
function task(patch: Partial<DevTask> = {}): DevTask {
return {
id: 'task-1',
taskNo: 'DEV-001',
requirementId: 'req-1',
title: '实现拖拽排序',
categoryId: 'cat-frontend-interaction',
assigneeId: 'Alice',
priority: 'P2',
expectedStartAt: '2026-06-25T01:00:00.000Z',
expectedEndAt: '2026-06-25T02:00:00.000Z',
status: 'todo',
isBlocked: false,
createdBy: 'PM',
createdAt: '2026-06-25T00:00:00.000Z',
updatedAt: '2026-06-25T00:00:00.000Z',
...patch,
};
}
test('normalizeDevTaskOnCreate forces todo and clears actual timestamps for AI drafts', () => {
const normalized = normalizeDevTaskOnCreate(task({
status: 'in_progress',
actualStartAt: '2026-06-25T01:00:00.000Z',
actualEndAt: '2026-06-25T02:00:00.000Z',
aiDraft: true,
}));
assert.equal(normalized.status, 'todo');
assert.equal(normalized.actualStartAt, undefined);
assert.equal(normalized.actualEndAt, undefined);
});
test('todo to in_progress writes actualStartAt from manual click time', () => {
const result = applyDevTaskTransition(task(), 'in_progress', {
now: new Date('2026-06-25T03:30:00.000Z'),
});
assert.equal(result.ok, true);
assert.equal(result.patch?.status, 'in_progress');
assert.equal(result.patch?.actualStartAt, '2026-06-25T03:30:00.000Z');
});
test('in_progress to testing does not write actualEndAt', () => {
const result = applyDevTaskTransition(task({
status: 'in_progress',
actualStartAt: '2026-06-25T03:30:00.000Z',
}), 'testing', {
now: new Date('2026-06-25T04:00:00.000Z'),
});
assert.equal(result.ok, true);
assert.equal(result.patch?.actualEndAt, undefined);
});
test('testing to submitted writes actualEndAt', () => {
const result = applyDevTaskTransition(task({
status: 'testing',
actualStartAt: '2026-06-25T03:30:00.000Z',
}), 'submitted', {
now: new Date('2026-06-25T05:00:00.000Z'),
});
assert.equal(result.ok, true);
assert.equal(result.patch?.actualEndAt, '2026-06-25T05:00:00.000Z');
});
test('invalid transition is rejected', () => {
const result = applyDevTaskTransition(task(), 'submitted', {
now: new Date('2026-06-25T05:00:00.000Z'),
});
assert.equal(result.ok, false);
assert.match(result.message || '', /不允许/);
});