fix: keep approval batching internal
CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s
CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s
This commit is contained in:
+1
-1
@@ -193,7 +193,7 @@ test("MCP approval_login returns both portable text and a standard resource link
|
||||
assert.equal((history.inputSchema as any).properties.endDate.default, "");
|
||||
const batch = tools.tools.find((tool) => tool.name === "approval_process_batch")!;
|
||||
assert.equal((batch.inputSchema as any).properties.items.maxItems, 20);
|
||||
assert.match(batch.description ?? "", /最多 20 条.*零写入/s);
|
||||
assert.match(batch.description ?? "", /Schema容量.*不向用户解释.*任一无效则零写入/s);
|
||||
} finally {
|
||||
await client.close();
|
||||
await server.close();
|
||||
|
||||
@@ -43,7 +43,8 @@ test("MCP initialization teaches the workflow while tools/list stays compact", a
|
||||
assert.match(client.getInstructions() ?? "", /非必填字段.*是否补充/s);
|
||||
assert.match(client.getInstructions() ?? "", /出差申请.*外出.*业务招待申请.*差旅费用报销.*业务招待费用报销/s);
|
||||
assert.match(client.getInstructions() ?? "", /revokeId.*approval_revoke_submission/);
|
||||
assert.match(client.getInstructions() ?? "", /最多(?:处理 )?20 条.*零写入.*禁止自动.*重试/s);
|
||||
assert.match(client.getInstructions() ?? "", /单次条数限制.*内部调用细节.*绝不向用户.*一键全选.*如何分组/s);
|
||||
assert.match(client.getInstructions() ?? "", /用户只是在查看.*业务信息.*不主动讨论.*自动分批.*不得造成重复确认/s);
|
||||
assert.match(client.getInstructions() ?? "", /近30天.*allowDuplicateInvoices=true/s);
|
||||
|
||||
const tools = await client.listTools();
|
||||
@@ -73,7 +74,7 @@ test("MCP initialization teaches the workflow while tools/list stays compact", a
|
||||
assert.match(historySchema.properties.startDate.description, /最近30天/);
|
||||
assert.match(descriptions.approval_process_batch ?? "", /用户明确.*不二次确认/);
|
||||
assert.match(descriptions.approval_process_batch ?? "", /succeeded.*completed.*followUpRequired/);
|
||||
assert.match(descriptions.approval_process_batch ?? "", /最多 20 条.*任一无效.*零写入/s);
|
||||
assert.match(descriptions.approval_process_batch ?? "", /Schema容量.*不得转述给用户.*任一无效.*零写入/s);
|
||||
assert.match(descriptions.approval_prepare_submission ?? "", /明确确认|确认/);
|
||||
assert.match(descriptions.approval_get_submission_schema ?? "", /必填字段齐全.*可选字段/);
|
||||
assert.match(descriptions.approval_get_submission_options ?? "", /承担部门.*城市编码/);
|
||||
|
||||
+11
-9
@@ -209,7 +209,7 @@ test("selectionId processes exactly the confirmed live result set and rejects dr
|
||||
assert.equal(writes, 2);
|
||||
});
|
||||
|
||||
test("todo lists explicitly describe the fixed 20-item batch policy", async () => {
|
||||
test("todo lists keep transport batch policy internal", async () => {
|
||||
const user = { userId: "user-1" };
|
||||
const manyDing = {
|
||||
async listMyTodos() {
|
||||
@@ -219,21 +219,23 @@ test("todo lists explicitly describe the fixed 20-item batch policy", async () =
|
||||
},
|
||||
} as unknown as ApprovalService;
|
||||
const overflow = await new UnifiedApprovalService({ ...config(), batchLimit: 99 }, manyDing).listTodos(user, "dingtalk", 30);
|
||||
assert.deepEqual(overflow.batchPolicy, {
|
||||
maxItemsPerCall: 20,
|
||||
selectionAvailable: false,
|
||||
selectionUnavailableReason: "result_exceeds_batch_limit",
|
||||
overflowGuidance: "一次最多处理 20 条。超过时可从同一次列表结果中选取 approvalId 分组提交;每组仍须经用户明确授权。",
|
||||
});
|
||||
assert.equal(overflow.returned, 21);
|
||||
assert.equal("selectionId" in overflow, false);
|
||||
assert.equal("selectionUnavailableReason" in overflow, false);
|
||||
assert.equal("selectionHint" in overflow, false);
|
||||
assert.equal("batchPolicy" in overflow, false);
|
||||
|
||||
const emptyDing = { async listMyTodos() { return { items: [] }; } } as unknown as ApprovalService;
|
||||
const empty = await new UnifiedApprovalService(config(), emptyDing).listTodos(user, "dingtalk", 10);
|
||||
assert.equal(empty.batchPolicy.selectionUnavailableReason, "empty_result");
|
||||
assert.equal("selectionId" in empty, false);
|
||||
assert.equal("selectionUnavailableReason" in empty, false);
|
||||
assert.equal("batchPolicy" in empty, false);
|
||||
|
||||
const xftUnavailable = { async listMyTodos() { throw new Error("XFT unavailable"); } };
|
||||
const partial = await new UnifiedApprovalService(config(), manyDing, xftUnavailable as never).listTodos(user, "all", 1);
|
||||
assert.equal(partial.batchPolicy.selectionUnavailableReason, "requested_source_unhealthy");
|
||||
assert.equal("selectionId" in partial, false);
|
||||
assert.equal("selectionUnavailableReason" in partial, false);
|
||||
assert.equal("batchPolicy" in partial, false);
|
||||
});
|
||||
|
||||
test("combined todo lists are deterministic regardless of source response timing", async () => {
|
||||
|
||||
Reference in New Issue
Block a user