Files
memind/mindspace-wechat-html-delivery.test.mjs
john 065b1588b7 fix: deliver WeChat pages when image_make fails
Fall back to workspace share thumbnails so already-published HTML can be
sent instead of blocking on REQUIRED_FRESH. Also allow an explicit
ALLOW_DIRECT_STABLE_RELEASE path matching the pre-2026-07-23 direct
stable packaging flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 18:19:29 +08:00

388 lines
8.5 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import {
ensureWechatFreshPageThumbnailsAtWorkspace,
prepareWechatHtmlDeliveryAtWorkspace,
} from './mindspace-wechat-html-delivery.mjs';
function pageHtml({
title = 'Page',
cover = '',
} = {}) {
const coverField = cover
? `,"cover":"${cover}"`
: '';
return [
'<!doctype html><html><head>',
`<title>${title}</title>`,
`<meta name="mindspace-cover" content='{"tag":"页面","accent":"#3366cc","accent2":"#112233","subtitle":"测试页面"${coverField}}'>`,
'</head><body><main>',
'x'.repeat(600),
'</main></body></html>',
].join('');
}
function canonical(relativePath) {
return `https://example.com/MindSpace/user-1/${relativePath}`;
}
test('MindSpace prepares WeChat HTML delivery with logical artifacts only', (t) => {
const publishDir = fs.mkdtempSync(
path.join(
os.tmpdir(),
'mindspace-wechat-html-',
),
);
t.after(() => {
fs.rmSync(publishDir, {
recursive: true,
force: true,
});
});
const content = pageHtml({
title: 'Hello',
});
const result =
prepareWechatHtmlDeliveryAtWorkspace({
publishDir,
buildCanonicalUrl: canonical,
requestStartedAt: Date.now() - 100,
reply: {
text:
'页面完成:https://wrong.example/MindSpace/user-1/public/hello.html',
messages: [
{
id: 'assistant-1',
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'write_file',
arguments: {
path:
'public/hello.html',
content,
},
},
},
},
],
},
],
},
intent: {
agentText:
'生成 public/hello.html',
},
});
assert.equal(
fs.readFileSync(
path.join(
publishDir,
'public',
'hello.html',
),
'utf8',
),
content,
);
assert.equal(
result.confirmedArtifacts.length,
1,
);
assert.deepEqual(
result.confirmedArtifacts[0],
{
relativePath: 'public/hello.html',
url: canonical('public/hello.html'),
canonicalUrl:
canonical('public/hello.html'),
exists: true,
isStub: false,
isHtmlDocument: true,
sharePreview: {
ok: false,
reason: 'missing_description',
},
auxiliary: false,
sizeBytes:
Buffer.byteLength(content),
},
);
assert.equal(
Object.hasOwn(
result.confirmedArtifacts[0],
'localPath',
),
false,
);
assert.equal(result.hasValidReplyLink, true);
assert.deepEqual(result.validReplyUrls, [
'https://wrong.example/MindSpace/user-1/public/hello.html',
]);
});
test('MindSpace copies a generated root HTML file into public before delivery', (t) => {
const publishDir = fs.mkdtempSync(
path.join(
os.tmpdir(),
'mindspace-wechat-root-html-',
),
);
t.after(() => {
fs.rmSync(publishDir, {
recursive: true,
force: true,
});
});
fs.writeFileSync(
path.join(publishDir, 'guide.html'),
pageHtml({ title: 'Guide' }),
'utf8',
);
const result =
prepareWechatHtmlDeliveryAtWorkspace({
publishDir,
buildCanonicalUrl: canonical,
reply: {
text: 'done',
messages: [
{
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'developer',
arguments: {
action: 'write',
path: 'guide.html',
},
},
},
},
],
},
],
},
intent: {
agentText: '生成 guide.html',
},
allowRecentArtifacts: false,
});
assert.equal(
fs.existsSync(
path.join(
publishDir,
'public',
'guide.html',
),
),
true,
);
assert.equal(
result.publishedArtifacts[0]
.relativePath,
'public/guide.html',
);
});
test('MindSpace verifies and renders fresh WeChat page thumbnails without returning paths', async (t) => {
const publishDir = fs.mkdtempSync(
path.join(
os.tmpdir(),
'mindspace-wechat-thumbnail-',
),
);
t.after(() => {
fs.rmSync(publishDir, {
recursive: true,
force: true,
});
});
const relativePath = 'public/fresh.html';
const htmlPath =
path.join(publishDir, relativePath);
fs.mkdirSync(path.dirname(htmlPath), {
recursive: true,
});
fs.writeFileSync(
htmlPath,
pageHtml({
title: 'Fresh',
cover: 'fresh.png',
}),
'utf8',
);
const result =
await ensureWechatFreshPageThumbnailsAtWorkspace(
{
publishDir,
buildCanonicalUrl: canonical,
artifacts: [
{
relativePath,
exists: true,
},
],
images: [
{
jobId: 'job-1',
purpose: 'hero',
mimeType: 'image/png',
htmlSrc: 'fresh.png',
},
],
},
);
assert.equal(result.ok, true);
assert.deepEqual(
result.matchRelativePaths,
[relativePath],
);
assert.deepEqual(
result.thumbnailRelativePaths,
['public/fresh.thumbnail.svg'],
);
assert.equal(
fs.existsSync(
path.join(
publishDir,
'public',
'fresh.thumbnail.svg',
),
),
true,
);
assert.equal(
JSON.stringify(result).includes(
publishDir,
),
false,
);
});
test('MindSpace falls back to workspace thumbnails when fresh image_make is missing', async (t) => {
const publishDir = fs.mkdtempSync(
path.join(
os.tmpdir(),
'mindspace-wechat-thumbnail-fallback-',
),
);
t.after(() => {
fs.rmSync(publishDir, {
recursive: true,
force: true,
});
});
const relativePath = 'public/fallback.html';
const htmlPath = path.join(publishDir, relativePath);
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
fs.writeFileSync(
htmlPath,
pageHtml({ title: 'Fallback' }),
'utf8',
);
const result = await ensureWechatFreshPageThumbnailsAtWorkspace({
publishDir,
buildCanonicalUrl: canonical,
artifacts: [{ relativePath, exists: true }],
images: [],
});
assert.equal(result.ok, true);
assert.equal(result.reason, 'workspace_thumbnail_fallback');
assert.deepEqual(result.matchRelativePaths, [relativePath]);
assert.equal(
fs.existsSync(path.join(publishDir, 'public', 'fallback.thumbnail.svg')),
true,
);
});
test('MindSpace owns unambiguous cover repair before thumbnail delivery', async (t) => {
const publishDir = fs.mkdtempSync(
path.join(
os.tmpdir(),
'mindspace-wechat-repair-',
),
);
t.after(() => {
fs.rmSync(publishDir, {
recursive: true,
force: true,
});
});
const relativePath =
'public/repaired.html';
const htmlPath =
path.join(publishDir, relativePath);
fs.mkdirSync(path.dirname(htmlPath), {
recursive: true,
});
const original = pageHtml({
title: 'Repair',
});
fs.writeFileSync(
htmlPath,
original,
'utf8',
);
const result =
await ensureWechatFreshPageThumbnailsAtWorkspace(
{
publishDir,
buildCanonicalUrl: canonical,
artifacts: [{ relativePath }],
images: [
{
jobId: 'job-repair',
purpose: 'hero',
mimeType: 'image/png',
htmlSrc: 'repair.png',
},
],
messages: [
{
role: 'assistant',
content: [
{
type: 'toolRequest',
toolCall: {
value: {
name: 'write_file',
arguments: {
path: relativePath,
content: original,
},
},
},
},
],
},
],
repairEnabled: true,
},
);
assert.equal(result.ok, true);
assert.equal(result.repair.ok, true);
assert.equal(
result.repair.relativePath,
relativePath,
);
assert.match(
fs.readFileSync(htmlPath, 'utf8'),
/repair\.png/,
);
});