完成五人团队正式内用最小收口(07-18-small-team-internal-readiness)
这个提交包含在:
@@ -0,0 +1,189 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { verifyLocalCandidateResults } from "../verify-local-candidate-results.mjs";
|
||||
|
||||
const bundles = [
|
||||
"login-logout",
|
||||
"reimbursement",
|
||||
"approval",
|
||||
"invoice",
|
||||
"procurement",
|
||||
"project-contract",
|
||||
"payment-ap",
|
||||
"people-audit",
|
||||
"attendance-archive",
|
||||
"mobile-workspace",
|
||||
];
|
||||
|
||||
function manifest() {
|
||||
return {
|
||||
localCandidateFlows: bundles.map((bundle) => ({
|
||||
bundle,
|
||||
witnessTag: `@local-candidate:${bundle}`,
|
||||
witnessSpec: "client/tests/e2e/synthetic-local-candidate.spec.ts",
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function spec(tag, status = "passed", overrides = {}) {
|
||||
return {
|
||||
title: tag,
|
||||
file: "client/tests/e2e/synthetic-local-candidate.spec.ts",
|
||||
line: 12,
|
||||
tags: [],
|
||||
tests: [
|
||||
{
|
||||
expectedStatus: "passed",
|
||||
results: [{ status }],
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function results(specs, overrides = {}) {
|
||||
return {
|
||||
suites: [{ title: "local candidate", specs }],
|
||||
errors: [],
|
||||
stats: { expected: specs.length, skipped: 0, unexpected: 0 },
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test("10 个唯一 witness tag 全部 passed 时通过", () => {
|
||||
const summary = verifyLocalCandidateResults(
|
||||
manifest(),
|
||||
results(bundles.map((bundle) => spec(`@local-candidate:${bundle}`))),
|
||||
);
|
||||
assert.equal(summary.ok, true);
|
||||
assert.equal(summary.passedTagCount, 10);
|
||||
assert.equal(summary.skippedTagCount, 0);
|
||||
});
|
||||
|
||||
test("结果缺失 tag 必须阻断", () => {
|
||||
const summary = verifyLocalCandidateResults(
|
||||
manifest(),
|
||||
results(bundles.slice(1).map((bundle) => spec(`@local-candidate:${bundle}`))),
|
||||
);
|
||||
assert.equal(summary.ok, false);
|
||||
assert.deepEqual(summary.missingTags, ["@local-candidate:login-logout"]);
|
||||
assert.ok(summary.issues.includes("missing_tag_result"));
|
||||
});
|
||||
|
||||
test("重复 tag 结果必须阻断", () => {
|
||||
const specs = bundles.map((bundle) => spec(`@local-candidate:${bundle}`));
|
||||
specs.push(spec("@local-candidate:login-logout"));
|
||||
const summary = verifyLocalCandidateResults(manifest(), results(specs));
|
||||
assert.equal(summary.ok, false);
|
||||
assert.deepEqual(summary.duplicateTags, ["@local-candidate:login-logout"]);
|
||||
});
|
||||
|
||||
test("未知 tag 结果必须阻断", () => {
|
||||
const specs = bundles.map((bundle) => spec(`@local-candidate:${bundle}`));
|
||||
specs.push(spec("@local-candidate:unknown"));
|
||||
const summary = verifyLocalCandidateResults(manifest(), results(specs));
|
||||
assert.equal(summary.ok, false);
|
||||
assert.deepEqual(summary.unknownTags, ["@local-candidate:unknown"]);
|
||||
});
|
||||
|
||||
test("全部 witness skipped 必须阻断", () => {
|
||||
const summary = verifyLocalCandidateResults(
|
||||
manifest(),
|
||||
results(bundles.map((bundle) => spec(`@local-candidate:${bundle}`, "skipped"))),
|
||||
);
|
||||
assert.equal(summary.ok, false);
|
||||
assert.equal(summary.skippedTagCount, 10);
|
||||
assert.equal(summary.passedTagCount, 0);
|
||||
assert.ok(summary.issues.includes("skipped_tag_result"));
|
||||
});
|
||||
|
||||
test("任一 witness 或整体 Playwright suite 失败必须阻断", () => {
|
||||
const specs = bundles.map((bundle) =>
|
||||
spec(`@local-candidate:${bundle}`, bundle === "payment-ap" ? "failed" : "passed"),
|
||||
);
|
||||
const summary = verifyLocalCandidateResults(
|
||||
manifest(),
|
||||
results(specs, { errors: [{}], stats: { expected: 9, skipped: 0, unexpected: 1 } }),
|
||||
);
|
||||
assert.equal(summary.ok, false);
|
||||
assert.deepEqual(summary.failedTags, ["@local-candidate:payment-ap"]);
|
||||
assert.ok(summary.issues.includes("playwright_suite_failed"));
|
||||
assert.deepEqual(summary.failedTests, [{
|
||||
file: "synthetic-local-candidate.spec.ts",
|
||||
line: 12,
|
||||
title: "@local-candidate:payment-ap",
|
||||
status: "failed",
|
||||
}]);
|
||||
});
|
||||
|
||||
test("非 witness acceptance 用例 skipped 也必须阻断且诊断不泄露绝对路径", () => {
|
||||
const specs = bundles.map((bundle) => spec(`@local-candidate:${bundle}`));
|
||||
specs.push(spec("本地附加验收", "skipped", {
|
||||
file: "/private/workspace/client/tests/e2e/acceptance.spec.ts",
|
||||
line: 27,
|
||||
}));
|
||||
const summary = verifyLocalCandidateResults(
|
||||
manifest(),
|
||||
results(specs, { stats: { expected: 10, skipped: 1, unexpected: 0 } }),
|
||||
);
|
||||
assert.equal(summary.ok, false);
|
||||
assert.ok(summary.issues.includes("playwright_suite_skipped"));
|
||||
assert.equal(summary.suiteSkippedTestCount, 1);
|
||||
assert.deepEqual(summary.skippedTests, [{
|
||||
file: "acceptance.spec.ts",
|
||||
line: 27,
|
||||
title: "本地附加验收",
|
||||
status: "skipped",
|
||||
}]);
|
||||
});
|
||||
|
||||
test("单一测试合并多个 witness tag 不能冒充十个独立流程", () => {
|
||||
const mergedTitle = bundles.map((bundle) => `@local-candidate:${bundle}`).join(" ");
|
||||
const summary = verifyLocalCandidateResults(manifest(), results([spec(mergedTitle)]));
|
||||
assert.equal(summary.ok, false);
|
||||
assert.equal(summary.totalTestResultCount, 1);
|
||||
assert.ok(summary.issues.includes("multi_tag_test_result"));
|
||||
assert.equal(summary.multiTagTests.length, 1);
|
||||
});
|
||||
|
||||
test("witness tag 必须来自 manifest 指定的测试文件", () => {
|
||||
const specs = bundles.map((bundle) => spec(`@local-candidate:${bundle}`));
|
||||
specs[0] = spec("@local-candidate:login-logout", "passed", {
|
||||
file: "/private/workspace/client/tests/e2e/unrelated.spec.ts",
|
||||
});
|
||||
const summary = verifyLocalCandidateResults(manifest(), results(specs));
|
||||
assert.equal(summary.ok, false);
|
||||
assert.ok(summary.issues.includes("wrong_witness_spec_result"));
|
||||
assert.deepEqual(summary.wrongWitnessSpecs, [{
|
||||
tag: "@local-candidate:login-logout",
|
||||
expectedFile: "synthetic-local-candidate.spec.ts",
|
||||
observedFile: "unrelated.spec.ts",
|
||||
}]);
|
||||
});
|
||||
|
||||
test("Playwright JSON 将 file 放在父 suite 时仍能核对 witness 来源", () => {
|
||||
const specs = bundles.map((bundle) => {
|
||||
const item = spec(`@local-candidate:${bundle}`);
|
||||
delete item.file;
|
||||
return item;
|
||||
});
|
||||
const report = results([], {
|
||||
suites: [{
|
||||
title: "local candidate witnesses",
|
||||
file: "tests/e2e/synthetic-local-candidate.spec.ts",
|
||||
specs,
|
||||
}],
|
||||
});
|
||||
const summary = verifyLocalCandidateResults(manifest(), report);
|
||||
assert.equal(summary.ok, true);
|
||||
assert.deepEqual(summary.wrongWitnessSpecs, []);
|
||||
});
|
||||
|
||||
test("Playwright JSON 只提供 spec basename 时按 manifest 唯一 basename 核对", () => {
|
||||
const specs = bundles.map((bundle) => spec(`@local-candidate:${bundle}`, "passed", {
|
||||
file: "synthetic-local-candidate.spec.ts",
|
||||
}));
|
||||
const summary = verifyLocalCandidateResults(manifest(), results(specs));
|
||||
assert.equal(summary.ok, true);
|
||||
assert.deepEqual(summary.wrongWitnessSpecs, []);
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户