Files
memind/scheduled-task-worker.test.mjs
T
john b9f5720ce0
Memind CI / Test, build, and release guards (push) Failing after 4s
fix(wechat): recognize news page intents and harden scheduled task delivery
Expand page.generate rules for post-page verbs and link-missing retries,
recover HTML artifacts when shadow LLM agrees, and stop marking scheduled
tasks failed when WeChat notification hits rate limits.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 06:46:37 +08:00

237 lines
6.1 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { startScheduledTaskWorker } from './scheduled-task-worker.mjs';
test('scheduled task worker executes due task and completes one-shot task', async () => {
const calls = [];
const notifications = [];
const wechat = [];
const task = {
id: 'task-1',
userId: 'user-1',
title: '每日新闻页',
taskSpec: '做新闻页',
recurrence: 'once',
notifyChannel: 'both',
attempts: 1,
};
const worker = startScheduledTaskWorker({
intervalMs: 60_000,
userAuth: { id: 'user-auth' },
tkmindProxy: { id: 'proxy' },
scheduledTaskService: {
async listDueTasks() {
calls.push('list');
return [task];
},
async lockTask(id) {
calls.push(`lock:${id}`);
return task;
},
async markTaskRunning(input) {
calls.push(`running:${input.id}`);
return { ...input, status: 'running' };
},
async markTaskSucceeded(input, payload) {
calls.push(`success:${input.id}:${payload.deliveryText}`);
return { ...input, status: 'completed' };
},
async markTaskFailed() {
calls.push('failed');
},
},
scheduleService: {
async createUserNotification(input) {
notifications.push(input.notificationType);
},
},
notificationDispatcher: {
async sendScheduleNotification({ userId, text }) {
wechat.push({ userId, text });
},
},
executeTask: async () => ({
sessionId: 'session-1',
requestId: 'req-1',
deliveryText: '页面:https://example.com/news.html',
}),
logger: { warn() {}, info() {} },
runOnStart: false,
});
await worker.runOnce();
worker.stop();
assert.deepEqual(calls, [
'list',
'lock:task-1',
'running:task-1',
'success:task-1:页面:https://example.com/news.html',
]);
assert.deepEqual(notifications, ['scheduled_task_result']);
assert.equal(wechat.length, 1);
assert.match(wechat[0].text, /定时任务完成:每日新闻页/);
});
test('scheduled task worker marks failure after execution error', async () => {
const calls = [];
const task = {
id: 'task-2',
userId: 'user-2',
title: '失败任务',
recurrence: 'daily',
notifyChannel: 'web',
attempts: 3,
};
const worker = startScheduledTaskWorker({
intervalMs: 60_000,
userAuth: { id: 'user-auth' },
tkmindProxy: { id: 'proxy' },
scheduledTaskService: {
async listDueTasks() {
return [task];
},
async lockTask() {
return task;
},
async markTaskRunning(input) {
return input;
},
async markTaskSucceeded() {
calls.push('success');
},
async markTaskFailed(input, err, { maxAttempts }) {
calls.push(`failed:${maxAttempts}:${err.message}`);
return { ...input, status: 'failed', lastError: err.message };
},
},
scheduleService: {
async createUserNotification(input) {
calls.push(`notify:${input.notificationType}`);
},
},
executeTask: async () => {
throw new Error('agent timeout');
},
maxAttempts: 3,
logger: { warn() {} },
runOnStart: false,
});
await worker.runOnce();
worker.stop();
assert.deepEqual(calls, ['failed:3:agent timeout', 'notify:scheduled_task_failed']);
});
test('scheduled task worker marks failure when agent only asks for clarification', async () => {
const calls = [];
const task = {
id: 'task-3',
userId: 'user-3',
title: '每日诗意清晨',
recurrence: 'daily',
notifyChannel: 'web',
attempts: 1,
};
const worker = startScheduledTaskWorker({
intervalMs: 60_000,
userAuth: { id: 'user-auth' },
tkmindProxy: { id: 'proxy' },
scheduledTaskService: {
async listDueTasks() {
return [task];
},
async lockTask() {
return task;
},
async markTaskRunning(input) {
return input;
},
async markTaskSucceeded() {
calls.push('success');
},
async markTaskFailed(input, err) {
calls.push(`failed:${err.code}`);
return { ...input, status: 'failed', lastError: err.message };
},
},
scheduleService: {
async createUserNotification(input) {
calls.push(`notify:${input.notificationType}`);
},
},
executeTask: async () => ({
sessionId: 'session-3',
requestId: 'req-3',
deliveryText: '请问每天早上几点执行?',
}),
logger: { warn() {} },
runOnStart: false,
});
await worker.runOnce();
worker.stop();
assert.deepEqual(calls, ['failed:SCHEDULED_TASK_NON_DELIVERY', 'notify:scheduled_task_failed']);
});
test('scheduled task worker keeps success when wechat notification fails', async () => {
const calls = [];
const task = {
id: 'task-4',
userId: 'user-4',
title: '每日新闻页',
recurrence: 'daily',
notifyChannel: 'both',
attempts: 1,
};
const worker = startScheduledTaskWorker({
intervalMs: 60_000,
userAuth: { id: 'user-auth' },
tkmindProxy: { id: 'proxy' },
scheduledTaskService: {
async listDueTasks() {
return [task];
},
async lockTask() {
return task;
},
async markTaskRunning(input) {
return input;
},
async markTaskSucceeded(input) {
calls.push(`success:${input.id}`);
return { ...input, status: 'completed' };
},
async markTaskFailed() {
calls.push('failed');
},
},
scheduleService: {
async createUserNotification() {
calls.push('notify:web');
},
},
notificationDispatcher: {
async sendScheduleNotification() {
const err = new Error('out of response count limit');
err.errcode = 45047;
throw err;
},
},
executeTask: async () => ({
sessionId: 'session-4',
requestId: 'req-4',
deliveryText: '页面已生成:https://example.com/news.html',
}),
logger: { warn() {} },
runOnStart: false,
});
await worker.runOnce();
worker.stop();
assert.deepEqual(calls, ['notify:web', 'success:task-4']);
});