843e4d1002
Memind CI / Test, build, and release guards (push) Successful in 2m58s
Prevent duplicate active scheduled tasks, fail static preparing contracts when HTML is missing, raise MindSpace remote timeout default to 30s, and add 103 repair scripts for inspection follow-ups. Co-authored-by: Cursor <cursoragent@cursor.com>
182 lines
6.1 KiB
JavaScript
182 lines
6.1 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('reconcileStuckStaticPageDeliveryContracts fails orphan contracts without html', async () => {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'scheduled-task-orphan-'));
|
|
const userId = 'user-1';
|
|
const relativePath = 'public/missing.html';
|
|
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 = 'failed'")) {
|
|
return [{ affectedRows: 1 }];
|
|
}
|
|
return [[]];
|
|
},
|
|
};
|
|
const released = await reconcileStuckStaticPageDeliveryContracts({
|
|
pool,
|
|
h5Root: dir,
|
|
logger: { info() {} },
|
|
});
|
|
assert.deepEqual(released, []);
|
|
});
|
|
|
|
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, /页面已生成/);
|
|
});
|