chatccc 0.2.51 → 0.2.53
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/README.md +103 -217
- package/bin/chatccc.mjs +23 -23
- package/config.sample.json +34 -30
- package/demo/ilink_echo_probe.ts +222 -222
- package/im-skills/feishu-skill/download-video.mjs +162 -162
- package/im-skills/feishu-skill/receive-send-file.md +66 -66
- package/im-skills/feishu-skill/receive-send-image.md +27 -27
- package/im-skills/feishu-skill/send-file.mjs +50 -50
- package/im-skills/feishu-skill/send-image.mjs +50 -50
- package/im-skills/feishu-skill/skill.md +10 -10
- package/package.json +59 -59
- package/src/__tests__/agent-image-rpc.test.ts +33 -33
- package/src/__tests__/agent-rpc-body.test.ts +41 -41
- package/src/__tests__/card-plain-text.test.ts +42 -0
- package/src/__tests__/claude-adapter.test.ts +54 -1
- package/src/__tests__/codex-adapter.test.ts +304 -304
- package/src/__tests__/config-reload.test.ts +58 -41
- package/src/__tests__/config-sample.test.ts +19 -0
- package/src/__tests__/crash-logging.test.ts +62 -62
- package/src/__tests__/fixtures/codex_simple_text.jsonl +4 -4
- package/src/__tests__/fixtures/codex_with_tool.jsonl +6 -6
- package/src/__tests__/im-skills.test.ts +46 -46
- package/src/__tests__/session.test.ts +95 -2
- package/src/__tests__/sim-agent.test.ts +173 -173
- package/src/__tests__/sim-platform.test.ts +76 -76
- package/src/__tests__/sim-store.test.ts +213 -213
- package/src/__tests__/stream-state.test.ts +134 -134
- package/src/__tests__/wechat-platform.test.ts +57 -0
- package/src/adapters/claude-adapter.ts +23 -2
- package/src/adapters/codex-adapter.ts +294 -294
- package/src/adapters/codex-session-meta-store.ts +130 -130
- package/src/agent-file-rpc.ts +156 -156
- package/src/agent-image-rpc.ts +152 -152
- package/src/agent-rpc-body.ts +48 -48
- package/src/card-plain-text.ts +108 -0
- package/src/cards.ts +4 -4
- package/src/config.ts +775 -742
- package/src/feishu-api.ts +6 -0
- package/src/im-skills.ts +109 -109
- package/src/index.ts +155 -788
- package/src/orchestrator.ts +1032 -0
- package/src/platform-adapter.ts +61 -0
- package/src/session-chat-binding.ts +7 -0
- package/src/session.ts +278 -113
- package/src/sim-agent.ts +167 -156
- package/src/trace.ts +50 -50
- package/src/web-ui.ts +1891 -1718
- package/src/wechat-platform.ts +517 -0
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
import { describe, it, expect, afterAll, beforeEach } from "vitest";
|
|
2
|
-
import { unlink } from "node:fs/promises";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
import { createSimAgent } from "../sim-agent.ts";
|
|
7
|
-
import { simStore, setMessageHandler } from "../sim-store.ts";
|
|
8
|
-
|
|
9
|
-
const MESSAGES_FILE = join(homedir(), ".chatccc", "sim", "messages.jsonl");
|
|
10
|
-
|
|
11
|
-
describe("SimAgent", () => {
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
simStore.reset();
|
|
14
|
-
setMessageHandler(async () => {});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
afterAll(async () => {
|
|
18
|
-
try { await unlink(MESSAGES_FILE); } catch { /* ok */ }
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("createSimAgent 绑定已有用户账户", () => {
|
|
22
|
-
const a = createSimAgent("sim_user_001");
|
|
23
|
-
expect(a.userId).toBe("sim_user_001");
|
|
24
|
-
expect(a.account.name).toBe("Developer");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("createSimAgent 不存在用户抛出错误", () => {
|
|
28
|
-
expect(() => createSimAgent("nonexistent")).toThrow('Account "nonexistent" not found');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("createSimAgent bot 账户抛出错误", () => {
|
|
32
|
-
expect(() => createSimAgent("bot")).toThrow('is not a user account');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("createP2pWithBot 创建后用户能看到", () => {
|
|
36
|
-
const a = createSimAgent("sim_user_001");
|
|
37
|
-
const chatId = a.createP2pWithBot();
|
|
38
|
-
expect(chatId).toBe("p2p_sim_user_001_bot");
|
|
39
|
-
const chats = a.listChats();
|
|
40
|
-
expect(chats.some((c) => c.type === "p2p")).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("sendMessage 将消息发送到指定群", async () => {
|
|
44
|
-
let receivedText = "";
|
|
45
|
-
let receivedChatId = "";
|
|
46
|
-
setMessageHandler(async (text, chatId) => {
|
|
47
|
-
receivedText = text;
|
|
48
|
-
receivedChatId = chatId;
|
|
49
|
-
simStore.sendReply(chatId, "text", `echo: ${text}`);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const a = createSimAgent("sim_user_001");
|
|
53
|
-
await a.sendMessage("sim_default", "你好");
|
|
54
|
-
|
|
55
|
-
expect(receivedText).toBe("你好");
|
|
56
|
-
expect(receivedChatId).toBe("sim_default");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("sendMessage 非成员发送消息抛出错误", async () => {
|
|
60
|
-
simStore.registerAccount({ id: "alice_test", kind: "user", name: "Alice" });
|
|
61
|
-
const a = createSimAgent("alice_test");
|
|
62
|
-
await expect(a.sendMessage("sim_default", "hello"))
|
|
63
|
-
.rejects.toThrow('is not a member');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("on message 订阅收到 bot 回复", async () => {
|
|
67
|
-
setMessageHandler(async (text, chatId) => {
|
|
68
|
-
simStore.sendReply(chatId, "text", `reply to: ${text}`);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const a = createSimAgent("sim_user_001");
|
|
72
|
-
let received: { chatId: string; content: string } | null = null;
|
|
73
|
-
a.on("message", (chatId, msg) => {
|
|
74
|
-
received = { chatId, content: msg.content };
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
await a.sendMessage("sim_default", "ping");
|
|
78
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
79
|
-
|
|
80
|
-
expect(received).not.toBeNull();
|
|
81
|
-
expect(received!.content).toBe("reply to: ping");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("waitForReply 等待 bot 回复", async () => {
|
|
85
|
-
setMessageHandler(async (text, chatId) => {
|
|
86
|
-
setTimeout(() => {
|
|
87
|
-
simStore.sendReply(chatId, "text", `async reply: ${text}`);
|
|
88
|
-
}, 10);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const a = createSimAgent("sim_user_001");
|
|
92
|
-
const replyPromise = a.waitForReply("sim_default", 5000);
|
|
93
|
-
await a.sendMessage("sim_default", "hello");
|
|
94
|
-
|
|
95
|
-
const reply = await replyPromise;
|
|
96
|
-
expect(reply).not.toBeNull();
|
|
97
|
-
expect(reply!.content).toBe("async reply: hello");
|
|
98
|
-
expect(reply!.senderId).toBe("bot");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("waitForReply 超时返回 null", async () => {
|
|
102
|
-
setMessageHandler(async () => {});
|
|
103
|
-
|
|
104
|
-
const a = createSimAgent("sim_user_001");
|
|
105
|
-
const reply = await a.waitForReply("sim_default", 100);
|
|
106
|
-
expect(reply).toBeNull();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("getMessages 获取消息历史", async () => {
|
|
110
|
-
setMessageHandler(async (text, chatId) => {
|
|
111
|
-
simStore.sendReply(chatId, "text", `echo: ${text}`);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const a = createSimAgent("sim_user_001");
|
|
115
|
-
await a.sendMessage("sim_default", "msg1");
|
|
116
|
-
await a.sendMessage("sim_default", "msg2");
|
|
117
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
118
|
-
|
|
119
|
-
const msgs = a.getMessages("sim_default");
|
|
120
|
-
expect(msgs.length).toBe(4); // 2 user + 2 bot
|
|
121
|
-
expect(msgs[0].senderId).toBe("sim_user_001");
|
|
122
|
-
expect(msgs[0].content).toBe("msg1");
|
|
123
|
-
expect(msgs[1].senderId).toBe("bot");
|
|
124
|
-
expect(msgs[1].content).toBe("echo: msg1");
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("on invited_to_group 收到拉群通知", () => {
|
|
128
|
-
simStore.registerAccount({ id: "new_user", kind: "user", name: "New" });
|
|
129
|
-
const a = createSimAgent("new_user");
|
|
130
|
-
let invitedChatId = "";
|
|
131
|
-
let invitedUserId = "";
|
|
132
|
-
a.on("invited_to_group", (chatId, userId) => {
|
|
133
|
-
invitedChatId = chatId;
|
|
134
|
-
invitedUserId = userId;
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
simStore.createGroupChat("新群", ["new_user"]);
|
|
138
|
-
expect(invitedChatId).toMatch(/^sim_/);
|
|
139
|
-
expect(invitedUserId).toBe("new_user");
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("off 取消消息订阅", async () => {
|
|
143
|
-
setMessageHandler(async (text, chatId) => {
|
|
144
|
-
simStore.sendReply(chatId, "text", `echo: ${text}`);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
const a = createSimAgent("sim_user_001");
|
|
148
|
-
let count = 0;
|
|
149
|
-
const handler = () => { count++; };
|
|
150
|
-
a.on("message", handler);
|
|
151
|
-
|
|
152
|
-
await a.sendMessage("sim_default", "test1");
|
|
153
|
-
await new Promise((r) => setTimeout(r, 30));
|
|
154
|
-
// sendMessage 产生 2 条消息(recordMessage + sendReply)
|
|
155
|
-
const countBeforeOff = count;
|
|
156
|
-
expect(countBeforeOff).toBe(2);
|
|
157
|
-
|
|
158
|
-
a.off("message", handler);
|
|
159
|
-
|
|
160
|
-
await a.sendMessage("sim_default", "test2");
|
|
161
|
-
await new Promise((r) => setTimeout(r, 30));
|
|
162
|
-
|
|
163
|
-
// off 后不应再增加
|
|
164
|
-
expect(count).toBe(countBeforeOff);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("listChats 返回用户会话列表", () => {
|
|
168
|
-
const a = createSimAgent("sim_user_001");
|
|
169
|
-
const chats = a.listChats();
|
|
170
|
-
expect(chats.length).toBeGreaterThanOrEqual(1);
|
|
171
|
-
expect(chats.some((c) => c.id === "sim_default")).toBe(true);
|
|
172
|
-
expect(chats.some((c) => c.type === "p2p")).toBe(true);
|
|
173
|
-
});
|
|
1
|
+
import { describe, it, expect, afterAll, beforeEach } from "vitest";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { createSimAgent } from "../sim-agent.ts";
|
|
7
|
+
import { simStore, setMessageHandler } from "../sim-store.ts";
|
|
8
|
+
|
|
9
|
+
const MESSAGES_FILE = join(homedir(), ".chatccc", "sim", "messages.jsonl");
|
|
10
|
+
|
|
11
|
+
describe("SimAgent", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
simStore.reset();
|
|
14
|
+
setMessageHandler(async () => {});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
try { await unlink(MESSAGES_FILE); } catch { /* ok */ }
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("createSimAgent 绑定已有用户账户", () => {
|
|
22
|
+
const a = createSimAgent("sim_user_001");
|
|
23
|
+
expect(a.userId).toBe("sim_user_001");
|
|
24
|
+
expect(a.account.name).toBe("Developer");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("createSimAgent 不存在用户抛出错误", () => {
|
|
28
|
+
expect(() => createSimAgent("nonexistent")).toThrow('Account "nonexistent" not found');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("createSimAgent bot 账户抛出错误", () => {
|
|
32
|
+
expect(() => createSimAgent("bot")).toThrow('is not a user account');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("createP2pWithBot 创建后用户能看到", () => {
|
|
36
|
+
const a = createSimAgent("sim_user_001");
|
|
37
|
+
const chatId = a.createP2pWithBot();
|
|
38
|
+
expect(chatId).toBe("p2p_sim_user_001_bot");
|
|
39
|
+
const chats = a.listChats();
|
|
40
|
+
expect(chats.some((c) => c.type === "p2p")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("sendMessage 将消息发送到指定群", async () => {
|
|
44
|
+
let receivedText = "";
|
|
45
|
+
let receivedChatId = "";
|
|
46
|
+
setMessageHandler(async (text, chatId) => {
|
|
47
|
+
receivedText = text;
|
|
48
|
+
receivedChatId = chatId;
|
|
49
|
+
simStore.sendReply(chatId, "text", `echo: ${text}`);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const a = createSimAgent("sim_user_001");
|
|
53
|
+
await a.sendMessage("sim_default", "你好");
|
|
54
|
+
|
|
55
|
+
expect(receivedText).toBe("你好");
|
|
56
|
+
expect(receivedChatId).toBe("sim_default");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("sendMessage 非成员发送消息抛出错误", async () => {
|
|
60
|
+
simStore.registerAccount({ id: "alice_test", kind: "user", name: "Alice" });
|
|
61
|
+
const a = createSimAgent("alice_test");
|
|
62
|
+
await expect(a.sendMessage("sim_default", "hello"))
|
|
63
|
+
.rejects.toThrow('is not a member');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("on message 订阅收到 bot 回复", async () => {
|
|
67
|
+
setMessageHandler(async (text, chatId) => {
|
|
68
|
+
simStore.sendReply(chatId, "text", `reply to: ${text}`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const a = createSimAgent("sim_user_001");
|
|
72
|
+
let received: { chatId: string; content: string } | null = null;
|
|
73
|
+
a.on("message", (chatId, msg) => {
|
|
74
|
+
received = { chatId, content: msg.content };
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await a.sendMessage("sim_default", "ping");
|
|
78
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
79
|
+
|
|
80
|
+
expect(received).not.toBeNull();
|
|
81
|
+
expect(received!.content).toBe("reply to: ping");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("waitForReply 等待 bot 回复", async () => {
|
|
85
|
+
setMessageHandler(async (text, chatId) => {
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
simStore.sendReply(chatId, "text", `async reply: ${text}`);
|
|
88
|
+
}, 10);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const a = createSimAgent("sim_user_001");
|
|
92
|
+
const replyPromise = a.waitForReply("sim_default", 5000);
|
|
93
|
+
await a.sendMessage("sim_default", "hello");
|
|
94
|
+
|
|
95
|
+
const reply = await replyPromise;
|
|
96
|
+
expect(reply).not.toBeNull();
|
|
97
|
+
expect(reply!.content).toBe("async reply: hello");
|
|
98
|
+
expect(reply!.senderId).toBe("bot");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("waitForReply 超时返回 null", async () => {
|
|
102
|
+
setMessageHandler(async () => {});
|
|
103
|
+
|
|
104
|
+
const a = createSimAgent("sim_user_001");
|
|
105
|
+
const reply = await a.waitForReply("sim_default", 100);
|
|
106
|
+
expect(reply).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("getMessages 获取消息历史", async () => {
|
|
110
|
+
setMessageHandler(async (text, chatId) => {
|
|
111
|
+
simStore.sendReply(chatId, "text", `echo: ${text}`);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const a = createSimAgent("sim_user_001");
|
|
115
|
+
await a.sendMessage("sim_default", "msg1");
|
|
116
|
+
await a.sendMessage("sim_default", "msg2");
|
|
117
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
118
|
+
|
|
119
|
+
const msgs = a.getMessages("sim_default");
|
|
120
|
+
expect(msgs.length).toBe(4); // 2 user + 2 bot
|
|
121
|
+
expect(msgs[0].senderId).toBe("sim_user_001");
|
|
122
|
+
expect(msgs[0].content).toBe("msg1");
|
|
123
|
+
expect(msgs[1].senderId).toBe("bot");
|
|
124
|
+
expect(msgs[1].content).toBe("echo: msg1");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("on invited_to_group 收到拉群通知", () => {
|
|
128
|
+
simStore.registerAccount({ id: "new_user", kind: "user", name: "New" });
|
|
129
|
+
const a = createSimAgent("new_user");
|
|
130
|
+
let invitedChatId = "";
|
|
131
|
+
let invitedUserId = "";
|
|
132
|
+
a.on("invited_to_group", (chatId, userId) => {
|
|
133
|
+
invitedChatId = chatId;
|
|
134
|
+
invitedUserId = userId;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
simStore.createGroupChat("新群", ["new_user"]);
|
|
138
|
+
expect(invitedChatId).toMatch(/^sim_/);
|
|
139
|
+
expect(invitedUserId).toBe("new_user");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("off 取消消息订阅", async () => {
|
|
143
|
+
setMessageHandler(async (text, chatId) => {
|
|
144
|
+
simStore.sendReply(chatId, "text", `echo: ${text}`);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const a = createSimAgent("sim_user_001");
|
|
148
|
+
let count = 0;
|
|
149
|
+
const handler = () => { count++; };
|
|
150
|
+
a.on("message", handler);
|
|
151
|
+
|
|
152
|
+
await a.sendMessage("sim_default", "test1");
|
|
153
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
154
|
+
// sendMessage 产生 2 条消息(recordMessage + sendReply)
|
|
155
|
+
const countBeforeOff = count;
|
|
156
|
+
expect(countBeforeOff).toBe(2);
|
|
157
|
+
|
|
158
|
+
a.off("message", handler);
|
|
159
|
+
|
|
160
|
+
await a.sendMessage("sim_default", "test2");
|
|
161
|
+
await new Promise((r) => setTimeout(r, 30));
|
|
162
|
+
|
|
163
|
+
// off 后不应再增加
|
|
164
|
+
expect(count).toBe(countBeforeOff);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("listChats 返回用户会话列表", () => {
|
|
168
|
+
const a = createSimAgent("sim_user_001");
|
|
169
|
+
const chats = a.listChats();
|
|
170
|
+
expect(chats.length).toBeGreaterThanOrEqual(1);
|
|
171
|
+
expect(chats.some((c) => c.id === "sim_default")).toBe(true);
|
|
172
|
+
expect(chats.some((c) => c.type === "p2p")).toBe(true);
|
|
173
|
+
});
|
|
174
174
|
});
|
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
import { describe, it, expect, afterAll } from "vitest";
|
|
2
|
-
import { unlink } from "node:fs/promises";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
|
|
6
|
-
import { SimulatedPlatform, SIM_DEFAULT_CHAT_ID } from "../sim-platform.ts";
|
|
7
|
-
|
|
8
|
-
const MESSAGES_FILE = join(homedir(), ".chatccc", "sim", "messages.jsonl");
|
|
9
|
-
|
|
10
|
-
describe("SimulatedPlatform", () => {
|
|
11
|
-
afterAll(async () => {
|
|
12
|
-
// 清理测试消息文件
|
|
13
|
-
try { await unlink(MESSAGES_FILE); } catch { /* ok */ }
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("getTenantAccessToken 返回固定 token", async () => {
|
|
17
|
-
const token = await SimulatedPlatform.getTenantAccessToken();
|
|
18
|
-
expect(token).toBe("sim_token");
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("createGroupChat 创建群并返回 sim_xxx ID", async () => {
|
|
22
|
-
const chatId = await SimulatedPlatform.createGroupChat("t", "测试群", ["u1"]);
|
|
23
|
-
expect(chatId).toMatch(/^sim_[0-9a-f]{8}$/);
|
|
24
|
-
const info = await SimulatedPlatform.getChatInfo("t", chatId);
|
|
25
|
-
expect(info.name).toBe("测试群");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("getChatInfo 默认群存在", async () => {
|
|
29
|
-
const info = await SimulatedPlatform.getChatInfo("t", SIM_DEFAULT_CHAT_ID);
|
|
30
|
-
expect(info.name).toBe("默认模拟会话");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("updateChatInfo 更新群信息", async () => {
|
|
34
|
-
const chatId = await SimulatedPlatform.createGroupChat("t", "原群名", ["u1"]);
|
|
35
|
-
await SimulatedPlatform.updateChatInfo("t", chatId, "新群名", "Claude Session: abc123");
|
|
36
|
-
const info = await SimulatedPlatform.getChatInfo("t", chatId);
|
|
37
|
-
expect(info.name).toBe("新群名");
|
|
38
|
-
expect(info.description).toContain("Claude Session");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("sendTextReply 返回 true", async () => {
|
|
42
|
-
const ok = await SimulatedPlatform.sendTextReply("t", "ch1", "你好");
|
|
43
|
-
expect(ok).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("sendCardReply 返回 true", async () => {
|
|
47
|
-
const ok = await SimulatedPlatform.sendCardReply("t", "ch1", "标题", "内容", "blue");
|
|
48
|
-
expect(ok).toBe(true);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("verifyAllPermissions 全部通过", async () => {
|
|
52
|
-
const results = await SimulatedPlatform.verifyAllPermissions("t");
|
|
53
|
-
expect(results.length).toBe(5);
|
|
54
|
-
expect(results.every((r) => r.ok)).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("addReaction 无异常", async () => {
|
|
58
|
-
await expect(SimulatedPlatform.addReaction("t", "msg1")).resolves.toBeUndefined();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("setChatAvatar 无异常", async () => {
|
|
62
|
-
await expect(SimulatedPlatform.setChatAvatar("t", "ch1", "claude", "busy")).resolves.toBeUndefined();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("纯函数 extractSessionInfo 正常工作", () => {
|
|
66
|
-
const result = SimulatedPlatform.extractSessionInfo("Claude Code Session: a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
|
67
|
-
expect(result).toEqual({ sessionId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", tool: "claude" });
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("纯函数 formatDelayNotice 正常工作", () => {
|
|
71
|
-
const notice = SimulatedPlatform.formatDelayNotice(Date.now() - 20 * 60 * 1000, "测试消息");
|
|
72
|
-
expect(notice).toBeDefined();
|
|
73
|
-
expect(notice).toContain("延迟送达");
|
|
74
|
-
// 近期消息不触发
|
|
75
|
-
expect(SimulatedPlatform.formatDelayNotice(Date.now(), "test")).toBeNull();
|
|
76
|
-
});
|
|
1
|
+
import { describe, it, expect, afterAll } from "vitest";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { SimulatedPlatform, SIM_DEFAULT_CHAT_ID } from "../sim-platform.ts";
|
|
7
|
+
|
|
8
|
+
const MESSAGES_FILE = join(homedir(), ".chatccc", "sim", "messages.jsonl");
|
|
9
|
+
|
|
10
|
+
describe("SimulatedPlatform", () => {
|
|
11
|
+
afterAll(async () => {
|
|
12
|
+
// 清理测试消息文件
|
|
13
|
+
try { await unlink(MESSAGES_FILE); } catch { /* ok */ }
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("getTenantAccessToken 返回固定 token", async () => {
|
|
17
|
+
const token = await SimulatedPlatform.getTenantAccessToken();
|
|
18
|
+
expect(token).toBe("sim_token");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("createGroupChat 创建群并返回 sim_xxx ID", async () => {
|
|
22
|
+
const chatId = await SimulatedPlatform.createGroupChat("t", "测试群", ["u1"]);
|
|
23
|
+
expect(chatId).toMatch(/^sim_[0-9a-f]{8}$/);
|
|
24
|
+
const info = await SimulatedPlatform.getChatInfo("t", chatId);
|
|
25
|
+
expect(info.name).toBe("测试群");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("getChatInfo 默认群存在", async () => {
|
|
29
|
+
const info = await SimulatedPlatform.getChatInfo("t", SIM_DEFAULT_CHAT_ID);
|
|
30
|
+
expect(info.name).toBe("默认模拟会话");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("updateChatInfo 更新群信息", async () => {
|
|
34
|
+
const chatId = await SimulatedPlatform.createGroupChat("t", "原群名", ["u1"]);
|
|
35
|
+
await SimulatedPlatform.updateChatInfo("t", chatId, "新群名", "Claude Session: abc123");
|
|
36
|
+
const info = await SimulatedPlatform.getChatInfo("t", chatId);
|
|
37
|
+
expect(info.name).toBe("新群名");
|
|
38
|
+
expect(info.description).toContain("Claude Session");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("sendTextReply 返回 true", async () => {
|
|
42
|
+
const ok = await SimulatedPlatform.sendTextReply("t", "ch1", "你好");
|
|
43
|
+
expect(ok).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("sendCardReply 返回 true", async () => {
|
|
47
|
+
const ok = await SimulatedPlatform.sendCardReply("t", "ch1", "标题", "内容", "blue");
|
|
48
|
+
expect(ok).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("verifyAllPermissions 全部通过", async () => {
|
|
52
|
+
const results = await SimulatedPlatform.verifyAllPermissions("t");
|
|
53
|
+
expect(results.length).toBe(5);
|
|
54
|
+
expect(results.every((r) => r.ok)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("addReaction 无异常", async () => {
|
|
58
|
+
await expect(SimulatedPlatform.addReaction("t", "msg1")).resolves.toBeUndefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("setChatAvatar 无异常", async () => {
|
|
62
|
+
await expect(SimulatedPlatform.setChatAvatar("t", "ch1", "claude", "busy")).resolves.toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("纯函数 extractSessionInfo 正常工作", () => {
|
|
66
|
+
const result = SimulatedPlatform.extractSessionInfo("Claude Code Session: a1b2c3d4-e5f6-7890-abcd-ef1234567890");
|
|
67
|
+
expect(result).toEqual({ sessionId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", tool: "claude" });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("纯函数 formatDelayNotice 正常工作", () => {
|
|
71
|
+
const notice = SimulatedPlatform.formatDelayNotice(Date.now() - 20 * 60 * 1000, "测试消息");
|
|
72
|
+
expect(notice).toBeDefined();
|
|
73
|
+
expect(notice).toContain("延迟送达");
|
|
74
|
+
// 近期消息不触发
|
|
75
|
+
expect(SimulatedPlatform.formatDelayNotice(Date.now(), "test")).toBeNull();
|
|
76
|
+
});
|
|
77
77
|
});
|