chatccc 0.2.63 → 0.2.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.sample.json +2 -3
- package/im-skills/feishu-skill/download-video.mjs +10 -17
- package/im-skills/wechat-file-skill/receive-send-file.md +39 -42
- package/im-skills/wechat-file-skill/send-file.mjs +84 -101
- package/im-skills/wechat-file-skill/skill.md +11 -11
- package/im-skills/wechat-image-skill/receive-send-image.md +5 -5
- package/im-skills/wechat-image-skill/send-image.mjs +2 -6
- package/im-skills/wechat-image-skill/skill.md +4 -4
- package/im-skills/wechat-video-skill/receive-send-video.md +39 -41
- package/im-skills/wechat-video-skill/send-video.mjs +80 -84
- package/im-skills/wechat-video-skill/skill.md +11 -11
- package/package.json +59 -59
- package/src/__tests__/agent-platform-routing.test.ts +26 -0
- package/src/__tests__/claude-adapter.test.ts +48 -48
- package/src/__tests__/config-reload.test.ts +14 -14
- package/src/__tests__/config-sample.test.ts +22 -22
- package/src/__tests__/im-skills.test.ts +49 -50
- package/src/__tests__/orchestrator.test.ts +1 -1
- package/src/__tests__/privacy.test.ts +142 -142
- package/src/__tests__/simplify.test.ts +282 -282
- package/src/__tests__/web-ui.test.ts +23 -23
- package/src/__tests__/wechat-platform.test.ts +0 -65
- package/src/adapters/claude-adapter.ts +40 -40
- package/src/agent-file-rpc.ts +19 -4
- package/src/agent-image-rpc.ts +19 -4
- package/src/agent-platform-routing.ts +28 -0
- package/src/config.ts +2 -24
- package/src/im-skills.ts +9 -0
- package/src/index.ts +11 -6
- package/src/orchestrator.ts +3 -4
- package/src/privacy.ts +67 -67
- package/src/session.ts +15 -3
- package/src/simplify.ts +119 -119
- package/src/web-ui.ts +3 -26
- package/src/wechat-platform.ts +68 -151
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdtemp, mkdir,
|
|
1
|
+
import { mkdtemp, mkdir, rm, writeFile, readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { afterEach, describe, expect, it } from "vitest";
|
|
@@ -44,58 +44,57 @@ describe("IM skills prompt rendering", () => {
|
|
|
44
44
|
expect(prompt).not.toContain("{{");
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
it("
|
|
48
|
-
tempRoot = await mkdtemp(join(tmpdir(), "chatccc-im-skills-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
it("filters skills by enabledSkillNames", async () => {
|
|
48
|
+
tempRoot = await mkdtemp(join(tmpdir(), "chatccc-im-skills-filter-"));
|
|
49
|
+
const feishuDir = join(tempRoot, "feishu-skill");
|
|
50
|
+
const wechatDir = join(tempRoot, "wechat-skill");
|
|
51
|
+
const outDir = join(tempRoot, "out");
|
|
52
|
+
await mkdir(feishuDir);
|
|
53
|
+
await mkdir(wechatDir);
|
|
54
|
+
await writeFile(join(feishuDir, "skill.md"), "Feishu {{cwd}}", "utf-8");
|
|
55
|
+
await writeFile(join(feishuDir, "receive-send-image.md"), "Feishu doc", "utf-8");
|
|
56
|
+
await writeFile(join(wechatDir, "skill.md"), "WeChat {{cwd}}", "utf-8");
|
|
57
|
+
await writeFile(join(wechatDir, "receive-send-image.md"), "WeChat doc", "utf-8");
|
|
58
58
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
wechat_send_video_script: "C:/work/im-skills/wechat-video-skill/send-video.mjs",
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
tempRoot,
|
|
70
|
-
);
|
|
59
|
+
const input = {
|
|
60
|
+
skillsDir: tempRoot,
|
|
61
|
+
enabledSkillNames: ["feishu-skill"],
|
|
62
|
+
variables: { cwd: "C:/work" },
|
|
63
|
+
};
|
|
64
|
+
const prompt = await buildImSkillsPrompt(input);
|
|
65
|
+
const exported = await exportSkillSubDocs(input, outDir);
|
|
71
66
|
|
|
72
|
-
expect(prompt).toContain("[ChatCCC IM skill:
|
|
73
|
-
expect(prompt).toContain("
|
|
74
|
-
expect(prompt).toContain("[ChatCCC IM skill: wechat-video-skill]");
|
|
67
|
+
expect(prompt).toContain("[ChatCCC IM skill: feishu-skill]");
|
|
68
|
+
expect(prompt).toContain("Feishu C:/work");
|
|
75
69
|
expect(prompt).not.toContain("[ChatCCC IM skill: wechat-skill]");
|
|
76
|
-
expect(prompt).toContain("
|
|
77
|
-
expect(
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
80
|
-
|
|
81
|
-
expect(prompt).toContain("Send videos");
|
|
82
|
-
expect(prompt).toContain("receive-send-image.md");
|
|
83
|
-
expect(prompt).toContain("receive-send-file.md");
|
|
84
|
-
expect(prompt).toContain("receive-send-video.md");
|
|
85
|
-
expect(prompt).not.toContain("{{wechat_send_image_script}}");
|
|
86
|
-
expect(prompt).not.toContain("{{wechat_send_file_script}}");
|
|
87
|
-
expect(prompt).not.toContain("{{wechat_send_video_script}}");
|
|
88
|
-
expect(exported.some((file) => file.endsWith(join("wechat-image-skill", "receive-send-image.md")))).toBe(true);
|
|
89
|
-
expect(exported.some((file) => file.endsWith(join("wechat-file-skill", "receive-send-file.md")))).toBe(true);
|
|
90
|
-
expect(exported.some((file) => file.endsWith(join("wechat-video-skill", "receive-send-video.md")))).toBe(true);
|
|
91
|
-
await expect(readFile(join(tempRoot, "wechat-image-skill", "receive-send-image.md"), "utf8")).resolves.toContain(
|
|
92
|
-
"send-image.mjs",
|
|
93
|
-
);
|
|
94
|
-
await expect(readFile(join(tempRoot, "wechat-file-skill", "receive-send-file.md"), "utf8")).resolves.toContain(
|
|
95
|
-
"send-file.mjs",
|
|
96
|
-
);
|
|
97
|
-
await expect(readFile(join(tempRoot, "wechat-video-skill", "receive-send-video.md"), "utf8")).resolves.toContain(
|
|
98
|
-
"send-video.mjs",
|
|
70
|
+
expect(prompt).not.toContain("WeChat C:/work");
|
|
71
|
+
expect(exported).toHaveLength(1);
|
|
72
|
+
expect(exported[0]).toContain(join("feishu-skill", "receive-send-image.md"));
|
|
73
|
+
await expect(readFile(join(outDir, "feishu-skill", "receive-send-image.md"), "utf8")).resolves.toContain(
|
|
74
|
+
"Feishu doc",
|
|
99
75
|
);
|
|
100
76
|
});
|
|
77
|
+
|
|
78
|
+
it("skips filtering when enabledSkillNames is omitted", async () => {
|
|
79
|
+
tempRoot = await mkdtemp(join(tmpdir(), "chatccc-im-skills-all-"));
|
|
80
|
+
const feishuDir = join(tempRoot, "feishu-skill");
|
|
81
|
+
const wechatDir = join(tempRoot, "wechat-skill");
|
|
82
|
+
const outDir = join(tempRoot, "out");
|
|
83
|
+
await mkdir(feishuDir);
|
|
84
|
+
await mkdir(wechatDir);
|
|
85
|
+
await writeFile(join(feishuDir, "skill.md"), "Feishu {{cwd}}", "utf-8");
|
|
86
|
+
await writeFile(join(feishuDir, "receive-send-image.md"), "Feishu doc", "utf-8");
|
|
87
|
+
await writeFile(join(wechatDir, "skill.md"), "WeChat {{cwd}}", "utf-8");
|
|
88
|
+
await writeFile(join(wechatDir, "receive-send-image.md"), "WeChat doc", "utf-8");
|
|
89
|
+
|
|
90
|
+
const prompt = await buildImSkillsPrompt({
|
|
91
|
+
skillsDir: tempRoot,
|
|
92
|
+
variables: { cwd: "C:/work" },
|
|
93
|
+
});
|
|
94
|
+
const exported = await exportSkillSubDocs({ skillsDir: tempRoot, variables: { cwd: "C:/work" } }, outDir);
|
|
95
|
+
|
|
96
|
+
expect(prompt).toContain("[ChatCCC IM skill: feishu-skill]");
|
|
97
|
+
expect(prompt).toContain("[ChatCCC IM skill: wechat-skill]");
|
|
98
|
+
expect(exported.length).toBeGreaterThanOrEqual(2);
|
|
99
|
+
});
|
|
101
100
|
});
|
|
@@ -1,143 +1,143 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach, afterAll, vi } from "vitest";
|
|
2
|
-
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
// 在 import privacy 之前 mock config,让 USER_DATA_DIR 指向临时目录
|
|
7
|
-
const TEST_DATA_DIR = await mkdtemp(join(tmpdir(), "chatccc-privacy-test-"));
|
|
8
|
-
vi.mock("../config.ts", async () => {
|
|
9
|
-
const actual = await vi.importActual<typeof import("../config.ts")>("../config.ts");
|
|
10
|
-
return {
|
|
11
|
-
...actual,
|
|
12
|
-
USER_DATA_DIR: TEST_DATA_DIR,
|
|
13
|
-
ts: () => "test-ts",
|
|
14
|
-
};
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
let applyPrivacy: (text: string) => string;
|
|
18
|
-
let reloadPrivacyRules: () => void;
|
|
19
|
-
let getPrivacyRules: () => Record<string, string>;
|
|
20
|
-
|
|
21
|
-
beforeEach(async () => {
|
|
22
|
-
vi.resetModules();
|
|
23
|
-
// 清理临时目录中的 privacy.json
|
|
24
|
-
try {
|
|
25
|
-
await rm(join(TEST_DATA_DIR, "privacy.json"), { force: true });
|
|
26
|
-
} catch {}
|
|
27
|
-
const mod = await import("../privacy.ts");
|
|
28
|
-
applyPrivacy = mod.applyPrivacy;
|
|
29
|
-
reloadPrivacyRules = mod.reloadPrivacyRules;
|
|
30
|
-
getPrivacyRules = mod.getPrivacyRules;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterEach(async () => {
|
|
34
|
-
try {
|
|
35
|
-
await rm(join(TEST_DATA_DIR, "privacy.json"), { force: true });
|
|
36
|
-
} catch {}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterAll(async () => {
|
|
40
|
-
try {
|
|
41
|
-
await rm(TEST_DATA_DIR, { recursive: true, force: true });
|
|
42
|
-
} catch {}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe("applyPrivacy", () => {
|
|
46
|
-
it("无 privacy.json 时返回原文", () => {
|
|
47
|
-
reloadPrivacyRules();
|
|
48
|
-
expect(applyPrivacy("hello weizhangjian")).toBe("hello weizhangjian");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("privacy.json 存在时按规则替换", async () => {
|
|
52
|
-
await writeFile(
|
|
53
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
54
|
-
JSON.stringify({ weizhangjian: "wzj", secret: "***" }),
|
|
55
|
-
"utf-8",
|
|
56
|
-
);
|
|
57
|
-
reloadPrivacyRules();
|
|
58
|
-
|
|
59
|
-
expect(applyPrivacy("hello weizhangjian")).toBe("hello wzj");
|
|
60
|
-
expect(applyPrivacy("my secret is safe")).toBe("my *** is safe");
|
|
61
|
-
expect(applyPrivacy("weizhangjian and secret")).toBe("wzj and ***");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("多规则替换多次出现", async () => {
|
|
65
|
-
await writeFile(
|
|
66
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
67
|
-
JSON.stringify({ a: "A", b: "B" }),
|
|
68
|
-
"utf-8",
|
|
69
|
-
);
|
|
70
|
-
reloadPrivacyRules();
|
|
71
|
-
|
|
72
|
-
expect(applyPrivacy("a b a b")).toBe("A B A B");
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("空文本直接返回", async () => {
|
|
76
|
-
await writeFile(
|
|
77
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
78
|
-
JSON.stringify({ x: "y" }),
|
|
79
|
-
"utf-8",
|
|
80
|
-
);
|
|
81
|
-
reloadPrivacyRules();
|
|
82
|
-
|
|
83
|
-
expect(applyPrivacy("")).toBe("");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("规则中的特殊字符不会被当作正则", async () => {
|
|
87
|
-
await writeFile(
|
|
88
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
89
|
-
JSON.stringify({ "a.b": "X", "(test)": "Y", "*star": "Z" }),
|
|
90
|
-
"utf-8",
|
|
91
|
-
);
|
|
92
|
-
reloadPrivacyRules();
|
|
93
|
-
|
|
94
|
-
expect(applyPrivacy("hello a.b world")).toBe("hello X world");
|
|
95
|
-
expect(applyPrivacy("text (test) here")).toBe("text Y here");
|
|
96
|
-
expect(applyPrivacy("a *star shines")).toBe("a Z shines");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("reloadPrivacyRules 强制重新加载", async () => {
|
|
100
|
-
await writeFile(
|
|
101
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
102
|
-
JSON.stringify({ old: "OLD" }),
|
|
103
|
-
"utf-8",
|
|
104
|
-
);
|
|
105
|
-
reloadPrivacyRules();
|
|
106
|
-
|
|
107
|
-
expect(applyPrivacy("old")).toBe("OLD");
|
|
108
|
-
expect(getPrivacyRules()).toEqual({ old: "OLD" });
|
|
109
|
-
|
|
110
|
-
// 变更磁盘内容后 reload
|
|
111
|
-
await writeFile(
|
|
112
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
113
|
-
JSON.stringify({ new: "NEW" }),
|
|
114
|
-
"utf-8",
|
|
115
|
-
);
|
|
116
|
-
reloadPrivacyRules();
|
|
117
|
-
|
|
118
|
-
expect(applyPrivacy("new")).toBe("NEW");
|
|
119
|
-
expect(getPrivacyRules()).toEqual({ new: "NEW" });
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("格式错误的 JSON 不抛异常,返回原文", async () => {
|
|
123
|
-
await writeFile(
|
|
124
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
125
|
-
"not json",
|
|
126
|
-
"utf-8",
|
|
127
|
-
);
|
|
128
|
-
reloadPrivacyRules();
|
|
129
|
-
|
|
130
|
-
expect(applyPrivacy("hello")).toBe("hello");
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("数组格式的 JSON 不抛异常,返回原文", async () => {
|
|
134
|
-
await writeFile(
|
|
135
|
-
join(TEST_DATA_DIR, "privacy.json"),
|
|
136
|
-
JSON.stringify(["a", "b"]),
|
|
137
|
-
"utf-8",
|
|
138
|
-
);
|
|
139
|
-
reloadPrivacyRules();
|
|
140
|
-
|
|
141
|
-
expect(applyPrivacy("hello")).toBe("hello");
|
|
142
|
-
});
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, afterAll, vi } from "vitest";
|
|
2
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
// 在 import privacy 之前 mock config,让 USER_DATA_DIR 指向临时目录
|
|
7
|
+
const TEST_DATA_DIR = await mkdtemp(join(tmpdir(), "chatccc-privacy-test-"));
|
|
8
|
+
vi.mock("../config.ts", async () => {
|
|
9
|
+
const actual = await vi.importActual<typeof import("../config.ts")>("../config.ts");
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
USER_DATA_DIR: TEST_DATA_DIR,
|
|
13
|
+
ts: () => "test-ts",
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
let applyPrivacy: (text: string) => string;
|
|
18
|
+
let reloadPrivacyRules: () => void;
|
|
19
|
+
let getPrivacyRules: () => Record<string, string>;
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
vi.resetModules();
|
|
23
|
+
// 清理临时目录中的 privacy.json
|
|
24
|
+
try {
|
|
25
|
+
await rm(join(TEST_DATA_DIR, "privacy.json"), { force: true });
|
|
26
|
+
} catch {}
|
|
27
|
+
const mod = await import("../privacy.ts");
|
|
28
|
+
applyPrivacy = mod.applyPrivacy;
|
|
29
|
+
reloadPrivacyRules = mod.reloadPrivacyRules;
|
|
30
|
+
getPrivacyRules = mod.getPrivacyRules;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
try {
|
|
35
|
+
await rm(join(TEST_DATA_DIR, "privacy.json"), { force: true });
|
|
36
|
+
} catch {}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterAll(async () => {
|
|
40
|
+
try {
|
|
41
|
+
await rm(TEST_DATA_DIR, { recursive: true, force: true });
|
|
42
|
+
} catch {}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("applyPrivacy", () => {
|
|
46
|
+
it("无 privacy.json 时返回原文", () => {
|
|
47
|
+
reloadPrivacyRules();
|
|
48
|
+
expect(applyPrivacy("hello weizhangjian")).toBe("hello weizhangjian");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("privacy.json 存在时按规则替换", async () => {
|
|
52
|
+
await writeFile(
|
|
53
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
54
|
+
JSON.stringify({ weizhangjian: "wzj", secret: "***" }),
|
|
55
|
+
"utf-8",
|
|
56
|
+
);
|
|
57
|
+
reloadPrivacyRules();
|
|
58
|
+
|
|
59
|
+
expect(applyPrivacy("hello weizhangjian")).toBe("hello wzj");
|
|
60
|
+
expect(applyPrivacy("my secret is safe")).toBe("my *** is safe");
|
|
61
|
+
expect(applyPrivacy("weizhangjian and secret")).toBe("wzj and ***");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("多规则替换多次出现", async () => {
|
|
65
|
+
await writeFile(
|
|
66
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
67
|
+
JSON.stringify({ a: "A", b: "B" }),
|
|
68
|
+
"utf-8",
|
|
69
|
+
);
|
|
70
|
+
reloadPrivacyRules();
|
|
71
|
+
|
|
72
|
+
expect(applyPrivacy("a b a b")).toBe("A B A B");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("空文本直接返回", async () => {
|
|
76
|
+
await writeFile(
|
|
77
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
78
|
+
JSON.stringify({ x: "y" }),
|
|
79
|
+
"utf-8",
|
|
80
|
+
);
|
|
81
|
+
reloadPrivacyRules();
|
|
82
|
+
|
|
83
|
+
expect(applyPrivacy("")).toBe("");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("规则中的特殊字符不会被当作正则", async () => {
|
|
87
|
+
await writeFile(
|
|
88
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
89
|
+
JSON.stringify({ "a.b": "X", "(test)": "Y", "*star": "Z" }),
|
|
90
|
+
"utf-8",
|
|
91
|
+
);
|
|
92
|
+
reloadPrivacyRules();
|
|
93
|
+
|
|
94
|
+
expect(applyPrivacy("hello a.b world")).toBe("hello X world");
|
|
95
|
+
expect(applyPrivacy("text (test) here")).toBe("text Y here");
|
|
96
|
+
expect(applyPrivacy("a *star shines")).toBe("a Z shines");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("reloadPrivacyRules 强制重新加载", async () => {
|
|
100
|
+
await writeFile(
|
|
101
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
102
|
+
JSON.stringify({ old: "OLD" }),
|
|
103
|
+
"utf-8",
|
|
104
|
+
);
|
|
105
|
+
reloadPrivacyRules();
|
|
106
|
+
|
|
107
|
+
expect(applyPrivacy("old")).toBe("OLD");
|
|
108
|
+
expect(getPrivacyRules()).toEqual({ old: "OLD" });
|
|
109
|
+
|
|
110
|
+
// 变更磁盘内容后 reload
|
|
111
|
+
await writeFile(
|
|
112
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
113
|
+
JSON.stringify({ new: "NEW" }),
|
|
114
|
+
"utf-8",
|
|
115
|
+
);
|
|
116
|
+
reloadPrivacyRules();
|
|
117
|
+
|
|
118
|
+
expect(applyPrivacy("new")).toBe("NEW");
|
|
119
|
+
expect(getPrivacyRules()).toEqual({ new: "NEW" });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("格式错误的 JSON 不抛异常,返回原文", async () => {
|
|
123
|
+
await writeFile(
|
|
124
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
125
|
+
"not json",
|
|
126
|
+
"utf-8",
|
|
127
|
+
);
|
|
128
|
+
reloadPrivacyRules();
|
|
129
|
+
|
|
130
|
+
expect(applyPrivacy("hello")).toBe("hello");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("数组格式的 JSON 不抛异常,返回原文", async () => {
|
|
134
|
+
await writeFile(
|
|
135
|
+
join(TEST_DATA_DIR, "privacy.json"),
|
|
136
|
+
JSON.stringify(["a", "b"]),
|
|
137
|
+
"utf-8",
|
|
138
|
+
);
|
|
139
|
+
reloadPrivacyRules();
|
|
140
|
+
|
|
141
|
+
expect(applyPrivacy("hello")).toBe("hello");
|
|
142
|
+
});
|
|
143
143
|
});
|