fix: guard WeChat Page Data delivery with Aider review
Memind CI / Test, build, and release guards (push) Has been cancelled
Memind CI / Test, build, and release guards (push) Has been cancelled
This commit is contained in:
@@ -9,6 +9,7 @@ import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import { createWechatMpService } from './wechat-mp.mjs';
|
||||
import { buildWechatAgentPrompt } from './wechat/prompts/chat-general.mjs';
|
||||
import { buildPageGenerateAgentPrompt } from './wechat/prompts/page-generate.mjs';
|
||||
import {
|
||||
evaluatePageDataFinishGuard,
|
||||
maybeAutoBindPageDataHtmlPages,
|
||||
@@ -151,6 +152,15 @@ test('buildWechatAgentPrompt injects page-data-collect requirements for survey r
|
||||
assert.match(prompt, /page-data-collect/);
|
||||
assert.match(prompt, /禁止 localStorage/);
|
||||
assert.match(prompt, /private_data_bind_workspace_page/);
|
||||
|
||||
const pagePrompt = buildPageGenerateAgentPrompt({
|
||||
msgType: 'text',
|
||||
agentText: '帮我创建一个可以提交的调查问卷页面,后台查看记录',
|
||||
});
|
||||
assert.match(pagePrompt, /Page Data 强制要求/);
|
||||
assert.match(pagePrompt, /private_data_execute/);
|
||||
assert.match(pagePrompt, /private_data_register_dataset/);
|
||||
assert.match(pagePrompt, /private_data_bind_workspace_page/);
|
||||
});
|
||||
|
||||
test('integration: wechat mp blocks localStorage survey delivery with page-data failure notice', async () => {
|
||||
@@ -247,6 +257,157 @@ test('integration: wechat mp blocks localStorage survey delivery with page-data
|
||||
}
|
||||
});
|
||||
|
||||
test('integration: WeChat Page Data runs Aider review before the delivery contract', async () => {
|
||||
const token = 'token';
|
||||
const timestamp = '1710000000';
|
||||
const nonce = 'nonce';
|
||||
const workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'wechat-page-data-review-'));
|
||||
const htmlPath = path.join(workspaceRoot, 'public', 'children-diet-survey.html');
|
||||
fs.mkdirSync(path.dirname(htmlPath), { recursive: true });
|
||||
fs.writeFileSync(htmlPath, VALID_SURVEY_HTML, 'utf8');
|
||||
const order = [];
|
||||
const reviewCalls = [];
|
||||
const wechatCalls = [];
|
||||
let failReview = false;
|
||||
|
||||
const service = createWechatMpService({
|
||||
config: {
|
||||
enabled: true,
|
||||
appId: 'wx123',
|
||||
appSecret: 'secret',
|
||||
token,
|
||||
publicBaseUrl: 'https://m.tkmind.cn',
|
||||
bindPath: '/auth/wechat/authorize?intent=login',
|
||||
ackText: 'ack',
|
||||
unsupportedText: 'unsupported',
|
||||
unboundTextPrefix: '请先绑定',
|
||||
progressDelayMs: 0,
|
||||
requireFreshPageThumbnail: false,
|
||||
pageDataAiderReviewEnabled: true,
|
||||
pageDataAiderReviewUsers: ['唐'],
|
||||
},
|
||||
userAuth: createWechatUserAuth(workspaceRoot),
|
||||
htmlDeliveryAuthority: {
|
||||
async prepareWechatHtmlDelivery() {
|
||||
const artifact = {
|
||||
relativePath: 'public/children-diet-survey.html',
|
||||
localPath: htmlPath,
|
||||
url: 'https://m.tkmind.cn/MindSpace/user-page-data/public/children-diet-survey.html',
|
||||
};
|
||||
return {
|
||||
publishedArtifacts: [artifact],
|
||||
expectedArtifacts: [artifact],
|
||||
recentArtifacts: [],
|
||||
confirmedArtifacts: [artifact],
|
||||
verifiedArtifacts: [artifact],
|
||||
validReplyUrls: [artifact.url],
|
||||
hasValidReplyLink: true,
|
||||
};
|
||||
},
|
||||
async ensureWechatFreshPageThumbnails() {
|
||||
return { ok: true };
|
||||
},
|
||||
},
|
||||
pageDataDeliveryReviewer: {
|
||||
async reviewIfNeeded(input) {
|
||||
order.push('aider-review');
|
||||
reviewCalls.push(input);
|
||||
if (failReview) {
|
||||
const error = new Error('review failed');
|
||||
error.code = 'PAGE_DATA_REVIEW_NOT_PASSED';
|
||||
throw error;
|
||||
}
|
||||
return {
|
||||
action: 'passed',
|
||||
executor: 'aider',
|
||||
reviewedFiles: input.relativePaths,
|
||||
};
|
||||
},
|
||||
},
|
||||
pageDataFinishGuard: {
|
||||
async prepareWechatPageDataDelivery({ reply }) {
|
||||
order.push('delivery-contract');
|
||||
return {
|
||||
outcome: { action: 'send' },
|
||||
autoBind: { errors: [] },
|
||||
deliveryArtifacts: [{
|
||||
relativePath: 'public/children-diet-survey.html',
|
||||
url: 'https://m.tkmind.cn/MindSpace/user-page-data/public/children-diet-survey.html',
|
||||
}],
|
||||
deliveryCheck: { ok: true, failures: [] },
|
||||
rewrittenText: reply?.text ?? '',
|
||||
};
|
||||
},
|
||||
},
|
||||
sessionApiFetch: async (sessionId, pathname) => {
|
||||
if (pathname === `/sessions/${sessionId}/events`) {
|
||||
return new Response(
|
||||
[
|
||||
`data: {"type":"Message","request_id":"req-page-data-review","message":{"id":"assistant-tool","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"toolRequest","toolCall":{"value":{"name":"sandbox-fs__write_file","arguments":{"path":"public/children-diet-survey.html","content":"${jsonEscapeHtml(VALID_SURVEY_HTML)}"}}}}]}}\n\n`,
|
||||
'data: {"type":"Message","request_id":"req-page-data-review","message":{"id":"assistant-1","role":"assistant","metadata":{"userVisible":true},"content":[{"type":"text","text":"问卷已发布:https://m.tkmind.cn/MindSpace/user-page-data/public/children-diet-survey.html"}]}}\n\n',
|
||||
'data: {"type":"Finish","request_id":"req-page-data-review","token_state":{"inputTokens":1,"outputTokens":1}}\n\n',
|
||||
].join(''),
|
||||
{ status: 200, headers: { 'Content-Type': 'text/event-stream' } },
|
||||
);
|
||||
}
|
||||
if (pathname === `/sessions/${sessionId}/reply`) {
|
||||
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||
}
|
||||
if (pathname === '/agent/harness_remember' || pathname === '/agent/harness_bootstrap') {
|
||||
return new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } });
|
||||
}
|
||||
throw new Error(`unexpected session api path: ${sessionId} ${pathname}`);
|
||||
},
|
||||
wechatFetch: createWechatFetchRecorder(wechatCalls),
|
||||
});
|
||||
|
||||
const originalRandomUuid = crypto.randomUUID;
|
||||
crypto.randomUUID = () => 'req-page-data-review';
|
||||
try {
|
||||
const result = await service.handleInboundMessage(inboundXml(), {
|
||||
timestamp,
|
||||
nonce,
|
||||
signature: signatureFor(token, timestamp, nonce),
|
||||
});
|
||||
assert.equal(result.status, 200);
|
||||
await result.task;
|
||||
|
||||
assert.deepEqual(order, ['aider-review', 'delivery-contract']);
|
||||
assert.equal(reviewCalls.length, 1);
|
||||
assert.equal(reviewCalls[0].sourceChannel, 'wechat_mp');
|
||||
assert.equal(reviewCalls[0].sourceMessageId, '10001');
|
||||
assert.equal(reviewCalls[0].forcePageData, true);
|
||||
assert.deepEqual(
|
||||
reviewCalls[0].relativePaths,
|
||||
['public/children-diet-survey.html'],
|
||||
);
|
||||
|
||||
failReview = true;
|
||||
order.length = 0;
|
||||
const outboundBeforeFailure = wechatCalls.length;
|
||||
const failedResult = await service.handleInboundMessage(inboundXml(), {
|
||||
timestamp,
|
||||
nonce,
|
||||
signature: signatureFor(token, timestamp, nonce),
|
||||
});
|
||||
await failedResult.task;
|
||||
assert.deepEqual(order, ['aider-review']);
|
||||
const failedSend = wechatCalls
|
||||
.slice(outboundBeforeFailure)
|
||||
.find(([url]) => String(url).includes('/cgi-bin/message/custom/send'));
|
||||
assert.ok(failedSend);
|
||||
const failedPayload = JSON.parse(failedSend[2]);
|
||||
assert.match(failedPayload.text.content, /Page Data|数据收集|绑定/);
|
||||
assert.doesNotMatch(
|
||||
failedPayload.text.content,
|
||||
/https:\/\/m\.tkmind\.cn\/MindSpace\/user-page-data\/public\/children-diet-survey\.html/,
|
||||
);
|
||||
} finally {
|
||||
crypto.randomUUID = originalRandomUuid;
|
||||
fs.rmSync(workspaceRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('integration: finish guard auto-bind clears unbound state for valid survey html', async () => {
|
||||
const workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'page-data-autobind-'));
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user