Files
memind/wechat/wechat-channel.test.mjs
T
john b5a88d9746 fix(brand): require TKMind platform marker on wechat page delivery
Backfill missing platform-brand footer at serve time and block service-
account links when the HTML file omits data-mindspace-page-tag.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-04 14:15:42 +08:00

190 lines
7.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 { classifyWechatIntent, isPageGenerateIntent } from './intent/classifier.mjs';
import {
filterSendableHtmlArtifacts,
isStubPublicHtmlContent,
selectSendableHtmlArtifacts,
verifyPageArtifactContent,
} from './verify/page-artifact.mjs';
import { guardScheduleConfirmationReply, looksLikeScheduleConfirmation } from './handlers/schedule-guard.mjs';
import { resolvePageGenerateOutcome } from './handlers/page-generate.mjs';
import { buildGreetingText, resolveSyncReply } from './handlers/sync-replies.mjs';
import { buildWechatAgentPrompt } from './prompts/chat-general.mjs';
import {
hasMindspaceCoverMeta,
hasPlatformBrandMarker,
hasShareDescription,
verifyArtifactSharePreview,
verifySharePreviewMeta,
} from './verify/share-preview.mjs';
test('classifyWechatIntent detects page.generate', () => {
const intent = classifyWechatIntent({
msgType: 'text',
agentText: '帮我生成一个唐诗页面,放一首诗',
});
assert.equal(intent.kind, 'page.generate');
assert.match(intent.topic, /唐诗页面/);
assert.equal(isPageGenerateIntent({ msgType: 'text', agentText: intent.topic }), true);
});
test('classifyWechatIntent detects session.reset', () => {
assert.equal(classifyWechatIntent({ msgType: 'text', agentText: '换话题' }).kind, 'session.reset');
});
test('selectSendableHtmlArtifacts never returns stub html', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-stub-'));
const stubPath = path.join(dir, 'tang-poem.html');
fs.writeFileSync(
stubPath,
'<html>服务号自动补出简版页面</html>',
'utf8',
);
const realPath = path.join(dir, 'real.html');
fs.writeFileSync(
realPath,
`<!doctype html><html><body><main>${'x'.repeat(600)}</main></body></html>`,
'utf8',
);
const artifacts = [
{ localPath: stubPath, relativePath: 'public/tang-poem.html', url: 'https://example/tang-poem.html' },
{ localPath: realPath, relativePath: 'public/real.html', url: 'https://example/real.html' },
];
const sendable = selectSendableHtmlArtifacts({ confirmedArtifacts: artifacts });
assert.equal(sendable.length, 1);
assert.match(sendable[0].relativePath, /real\.html/);
assert.equal(isStubPublicHtmlContent('<html>服务号自动补出简版页面</html>'), true);
});
test('resolveSyncReply handles greeting and status probes', () => {
const user = { nickname: '唐' };
assert.match(
resolveSyncReply(classifyWechatIntent({ msgType: 'text', agentText: '你好' }), user, {
statusFallbackText: '处理中',
}),
/唐/,
);
assert.match(
resolveSyncReply(classifyWechatIntent({ msgType: 'text', agentText: '??' }), user, {
statusFallbackText: '处理中',
}),
/我在这边/,
);
});
test('buildWechatAgentPrompt lives in wechat prompts package', () => {
const prompt = buildWechatAgentPrompt({
msgType: 'text',
agentText: '明天早上 6 点跑步,5 点半提醒我',
});
assert.match(prompt, /schedule-assistant/);
});
test('schedule confirmation guard blocks false positives', async () => {
assert.equal(looksLikeScheduleConfirmation('已经帮你设置好了待办提醒'), true);
const guarded = await guardScheduleConfirmationReply({
replyText: '已经帮你设置好了待办提醒',
scheduleService: { listItemsBySourceMessage: async () => [] },
userId: 'user-1',
sourceMessageId: 'msg-1',
logger: null,
});
assert.match(guarded, /不能算设置成功/);
});
test('resolvePageGenerateOutcome fails when only stub artifacts exist', () => {
const outcome = resolvePageGenerateOutcome({
reply: { text: '页面已生成 https://m.tkmind.cn/MindSpace/u/public/tang-poem.html' },
confirmedArtifacts: [
{
localPath: '/tmp/stub.html',
relativePath: 'public/tang-poem.html',
},
],
htmlGenerationNeedsRetry: true,
replyHasPublicLinks: true,
});
assert.equal(outcome.action, 'fail');
assert.match(outcome.failureText, /服务号页面技能/);
});
test('verifyPageArtifactContent rejects small stub files', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-verify-'));
const file = path.join(dir, 'page.html');
fs.writeFileSync(file, '<html>服务号兜底</html>', 'utf8');
const artifact = { localPath: file, relativePath: 'public/page.html' };
assert.equal(verifyPageArtifactContent(artifact).ok, false);
assert.equal(filterSendableHtmlArtifacts([artifact]).length, 0);
});
function writePreviewReadyHtml(file, { withCover = true, withBrand = true } = {}) {
const coverMeta = withCover
? '<meta name="mindspace-cover" content=\'{"tag":"旅行","accent":"#ff6b35","accent2":"#24243e","subtitle":"测试页面"}\'>'
: '';
const brandMeta = withBrand
? '<p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p>'
: '';
fs.writeFileSync(
file,
`<!doctype html><html><head>
<meta name="description" content="一句话摘要">
${coverMeta}
</head><body><main>${'x'.repeat(600)}</main>${brandMeta}</body></html>`,
'utf8',
);
}
test('verifySharePreviewMeta requires mindspace-cover, description, and platform brand', () => {
const withAll =
'<meta name="description" content="摘要"><meta name="mindspace-cover" content=\'{"tag":"旅行"}\'><p data-mindspace-page-tag="platform-brand">TKMind · 智趣</p>';
assert.equal(verifySharePreviewMeta(withAll).ok, true);
assert.equal(verifySharePreviewMeta('<meta name="description" content="摘要">').ok, false);
assert.equal(hasMindspaceCoverMeta(withAll), true);
assert.equal(hasShareDescription(withAll), true);
assert.equal(hasPlatformBrandMarker(withAll), true);
});
test('resolvePageGenerateOutcome fails when html lacks share preview meta', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-preview-'));
const file = path.join(dir, 'page.html');
writePreviewReadyHtml(file, { withCover: false });
const outcome = resolvePageGenerateOutcome({
reply: { text: '页面已生成 https://m.tkmind.cn/MindSpace/u/public/page.html' },
confirmedArtifacts: [{ localPath: file, relativePath: 'public/page.html' }],
});
assert.equal(outcome.action, 'fail');
assert.equal(outcome.reason, 'missing_mindspace_cover');
assert.match(outcome.failureText, /预览元数据/);
});
test('resolvePageGenerateOutcome fails when html lacks platform brand marker', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-brand-'));
const file = path.join(dir, 'page.html');
writePreviewReadyHtml(file, { withBrand: false });
const outcome = resolvePageGenerateOutcome({
reply: { text: '页面已生成' },
confirmedArtifacts: [{ localPath: file, relativePath: 'public/page.html' }],
});
assert.equal(outcome.action, 'fail');
assert.equal(outcome.reason, 'missing_platform_brand');
assert.match(outcome.failureText, /平台品牌/);
});
test('resolvePageGenerateOutcome sends when share preview meta is present', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-preview-ok-'));
const file = path.join(dir, 'page.html');
writePreviewReadyHtml(file);
const artifact = { localPath: file, relativePath: 'public/page.html' };
assert.equal(verifyArtifactSharePreview(artifact).ok, true);
const outcome = resolvePageGenerateOutcome({
reply: { text: '页面已生成' },
confirmedArtifacts: [artifact],
});
assert.equal(outcome.action, 'send');
assert.equal(outcome.artifacts.length, 1);
});