Files
memind/scheduled-task-executor.test.mjs
T
john 7c0ed58ae1
Memind CI / Test, build, and release guards (push) Successful in 3m36s
fix(scheduled-task): release page delivery contracts before notifying users
Scheduled automation now prepares and retries MindSpace delivery contracts,
blocks WeChat pushes when public HTML links are not ready, and reconciles
stuck static preparing contracts on each worker scan.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 08:26:21 +08:00

159 lines
5.3 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import {
buildScheduledTaskExecutionPrompt,
deliveryTextPromisesPublicHtml,
extractPublicHtmlPathsFromText,
extractScheduledTaskDeliveryText,
finalizeScheduledTaskPageDelivery,
formatScheduledTaskDeliveryMessage,
looksLikeScheduledTaskNonDelivery,
reconcileStuckStaticPageDeliveryContracts,
resolveScheduledTaskDeliveryRetryDelaysMs,
} from './scheduled-task-executor.mjs';
test('buildScheduledTaskExecutionPrompt includes task spec and automation marker', () => {
const message = buildScheduledTaskExecutionPrompt({
id: 'task-1',
title: '每日新闻页',
taskSpec: '搜索今日新闻并生成 HTML 页面',
recurrence: 'daily',
timezone: 'Asia/Shanghai',
});
const text = message.content[0].text;
assert.match(text, /scheduled-task-automation/);
assert.match(text, /每日新闻页/);
assert.match(text, /搜索今日新闻并生成 HTML 页面/);
assert.match(text, /Scheduled Automation/);
assert.match(text, /禁止调用 scheduled_task_create/);
});
test('looksLikeScheduledTaskNonDelivery detects clarification replies', () => {
assert.equal(
looksLikeScheduledTaskNonDelivery('请问 7月31日具体几点执行这个任务?'),
true,
);
assert.equal(
looksLikeScheduledTaskNonDelivery('页面已生成:https://example.com/news.html'),
false,
);
assert.equal(
looksLikeScheduledTaskNonDelivery(
'页面已生成:https://m.tkmind.cn/MindSpace/user-1/public/daily-news.html',
),
true,
);
assert.equal(
looksLikeScheduledTaskNonDelivery('好的', { readyPaths: ['public/news.html'] }),
false,
);
assert.equal(looksLikeScheduledTaskNonDelivery('验证成功'), false);
assert.equal(looksLikeScheduledTaskNonDelivery('已完成'), false);
assert.equal(looksLikeScheduledTaskNonDelivery('好的'), true);
});
test('extractPublicHtmlPathsFromText parses MindSpace public links', () => {
assert.deepEqual(
extractPublicHtmlPathsFromText(
'链接:https://m.tkmind.cn/MindSpace/u/public/daily-news-0816.html',
),
['public/daily-news-0816.html'],
);
});
test('deliveryTextPromisesPublicHtml detects page delivery replies', () => {
assert.equal(
deliveryTextPromisesPublicHtml('今日摘要:天气不错'),
false,
);
assert.equal(
deliveryTextPromisesPublicHtml('public/daily-news.html 已生成'),
true,
);
});
test('resolveScheduledTaskDeliveryRetryDelaysMs reads env override', () => {
assert.deepEqual(
resolveScheduledTaskDeliveryRetryDelaysMs({
H5_SCHEDULED_TASK_DELIVERY_RETRY_DELAYS_MS: '0,100,250',
}),
[0, 100, 250],
);
});
test('finalizeScheduledTaskPageDelivery prepares and releases static contracts', async () => {
const calls = [];
const pool = {
async query(sql, params) {
calls.push({ sql, params });
if (sql.includes('FROM h5_page_delivery_contracts')) {
return [[{ workspace_relative_path: 'public/news.html' }]];
}
if (sql.includes('SELECT id, data_mode, status')) {
return [[{ id: 'c1', data_mode: 'static', status: 'preparing' }]];
}
if (sql.includes("SET status = 'ready'")) {
return [{ affectedRows: 1 }];
}
return [[]];
},
};
const readyPaths = await finalizeScheduledTaskPageDelivery({
pool,
userId: 'user-1',
sessionId: 'session-1',
messages: [],
publishDir: '/tmp/publish',
deliveryText: 'public/news.html 已生成',
logger: { warn() {}, info() {} },
});
assert.deepEqual(readyPaths, ['public/news.html']);
assert.ok(calls.some((call) => call.sql.includes('INSERT INTO h5_page_delivery_contracts')));
});
test('reconcileStuckStaticPageDeliveryContracts releases materialized static pages', async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'scheduled-task-'));
const userId = 'user-1';
const relativePath = 'public/news.html';
const filePath = path.join(dir, 'MindSpace', userId, relativePath);
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, '<html></html>', 'utf8');
const pool = {
async query(sql) {
if (sql.includes('FROM h5_page_delivery_contracts')) {
return [[{ user_id: userId, workspace_relative_path: relativePath }]];
}
if (sql.includes("SET status = 'ready'")) {
return [{ affectedRows: 1 }];
}
return [[]];
},
};
const released = await reconcileStuckStaticPageDeliveryContracts({
pool,
h5Root: dir,
logger: { info() {} },
});
assert.deepEqual(released, [relativePath]);
});
test('extractScheduledTaskDeliveryText reads last assistant message', () => {
const text = extractScheduledTaskDeliveryText([
{ role: 'user', content: [{ type: 'text', text: 'hi' }] },
{ role: 'assistant', content: [{ type: 'text', text: '今日新闻页:https://example.com/news.html' }] },
], { title: '每日新闻页' });
assert.match(text, /https:\/\/example.com\/news.html/);
});
test('formatScheduledTaskDeliveryMessage wraps delivery body', () => {
const text = formatScheduledTaskDeliveryMessage(
{ title: '每日新闻页' },
'页面已生成:https://example.com/news.html',
);
assert.match(text, /定时任务完成:每日新闻页/);
assert.match(text, /页面已生成/);
});