WEXT1F 增加正式模板外出加班取证门禁
这个提交包含在:
@@ -77,7 +77,8 @@ describe("check-wecom-approval-archive", () => {
|
||||
status: "approved",
|
||||
limit: 50,
|
||||
});
|
||||
expect(() => parseArgs(["--template=outing"])).toThrow(/Unsupported --template/);
|
||||
expect(parseArgs(["--template=outing", "--template=overtime"]).templates).toEqual(["outing", "overtime"]);
|
||||
expect(() => parseArgs(["--template=unknown"])).toThrow(/Unsupported --template/);
|
||||
expect(() => parseArgs(["--limit=101"])).toThrow(/--limit/);
|
||||
});
|
||||
|
||||
|
||||
@@ -89,8 +89,11 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
"--size=50",
|
||||
"--template=leave:tpl-leave",
|
||||
"--template=missed_punch:tpl-missed",
|
||||
"--target=leave",
|
||||
"--targets=missed_punch",
|
||||
"--discover-template-ids",
|
||||
"--discover-applicant-employee-no=SMOKE002",
|
||||
"--filter-creator-employee-no=SMOKE002",
|
||||
]);
|
||||
|
||||
expect(options.live).toBe(true);
|
||||
@@ -98,12 +101,16 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
expect(options.size).toBe(50);
|
||||
expect(options.discoverTemplateIds).toBe(true);
|
||||
expect(options.discoverApplicantEmployeeNo).toBe("SMOKE002");
|
||||
expect(options.filterCreatorEmployeeNo).toBe("SMOKE002");
|
||||
expect(options.persistToDb).toBe(false);
|
||||
expect(options.targets).toEqual(["leave", "missed_punch"]);
|
||||
expect(options.templates).toEqual({
|
||||
leave: "tpl-leave",
|
||||
missed_punch: "tpl-missed",
|
||||
});
|
||||
expect(() => parseArgs(["--template=outing:tpl"])).toThrow(/unsupported target/);
|
||||
expect(parseArgs(["--target=outing", "--targets=overtime"]).targets).toEqual(["outing", "overtime"]);
|
||||
expect(parseArgs(["--template=outing:tpl"]).templates).toMatchObject({ outing: "tpl" });
|
||||
expect(() => parseArgs(["--target=unknown"])).toThrow(/unsupported target/);
|
||||
expect(() => parseArgs(["--size=101"])).toThrow(/--size/);
|
||||
expect(parseArgs(["--persist-to-db"]).persistToDb).toBe(true);
|
||||
});
|
||||
@@ -133,6 +140,29 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("builds explicit WEXT1-FORMAL outing and overtime inputs without changing defaults", () => {
|
||||
const defaultInputs = buildInputs(baseOptions({ templates: {} }), {});
|
||||
expect(defaultInputs.map((item) => item.key)).toEqual(["leave", "missed_punch"]);
|
||||
|
||||
const inputs = buildInputs(baseOptions({
|
||||
targets: ["outing", "overtime"],
|
||||
templates: { outing: "cli-outing-template" },
|
||||
}), {
|
||||
WECOM_APPROVAL_OUTING_TEMPLATE_ID: "env-outing-template",
|
||||
WECOM_APPROVAL_OVERTIME_TEMPLATE_ID: "env-overtime-template",
|
||||
});
|
||||
|
||||
expect(inputs.map((item) => item.key)).toEqual(["outing", "overtime"]);
|
||||
expect(inputs.find((item) => item.key === "outing")).toMatchObject({
|
||||
templateSource: "cli",
|
||||
templateIdPrefix: maskApprovalIdentifier("cli-outing-template"),
|
||||
});
|
||||
expect(inputs.find((item) => item.key === "overtime")).toMatchObject({
|
||||
templateSource: "WECOM_APPROVAL_OVERTIME_TEMPLATE_ID",
|
||||
templateIdPrefix: maskApprovalIdentifier("env-overtime-template"),
|
||||
});
|
||||
});
|
||||
|
||||
it("documents WECOM_AGENT_SECRET as the only Secret source", () => {
|
||||
const check = buildSecretCheck(baseConfig());
|
||||
expect(check).toMatchObject({
|
||||
@@ -227,6 +257,8 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
it("auto-discovers missing template IDs by applicant creator without leaking full identifiers", async () => {
|
||||
const discoveredLeaveTemplateId = "tpllv-auto-discovered-leave-sensitive";
|
||||
const discoveredMissedTemplateId = "tplmp-auto-discovered-missed-sensitive";
|
||||
const discoveredOutingTemplateId = "tplout-auto-discovered-outing-sensitive";
|
||||
const discoveredOvertimeTemplateId = "tplot-auto-discovered-overtime-sensitive";
|
||||
const fetchApprovalInfoListImpl = vi.fn(async ({ filters }) => {
|
||||
if (filters[0].key === "creator") {
|
||||
return {
|
||||
@@ -234,10 +266,15 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
tokenSource: "WECOM_AGENT_SECRET",
|
||||
response: {
|
||||
errcode: 0,
|
||||
spNoCount: 2,
|
||||
spNoPrefixes: [maskApprovalIdentifier("sp-discovery-leave"), maskApprovalIdentifier("sp-discovery-missed")],
|
||||
spNoCount: 4,
|
||||
spNoPrefixes: [
|
||||
maskApprovalIdentifier("sp-discovery-leave"),
|
||||
maskApprovalIdentifier("sp-discovery-missed"),
|
||||
maskApprovalIdentifier("sp-discovery-outing"),
|
||||
maskApprovalIdentifier("sp-discovery-overtime"),
|
||||
],
|
||||
},
|
||||
spNos: ["sp-discovery-leave", "sp-discovery-missed"],
|
||||
spNos: ["sp-discovery-leave", "sp-discovery-missed", "sp-discovery-outing", "sp-discovery-overtime"],
|
||||
nextCursor: "",
|
||||
};
|
||||
}
|
||||
@@ -274,13 +311,37 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
response: { errcode: 0, spStatus: "2" },
|
||||
};
|
||||
}
|
||||
if (spNo === "sp-discovery-outing") {
|
||||
return {
|
||||
ok: true,
|
||||
tokenSource: "WECOM_AGENT_SECRET",
|
||||
templateId: discoveredOutingTemplateId,
|
||||
spName: "外出",
|
||||
summary: detailSummary(spNo, { templateIdPrefix: maskApprovalIdentifier(discoveredOutingTemplateId) }),
|
||||
response: { errcode: 0, spStatus: "2" },
|
||||
};
|
||||
}
|
||||
if (spNo === "sp-discovery-overtime") {
|
||||
return {
|
||||
ok: true,
|
||||
tokenSource: "WECOM_AGENT_SECRET",
|
||||
templateId: discoveredOvertimeTemplateId,
|
||||
spName: "加班",
|
||||
summary: detailSummary(spNo, { templateIdPrefix: maskApprovalIdentifier(discoveredOvertimeTemplateId) }),
|
||||
response: { errcode: 0, spStatus: "2" },
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
tokenSource: "WECOM_AGENT_SECRET",
|
||||
summary: detailSummary(spNo, {
|
||||
templateIdPrefix: spNo.startsWith(discoveredLeaveTemplateId)
|
||||
? maskApprovalIdentifier(discoveredLeaveTemplateId)
|
||||
: maskApprovalIdentifier(discoveredMissedTemplateId),
|
||||
: spNo.startsWith(discoveredMissedTemplateId)
|
||||
? maskApprovalIdentifier(discoveredMissedTemplateId)
|
||||
: spNo.startsWith(discoveredOutingTemplateId)
|
||||
? maskApprovalIdentifier(discoveredOutingTemplateId)
|
||||
: maskApprovalIdentifier(discoveredOvertimeTemplateId),
|
||||
}),
|
||||
response: { errcode: 0, spStatus: "2" },
|
||||
};
|
||||
@@ -288,6 +349,7 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
|
||||
const report = await executeCompensationPoc(baseOptions({
|
||||
templates: {},
|
||||
targets: ["leave", "missed_punch", "outing", "overtime"],
|
||||
discoverTemplateIds: true,
|
||||
discoverApplicantEmployeeNo: "SMOKE002",
|
||||
}), {
|
||||
@@ -315,6 +377,18 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
statusLabel: "已通过",
|
||||
spNoPrefix: maskApprovalIdentifier(`${discoveredMissedTemplateId}-sp`),
|
||||
},
|
||||
{
|
||||
callbackKind: "approval_status_change",
|
||||
spStatus: "2",
|
||||
statusLabel: "已通过",
|
||||
spNoPrefix: maskApprovalIdentifier(`${discoveredOutingTemplateId}-sp`),
|
||||
},
|
||||
{
|
||||
callbackKind: "approval_status_change",
|
||||
spStatus: "2",
|
||||
statusLabel: "已通过",
|
||||
spNoPrefix: maskApprovalIdentifier(`${discoveredOvertimeTemplateId}-sp`),
|
||||
},
|
||||
]),
|
||||
});
|
||||
|
||||
@@ -328,13 +402,23 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
expect(fetchApprovalInfoListImpl.mock.calls[2][0].filters).toEqual([
|
||||
{ key: "template_id", value: discoveredMissedTemplateId },
|
||||
]);
|
||||
expect(fetchApprovalInfoListImpl.mock.calls[3][0].filters).toEqual([
|
||||
{ key: "template_id", value: discoveredOutingTemplateId },
|
||||
]);
|
||||
expect(fetchApprovalInfoListImpl.mock.calls[4][0].filters).toEqual([
|
||||
{ key: "template_id", value: discoveredOvertimeTemplateId },
|
||||
]);
|
||||
const serialized = JSON.stringify(report);
|
||||
expect(serialized).not.toContain(discoveredLeaveTemplateId);
|
||||
expect(serialized).not.toContain(discoveredMissedTemplateId);
|
||||
expect(serialized).not.toContain(discoveredOutingTemplateId);
|
||||
expect(serialized).not.toContain(discoveredOvertimeTemplateId);
|
||||
expect(serialized).not.toContain(SENSITIVE_USER_ID);
|
||||
expect(report.checks.templateDiscovery.discoveredTemplatePrefixes).toEqual({
|
||||
leave: maskApprovalIdentifier(discoveredLeaveTemplateId),
|
||||
missed_punch: maskApprovalIdentifier(discoveredMissedTemplateId),
|
||||
outing: maskApprovalIdentifier(discoveredOutingTemplateId),
|
||||
overtime: maskApprovalIdentifier(discoveredOvertimeTemplateId),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -402,6 +486,107 @@ describe("check-wecom-approval-compensation-poc", () => {
|
||||
expect(report.checks.archivePersistence.status).toBe("SKIPPED");
|
||||
});
|
||||
|
||||
it("filters WEXT1-FORMAL template pulls by creator employee to avoid broad formal-template reads", async () => {
|
||||
const creatorUserId = "formal_creator_userid_should_not_leak";
|
||||
const outingTemplateId = "outing-template-sensitive";
|
||||
const overtimeTemplateId = "overtime-template-sensitive";
|
||||
const fetchApprovalInfoListImpl = vi.fn(async ({ filters }) => {
|
||||
expect(filters).toHaveLength(2);
|
||||
expect(filters[0].key).toBe("template_id");
|
||||
expect(filters[1]).toEqual({ key: "creator", value: creatorUserId });
|
||||
return {
|
||||
ok: true,
|
||||
tokenSource: "WECOM_AGENT_SECRET",
|
||||
response: { errcode: 0, spNoCount: 1, spNoPrefixes: [maskApprovalIdentifier(`${filters[0].value}-sp`)] },
|
||||
spNos: [`${filters[0].value}-sp`],
|
||||
nextCursor: "",
|
||||
};
|
||||
});
|
||||
const fetchApprovalDetailImpl = vi.fn(async ({ spNo }) => ({
|
||||
ok: true,
|
||||
tokenSource: "WECOM_AGENT_SECRET",
|
||||
summary: detailSummary(spNo, { spStatus: "2" }),
|
||||
response: { errcode: 0, spStatus: "2" },
|
||||
}));
|
||||
|
||||
const report = await executeCompensationPoc(baseOptions({
|
||||
targets: ["outing", "overtime"],
|
||||
templates: {
|
||||
outing: outingTemplateId,
|
||||
overtime: overtimeTemplateId,
|
||||
},
|
||||
filterCreatorEmployeeNo: "SMOKE002",
|
||||
}), {
|
||||
config: baseConfig(),
|
||||
env: {},
|
||||
generatedAt: "2026-05-20T00:00:00.000Z",
|
||||
queryApplicantActorImpl: vi.fn(async () => ({
|
||||
employeeNo: "SMOKE002",
|
||||
status: "active",
|
||||
archived: false,
|
||||
wecomExternalUserId: creatorUserId,
|
||||
})),
|
||||
fetchApprovalInfoListImpl,
|
||||
fetchApprovalDetailImpl,
|
||||
readCallbackEvidenceImpl: vi.fn(async () => [
|
||||
{
|
||||
callbackKind: "approval_status_change",
|
||||
spStatus: "2",
|
||||
statusLabel: "已通过",
|
||||
spNoPrefix: maskApprovalIdentifier(`${outingTemplateId}-sp`),
|
||||
},
|
||||
{
|
||||
callbackKind: "approval_status_change",
|
||||
spStatus: "2",
|
||||
statusLabel: "已通过",
|
||||
spNoPrefix: maskApprovalIdentifier(`${overtimeTemplateId}-sp`),
|
||||
},
|
||||
]),
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PASS_COMPENSATION_POC");
|
||||
expect(report.checks.creatorFilter).toMatchObject({
|
||||
status: "OK",
|
||||
employeeNo: "SMOKE002",
|
||||
userIdPrefix: maskApprovalIdentifier(creatorUserId),
|
||||
});
|
||||
expect(fetchApprovalInfoListImpl).toHaveBeenCalledTimes(2);
|
||||
const serialized = JSON.stringify(report);
|
||||
expect(serialized).not.toContain(creatorUserId);
|
||||
expect(serialized).not.toContain(outingTemplateId);
|
||||
expect(serialized).not.toContain(overtimeTemplateId);
|
||||
});
|
||||
|
||||
it("does not pull formal templates when the requested creator filter cannot be resolved", async () => {
|
||||
const fetchApprovalInfoListImpl = vi.fn();
|
||||
const report = await executeCompensationPoc(baseOptions({
|
||||
targets: ["outing"],
|
||||
templates: { outing: "formal-outing-template-sensitive" },
|
||||
filterCreatorEmployeeNo: "SMOKE002",
|
||||
}), {
|
||||
config: baseConfig(),
|
||||
env: {},
|
||||
generatedAt: "2026-05-20T00:00:00.000Z",
|
||||
queryApplicantActorImpl: vi.fn(async () => ({
|
||||
employeeNo: "SMOKE002",
|
||||
status: "active",
|
||||
archived: false,
|
||||
wecomExternalUserId: "",
|
||||
})),
|
||||
fetchApprovalInfoListImpl,
|
||||
fetchApprovalDetailImpl: vi.fn(),
|
||||
readCallbackEvidenceImpl: vi.fn(async () => []),
|
||||
});
|
||||
|
||||
expect(report.status).toBe("PARTIAL_COMPENSATION_NEEDS_CONFIG");
|
||||
expect(fetchApprovalInfoListImpl).not.toHaveBeenCalled();
|
||||
expect(report.checks.targets[0]).toMatchObject({
|
||||
target: "outing",
|
||||
status: "NEEDS_CONFIG",
|
||||
reason: "creator_filter_not_ready",
|
||||
});
|
||||
});
|
||||
|
||||
it("requires both --persist-to-db and WECOM_ARCHIVE_WRITE=1 before OA archive writes", async () => {
|
||||
const report = await executeCompensationPoc(baseOptions({
|
||||
live: true,
|
||||
|
||||
在新工单中引用
屏蔽一个用户