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'); });