Files
memind/mindspace-wechat-mp-config.test.mjs
T
john 78b7d546c2 feat(mindspace): add WeChat MP config and one-click draft push on publish.
Let users bind their own official account credentials in M 配置 and push MindSpace pages to the WeChat draft box during or after publish, reusing the existing draft conversion pipeline.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 16:43:25 +08:00

112 lines
3.5 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
createMindSpaceWechatMpConfigService,
fetchWechatMpAccessToken,
mindspaceWechatMpConfigInternals,
} from './mindspace-wechat-mp-config.mjs';
function createPool() {
const rows = new Map();
return {
rows,
async query(sql, params = []) {
if (sql.includes('CREATE TABLE')) return [[]];
if (sql.includes('INSERT INTO h5_user_wechat_mp_configs')) {
rows.set(params[0], {
user_id: params[0],
app_id: params[1],
account_type: params[2],
label: params[3],
author: params[4],
secret_ciphertext: params[5],
secret_iv: params[6],
secret_tag: params[7],
verified_at: params[8],
created_at: params[9],
updated_at: params[10],
});
return [{ affectedRows: 1 }];
}
if (sql.includes('FROM h5_user_wechat_mp_configs')) {
const row = rows.get(params[0]);
return [[row ?? null].filter(Boolean)];
}
if (sql.startsWith('UPDATE h5_user_wechat_mp_configs')) {
const row = rows.get(params[2]);
if (row) {
row.verified_at = params[0];
row.updated_at = params[1];
}
return [{ affectedRows: 1 }];
}
if (sql.startsWith('DELETE FROM h5_user_wechat_mp_configs')) {
rows.delete(params[0]);
return [{ affectedRows: 1 }];
}
throw new Error(`unexpected sql: ${sql}`);
},
};
}
test('upsertConfig stores encrypted secret and returns masked config', async () => {
const pool = createPool();
const service = createMindSpaceWechatMpConfigService(pool, {
env: { H5_SETTINGS_ENCRYPTION_KEY: 'test-key' },
wechatFetch: async () => ({
ok: true,
text: async () => JSON.stringify({ access_token: 'token-1', expires_in: 7200 }),
}),
});
const config = await service.upsertConfig('user-1', {
appId: 'wx1234567890',
appSecret: 'secret-value-1234567890',
accountType: 'service',
label: '我的服务号',
author: 'TKMind',
});
assert.equal(config.configured, true);
assert.equal(config.appId, 'wx1234567890');
assert.ok(config.appSecretMasked.includes('*'));
assert.ok(config.appSecretMasked.endsWith('7890'));
assert.equal(config.label, '我的服务号');
assert.ok(config.verifiedAt);
});
test('verifyConfig refreshes verifiedAt', async () => {
const pool = createPool();
const service = createMindSpaceWechatMpConfigService(pool, {
env: { H5_SETTINGS_ENCRYPTION_KEY: 'test-key' },
wechatFetch: async () => ({
ok: true,
text: async () => JSON.stringify({ access_token: 'token-2', expires_in: 7200 }),
}),
});
await service.upsertConfig('user-1', {
appId: 'wx1234567890',
appSecret: 'secret-value-1234567890',
});
const verified = await service.verifyConfig('user-1');
assert.ok(verified.verifiedAt);
});
test('fetchWechatMpAccessToken surfaces upstream error', async () => {
await assert.rejects(
() => fetchWechatMpAccessToken(
{ appId: 'bad', appSecret: 'bad' },
{
wechatFetch: async () => ({
ok: true,
text: async () => JSON.stringify({ errcode: 40013, errmsg: 'invalid appid' }),
}),
},
),
/invalid appid/,
);
});
test('normalizeAccountType defaults to service', () => {
assert.equal(mindspaceWechatMpConfigInternals.normalizeAccountType('subscription'), 'subscription');
assert.equal(mindspaceWechatMpConfigInternals.normalizeAccountType('unknown'), 'service');
});