60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
import type { TestCase, TestCaseStatus } from './test-case';
|
|
import { canTcTransition } from './test-case';
|
|
|
|
export interface TestCaseWorkflowResult {
|
|
ok: boolean;
|
|
patch?: Partial<TestCase>;
|
|
message?: string;
|
|
}
|
|
|
|
export interface TestCaseTransitionOptions {
|
|
now?: Date;
|
|
failReason?: string;
|
|
blockReason?: string;
|
|
}
|
|
|
|
export function normalizeTestCaseOnCreate(testCase: TestCase): TestCase {
|
|
return {
|
|
...testCase,
|
|
status: 'pending',
|
|
startedAt: undefined,
|
|
completedAt: undefined,
|
|
executedAt: undefined,
|
|
failReason: undefined,
|
|
blockReason: undefined,
|
|
};
|
|
}
|
|
|
|
export function applyTestCaseTransition(
|
|
testCase: TestCase,
|
|
to: TestCaseStatus,
|
|
options: TestCaseTransitionOptions = {},
|
|
): TestCaseWorkflowResult {
|
|
if (!canTcTransition(testCase.status, to)) {
|
|
return { ok: false, message: `不允许从「${testCase.status}」流转到「${to}」` };
|
|
}
|
|
|
|
const nowIso = (options.now ?? new Date()).toISOString();
|
|
const patch: Partial<TestCase> = {
|
|
status: to,
|
|
aiDraft: false,
|
|
executedAt: nowIso,
|
|
};
|
|
|
|
if (to === 'running') {
|
|
if (!testCase.startedAt) patch.startedAt = nowIso;
|
|
patch.completedAt = undefined;
|
|
patch.failReason = undefined;
|
|
patch.blockReason = undefined;
|
|
}
|
|
|
|
if (to === 'passed' || to === 'failed' || to === 'blocked') {
|
|
patch.completedAt = nowIso;
|
|
}
|
|
|
|
if (to === 'failed' && options.failReason?.trim()) patch.failReason = options.failReason.trim();
|
|
if (to === 'blocked' && options.blockReason?.trim()) patch.blockReason = options.blockReason.trim();
|
|
|
|
return { ok: true, patch };
|
|
}
|