Files
memind/wechat/handlers/schedule.test.mjs
T
john d58dc2a251 feat(wechat): add Intent Transaction Layer with unified task schema
Introduce Draft → Confirm → Commit flow for WeChat schedule intents behind
feature flags, plus h5_tasks dual-write/read aggregation and rollout scripts
so reminders and automations get explicit user confirmation before persisting.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 15:59:17 +08:00

185 lines
6.0 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { handleWechatScheduleIntent } from './schedule.mjs';
function createScheduleService() {
return {
async createItem(payload) {
return { id: 'item-1', ...payload };
},
async createReminder(payload) {
return { id: 'reminder-1', ...payload };
},
async createDailyTodoDigest(payload) {
return { id: 'digest-1', minute: payload.minute, hour: payload.hour, ...payload };
},
async createBalanceLowAlert(payload) {
return { id: 'balance-1', ...payload };
},
async buildTodoDigestText() {
return '今天有 2 条待办。';
},
};
}
function createScheduleLlmConfigService(enabled = true) {
return {
async isScheduleLlmEnabled() {
return enabled;
},
async getConfig() {
return {
scheduleLlmEnabled: enabled,
modelProviderKeyId: 'deepseek-key',
model: 'deepseek-v4-pro',
};
},
};
}
test('schedule handler keeps rule-based parsing as first priority', async () => {
let llmCalled = false;
const reply = await handleWechatScheduleIntent({
intent: { agentText: '帮我记一下 跟进合同', msgId: 'msg-1' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
llmCalled = true;
return { ok: true, reply: '{"action":"none"}' };
},
},
});
assert.match(reply, /已记录到待办列表/);
assert.equal(llmCalled, false);
});
test('schedule handler can use llm fallback for ambiguous todo text', async () => {
const reply = await handleWechatScheduleIntent({
intent: { agentText: '下周把合同发给客户,别忘了', msgId: 'msg-2' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
return {
ok: true,
reply: '{"action":"create_todo","title":"把合同发给客户"}',
};
},
},
});
assert.match(reply, /把合同发给客户/);
});
test('schedule handler directly handles daily digest shortcut phrase', async () => {
const reply = await handleWechatScheduleIntent({
intent: { agentText: '每天早上7点把当天待办发给我', msgId: 'msg-digest-1' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
});
assert.match(reply, /已设置/);
assert.match(reply, /每天早上 7点/);
});
test('schedule handler does not call llm when switch is off', async () => {
let llmCalled = false;
const reply = await handleWechatScheduleIntent({
intent: { agentText: '别忘了报销', msgId: 'msg-4' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(false),
llmProviderService: {
async createChatCompletion() {
llmCalled = true;
return { ok: true, reply: '{"action":"create_todo","title":"报销"}' };
},
},
});
assert.equal(reply, null);
assert.equal(llmCalled, false);
});
test('schedule handler falls back quietly when llm request fails', async () => {
const warnings = [];
const reply = await handleWechatScheduleIntent({
intent: { agentText: '别忘了报销', msgId: 'msg-5' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
return { ok: false, message: 'provider unavailable' };
},
},
logger: { warn: (...args) => warnings.push(args.join(' ')) },
});
assert.equal(reply, null);
assert.equal(warnings.length, 1);
assert.match(warnings[0], /provider unavailable/);
});
test('schedule handler falls back quietly when llm request throws', async () => {
const warnings = [];
const reply = await handleWechatScheduleIntent({
intent: { agentText: '什么是机器学习', msgId: 'msg-6' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
throw new TypeError('fetch failed');
},
},
logger: { warn: (...args) => warnings.push(args.join(' ')) },
});
assert.equal(reply, null);
assert.equal(warnings.length, 1);
assert.match(warnings[0], /fetch failed/);
});
test('schedule handler directly creates simple timed reminder', async () => {
const calls = [];
const reply = await handleWechatScheduleIntent({
intent: {
agentText: '帮我设置提醒,下午 14:30 分开会,项目计划例会',
msgId: 'msg-reminder-1',
},
user: { userId: 'user-1' },
scheduleService: {
async createItem(payload) {
calls.push(['createItem', payload.title, payload.startAt]);
return { id: 'item-reminder-1', ...payload };
},
async createReminder(payload) {
calls.push(['createReminder', payload.itemId, payload.remindAt]);
return { id: 'reminder-1', ...payload };
},
},
});
assert.match(reply, /已设置提醒/);
assert.match(reply, /项目计划例会/);
assert.equal(calls.length, 2);
assert.equal(calls[0][0], 'createItem');
assert.equal(calls[1][0], 'createReminder');
});
test('schedule handler falls through for multi-time reminder requests', async () => {
const reply = await handleWechatScheduleIntent({
intent: { agentText: '明天早上六点去跑步,五点半提醒我', msgId: 'msg-3' },
user: { userId: 'user-1' },
scheduleService: createScheduleService(),
wechatScheduleLlmConfigService: createScheduleLlmConfigService(true),
llmProviderService: {
async createChatCompletion() {
return {
ok: true,
reply: '{"action":"schedule_agent"}',
};
},
},
});
assert.equal(reply, null);
});