返回 CodeWhale
lib.test.mjs
根目录 / integrations / wecom-bridge / test / lib.test.mjs
1 import assert from "node:assert/strict";
2 import { mkdtemp, rm, stat } from "node:fs/promises";
3 import { tmpdir } from "node:os";
4 import path from "node:path";
5 import test from "node:test";
6
7 import { ThreadStore, validateBridgeConfig, isApprovalResponse, isDenyResponse } from "../src/lib.mjs";
8
9 // ─── ThreadStore ─────────────────────────────────────────────────
10
11 test("ThreadStore writes private state files", async () => {
12 const dir = await mkdtemp(path.join(tmpdir(), "codewhale-wecom-"));
13 try {
14 const statePath = path.join(dir, "nested", "thread-map.json");
15 const store = await ThreadStore.open(statePath);
16
17 await store.setChat("single:user-a", {
18 threadId: "thread-a",
19 lastSeq: 1,
20 activeTurnId: null
21 });
22
23 const saved = await ThreadStore.open(statePath);
24 assert.equal((await saved.getChat("single:user-a")).threadId, "thread-a");
25
26 if (process.platform !== "win32") {
27 assert.equal((await stat(path.dirname(statePath))).mode & 0o777, 0o700);
28 assert.equal((await stat(statePath)).mode & 0o777, 0o600);
29 }
30 } finally {
31 await rm(dir, { recursive: true, force: true });
32 }
33 });
34
35 // ─── Config validation ──────────────────────────────────────────
36
37 test("validateBridgeConfig rejects placeholder secrets", () => {
38 const result = validateBridgeConfig({
39 WECOM_BOT_ID: "your-bot-id",
40 WECOM_BOT_SECRET: "your-bot-secret",
41 CODEWHALE_RUNTIME_TOKEN: "replace-with-long-random-token",
42 CODEWHALE_RUNTIME_URL: "http://127.0.0.1:7878"
43 });
44
45 assert.equal(result.ok, false);
46 assert.deepEqual(
47 result.errors.map((item) => item.code),
48 ["placeholder_runtime_token"]
49 );
50 });
51
52 // ─── isApprovalResponse ─────────────────────────────────────────
53
54 test("isApprovalResponse: single-word Chinese keywords", () => {
55 for (const kw of ["允许", "可以", "好", "同意", "批准"]) {
56 assert.equal(isApprovalResponse(kw), true, `"${kw}" should be approval`);
57 }
58 });
59
60 test("isApprovalResponse: single-word English keywords", () => {
61 for (const kw of ["yes", "ok", "y", "approve", "allow"]) {
62 assert.equal(isApprovalResponse(kw), true, `"${kw}" should be approval`);
63 }
64 });
65
66 test("isApprovalResponse: two-character Chinese keywords", () => {
67 for (const kw of ["好的", "可以", "没问题", "批准了", "同意"]) {
68 assert.equal(isApprovalResponse(kw), true, `"${kw}" should be approval`);
69 }
70 });
71
72 test("isApprovalResponse: case-insensitive", () => {
73 assert.equal(isApprovalResponse("YES"), true);
74 assert.equal(isApprovalResponse("Ok"), true);
75 assert.equal(isApprovalResponse("Allow"), true);
76 });
77
78 test("isApprovalResponse: trims whitespace", () => {
79 assert.equal(isApprovalResponse(" 允许 "), true);
80 assert.equal(isApprovalResponse(" yes "), true);
81 });
82
83 test("isApprovalResponse: rejects non-approval text", () => {
84 assert.equal(isApprovalResponse("帮我查一下"), false);
85 assert.equal(isApprovalResponse("/allow abc123"), false);
86 assert.equal(isApprovalResponse("run prompt"), false);
87 });
88
89 test("isApprovalResponse: handles null/empty/undefined", () => {
90 assert.equal(isApprovalResponse(null), false);
91 assert.equal(isApprovalResponse(""), false);
92 assert.equal(isApprovalResponse(undefined), false);
93 });
94
95 // ─── isDenyResponse ─────────────────────────────────────────────
96
97 test("isDenyResponse: single-word Chinese keywords", () => {
98 for (const kw of ["拒绝", "不行", "不要", "取消", "否"]) {
99 assert.equal(isDenyResponse(kw), true, `"${kw}" should be denial`);
100 }
101 });
102
103 test("isDenyResponse: single-word English keywords", () => {
104 for (const kw of ["no", "n", "deny", "reject", "stop"]) {
105 assert.equal(isDenyResponse(kw), true, `"${kw}" should be denial`);
106 }
107 });
108
109 test("isDenyResponse: two-character Chinese keywords", () => {
110 for (const kw of ["不可以", "不同意", "不要执行"]) {
111 assert.equal(isDenyResponse(kw), true, `"${kw}" should be denial`);
112 }
113 });
114
115 test("isDenyResponse: case-insensitive and trims whitespace", () => {
116 assert.equal(isDenyResponse(" NO "), true);
117 assert.equal(isDenyResponse("No"), true);
118 });
119
120 test("isDenyResponse: rejects non-denial text", () => {
121 assert.equal(isDenyResponse("让我想想"), false);
122 assert.equal(isDenyResponse("/deny abc123"), false);
123 assert.equal(isDenyResponse("继续"), false);
124 });
125
126 test("isDenyResponse: handles null/empty/undefined", () => {
127 assert.equal(isDenyResponse(null), false);
128 assert.equal(isDenyResponse(""), false);
129 assert.equal(isDenyResponse(undefined), false);
130 });
131
132 // ─── Mutual exclusivity ─────────────────────────────────────────
133
134 test("no keyword is both approval and denial", () => {
135 const all = [
136 "允许", "可以", "好", "同意", "批准",
137 "好的", "没问题", "批准了",
138 "yes", "ok", "y", "approve", "allow",
139 "拒绝", "不行", "不要", "取消", "否",
140 "不可以", "不同意", "不要执行",
141 "no", "n", "deny", "reject", "stop"
142 ];
143 for (const kw of all) {
144 assert.notEqual(
145 isApprovalResponse(kw), isDenyResponse(kw),
146 `"${kw}" should not be both approval and denial`
147 );
148 }
149 });
150
150 lines Plain Text