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 { 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 || '', /不允许/); });