mindspace: close authority boundaries
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
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 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/,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user