177 lines
6.9 KiB
JavaScript
177 lines
6.9 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 {
|
|
collectWechatGeneratedImages,
|
|
extractMindspaceCoverPath,
|
|
repairUnambiguousFreshWechatPageThumbnail,
|
|
verifyFreshWechatPageThumbnails,
|
|
} from './generated-thumbnail.mjs';
|
|
|
|
function generatedImageMessages({ jobId = 'job-1', htmlSrc = 'images/fresh.webp' } = {}) {
|
|
const result = {
|
|
ok: true,
|
|
purpose: 'hero',
|
|
jobId,
|
|
source: { mimeType: 'image/webp' },
|
|
asset: {
|
|
id: `asset-${jobId}`,
|
|
htmlSrc,
|
|
publicUrl: `https://example.com/MindSpace/user/public/${htmlSrc}`,
|
|
workspaceRelativePath: `public/${htmlSrc}`,
|
|
},
|
|
};
|
|
return [{
|
|
role: 'assistant',
|
|
content: [
|
|
{
|
|
id: `call-${jobId}`,
|
|
type: 'toolRequest',
|
|
toolCall: { value: { name: 'sandbox-fs__generate_image', arguments: { purpose: 'hero' } } },
|
|
},
|
|
{
|
|
id: `call-${jobId}`,
|
|
type: 'toolResponse',
|
|
toolResult: {
|
|
status: 'success',
|
|
value: { content: [{ type: 'text', text: JSON.stringify(result) }] },
|
|
},
|
|
},
|
|
],
|
|
}];
|
|
}
|
|
|
|
test('fresh page thumbnail must reference a current-run generated image', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-fresh-cover-'));
|
|
try {
|
|
const htmlPath = path.join(root, 'page.html');
|
|
fs.writeFileSync(
|
|
htmlPath,
|
|
'<meta name="mindspace-cover" content=\'{"cover":"images/fresh.webp"}\'>',
|
|
'utf8',
|
|
);
|
|
const images = collectWechatGeneratedImages(generatedImageMessages());
|
|
assert.equal(images.length, 1);
|
|
assert.equal(extractMindspaceCoverPath(fs.readFileSync(htmlPath, 'utf8')), 'images/fresh.webp');
|
|
const result = verifyFreshWechatPageThumbnails(
|
|
[{ localPath: htmlPath, relativePath: 'public/page.html' }],
|
|
images,
|
|
);
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.matches[0].image.jobId, 'job-1');
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('old cover path and sharing one generated image across two pages fail closed', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-stale-cover-'));
|
|
try {
|
|
const first = path.join(root, 'one.html');
|
|
const second = path.join(root, 'two.html');
|
|
fs.writeFileSync(first, '<meta name="mindspace-cover" content=\'{"cover":"images/fresh.webp"}\'>');
|
|
fs.writeFileSync(second, '<meta name="mindspace-cover" content=\'{"cover":"images/fresh.webp"}\'>');
|
|
const images = collectWechatGeneratedImages(generatedImageMessages());
|
|
const reused = verifyFreshWechatPageThumbnails(
|
|
[
|
|
{ localPath: first, relativePath: 'public/one.html' },
|
|
{ localPath: second, relativePath: 'public/two.html' },
|
|
],
|
|
images,
|
|
);
|
|
assert.equal(reused.ok, false);
|
|
assert.equal(reused.reason, 'cover_not_from_current_run');
|
|
|
|
fs.writeFileSync(first, '<meta name="mindspace-cover" content=\'{"cover":"images/old.webp"}\'>');
|
|
const stale = verifyFreshWechatPageThumbnails(
|
|
[{ localPath: first, relativePath: 'public/one.html' }],
|
|
images,
|
|
);
|
|
assert.equal(stale.ok, false);
|
|
assert.equal(stale.reason, 'cover_not_from_current_run');
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('auxiliary admin pages do not require a separate generated thumbnail', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-aux-cover-'));
|
|
try {
|
|
const front = path.join(root, 'survey.html');
|
|
const admin = path.join(root, 'survey-admin.html');
|
|
fs.writeFileSync(front, '<meta name="mindspace-cover" content=\'{"cover":"images/fresh.webp"}\'>');
|
|
fs.writeFileSync(admin, '<meta name="mindspace-page-role" content="auxiliary">');
|
|
const result = verifyFreshWechatPageThumbnails(
|
|
[
|
|
{ localPath: front, relativePath: 'public/survey.html' },
|
|
{ localPath: admin, relativePath: 'public/survey-admin.html' },
|
|
],
|
|
collectWechatGeneratedImages(generatedImageMessages()),
|
|
);
|
|
assert.equal(result.ok, true);
|
|
assert.equal(result.matches.length, 1);
|
|
assert.equal(result.skippedArtifacts.length, 1);
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('one current-run hero repairs one page from the current write snapshot and re-verifies', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-repair-cover-'));
|
|
try {
|
|
const htmlPath = path.join(root, 'page.html');
|
|
const diskHtml = '<html><head><meta name="mindspace-cover" content=\'{"tag":"页面","subtitle":"磁盘旧副本"}\'></head><body>disk copy</body></html>';
|
|
const currentRunHtml = '<html><head><meta name="mindspace-cover" content=\'{"tag":"页面","subtitle":"本轮副本"}\'></head><body>current run copy</body></html>';
|
|
fs.writeFileSync(htmlPath, diskHtml, 'utf8');
|
|
const artifacts = [{ localPath: htmlPath, relativePath: 'public/page.html' }];
|
|
const images = collectWechatGeneratedImages(generatedImageMessages());
|
|
|
|
const failed = verifyFreshWechatPageThumbnails(artifacts, images);
|
|
assert.equal(failed.reason, 'missing_generated_cover');
|
|
const repaired = repairUnambiguousFreshWechatPageThumbnail({
|
|
artifacts,
|
|
images,
|
|
currentRunHtmlArtifacts: [{ relativePath: 'public/page.html', content: currentRunHtml }],
|
|
verificationReason: failed.reason,
|
|
});
|
|
|
|
assert.equal(repaired.ok, true);
|
|
assert.equal(repaired.restoredCurrentRunHtml, true);
|
|
const written = fs.readFileSync(htmlPath, 'utf8');
|
|
assert.match(written, /current run copy/);
|
|
assert.doesNotMatch(written, /disk copy/);
|
|
assert.equal(extractMindspaceCoverPath(written), 'images/fresh.webp');
|
|
assert.equal(verifyFreshWechatPageThumbnails(artifacts, images).ok, true);
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('thumbnail repair refuses an ambiguous page-to-image mapping without changing the page', () => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-ambiguous-repair-'));
|
|
try {
|
|
const htmlPath = path.join(root, 'page.html');
|
|
const original = '<meta name="mindspace-cover" content=\'{"tag":"页面"}\'>';
|
|
fs.writeFileSync(htmlPath, original, 'utf8');
|
|
const artifacts = [{ localPath: htmlPath, relativePath: 'public/page.html' }];
|
|
const images = collectWechatGeneratedImages([
|
|
...generatedImageMessages({ jobId: 'job-1', htmlSrc: 'images/one.webp' }),
|
|
...generatedImageMessages({ jobId: 'job-2', htmlSrc: 'images/two.webp' }),
|
|
]);
|
|
|
|
const repaired = repairUnambiguousFreshWechatPageThumbnail({
|
|
artifacts,
|
|
images,
|
|
verificationReason: 'missing_generated_cover',
|
|
});
|
|
|
|
assert.equal(repaired.ok, false);
|
|
assert.equal(repaired.reason, 'ambiguous_page_image_mapping');
|
|
assert.equal(fs.readFileSync(htmlPath, 'utf8'), original);
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|