Files
memind/wechat-news-morning-draft.test.mjs
T
john 6d76618c0b feat(wechat): add news morning draft inline HTML converter
Push daily-news pages to WeChat drafts as inline-styled HTML instead of
full-page long images, with automatic trimming to stay within the 20k limit.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 09:23:24 +08:00

172 lines
6.0 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 {
convertNewsPageHtmlToWechatArticle,
createWechatNewsMorningDraftService,
findLatestNewsMorningPage,
NEWS_MORNING_TEMPLATE_VERSION,
wechatNewsMorningDraftInternals,
} from './wechat-news-morning-draft.mjs';
import { PUBLIC_ZONE_DIR, PUBLISH_ROOT_DIR } from './user-publish.mjs';
const SAMPLE_HTML = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>每日新闻早报 · 2026年9月10日</title>
<meta name="description" content="2026年9月10日新闻热点摘要">
<style>.hero{background:#b71c1c;color:#fff;}</style>
</head>
<body>
<div class="container">
<div class="hero">
<h1>📰 每日新闻早报</h1>
<div class="date-badge">2026年9月10日 · 星期三</div>
<div class="subtitle">综合多家权威媒体梳理全网核心热点。</div>
</div>
<div class="stats">
<div class="stat s1"><b>4 大</b><span>核心热点</span></div>
</div>
<div class="section" id="headlines">
<div class="section-title">📰 今日要闻</div>
<div class="card">
<span class="tag tag-red">要闻</span>
<h3>示例热点</h3>
<p>这是示例正文。</p>
<div class="source">来源:示例媒体</div>
</div>
</div>
<div class="trending">
<h3>热榜</h3>
<div class="chips"><span class="chip"><span class="emoji">🔥</span>话题 A</span></div>
<p>今日热榜整体偏科技。</p>
</div>
</div>
</body>
</html>`;
function createPool(seedRow = null) {
const state = { row: seedRow, runs: [] };
return {
async query(sql, params) {
if (sql.includes('CREATE TABLE')) return [[], []];
if (sql.includes('SELECT config_json')) return [state.row ? [state.row] : [], []];
if (sql.includes('INSERT INTO h5_wechat_admin_config')) {
state.row = {
config_json: params[1],
updated_by: params[2],
updated_at: params[3],
};
return [[], []];
}
if (sql.includes('INSERT INTO h5_wechat_news_draft_runs')) {
state.runs.unshift({
id: params[0],
status: params[1],
pageSlug: params[2],
pageUrl: params[3],
draftMediaId: params[4],
errorMessage: params[5],
triggeredBy: params[6],
createdAt: params[7],
});
return [[], []];
}
if (sql.includes('FROM h5_wechat_news_draft_runs')) {
return [state.runs, []];
}
throw new Error(`Unexpected query: ${sql}`);
},
};
}
test('convertNewsPageHtmlToWechatArticle converts daily-news to inline html', () => {
const article = convertNewsPageHtmlToWechatArticle(SAMPLE_HTML, {
publicUrl: 'https://m.tkmind.cn/MindSpace/demo/public/daily-news-0910.html',
});
assert.match(article.title, /2026年9月10日/u);
assert.equal(article.contentMode, 'inline_html');
assert.match(article.content, /每日新闻早报/u);
assert.match(article.content, /示例热点/u);
assert.match(article.content, /点击阅读原文/u);
});
test('buildDailyNewsWechatHtmlContent preserves style and body markup', () => {
const content = wechatNewsMorningDraftInternals.buildDailyNewsWechatHtmlContent(SAMPLE_HTML, {
publicUrl: 'https://m.tkmind.cn/MindSpace/demo/public/daily-news-0910.html',
publicBaseUrl: 'https://m.tkmind.cn',
userId: 'demo-user',
});
assert.match(content, /<style>/u);
assert.match(content, /每日新闻早报/u);
assert.match(content, /class="hero"/u);
assert.match(content, /示例热点/u);
});
test('findLatestNewsMorningPage prefers dated slug for today', async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'news-draft-'));
const userId = 'user-1';
const publicDir = path.join(root, PUBLISH_ROOT_DIR, userId, PUBLIC_ZONE_DIR);
await fs.promises.mkdir(publicDir, { recursive: true });
const oldPath = path.join(publicDir, 'news-hotspots-2026-08-28.html');
const todayPath = path.join(publicDir, 'news-hotspots-2026-09-10.html');
await fs.promises.writeFile(oldPath, SAMPLE_HTML);
await fs.promises.writeFile(todayPath, SAMPLE_HTML);
await fs.promises.utimes(oldPath, new Date('2026-09-09'), new Date('2026-09-09'));
await fs.promises.utimes(todayPath, new Date('2026-09-10'), new Date('2026-09-10'));
const page = findLatestNewsMorningPage({
h5Root: root,
userId,
slugPattern: 'news-hotspots-*',
date: new Date('2026-09-10T01:00:00+08:00'),
});
assert.equal(page.slug, 'news-hotspots-2026-09-10');
});
test('news morning draft service persists config and records failed push', async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'news-draft-service-'));
const userId = 'user-2';
const publicDir = path.join(root, PUBLISH_ROOT_DIR, userId, PUBLIC_ZONE_DIR);
await fs.promises.mkdir(publicDir, { recursive: true });
await fs.promises.writeFile(path.join(publicDir, 'daily-news-0910.html'), SAMPLE_HTML);
const service = createWechatNewsMorningDraftService(createPool(), {
mpConfig: { enabled: false },
h5Root: root,
env: {},
});
const updated = await service.updateConfig(
{
enabled: true,
sourceUserId: userId,
pushHour: 6,
pushMinute: 15,
author: 'TKMind',
},
{ updatedBy: 'admin-1' },
);
assert.equal(updated.enabled, true);
assert.equal(updated.sourceUserId, userId);
assert.equal(updated.templateVersion, NEWS_MORNING_TEMPLATE_VERSION);
const preview = await service.preview();
assert.equal(preview.page.slug, 'daily-news-0910');
assert.match(preview.template.spec, /0910|2026-09-10/u);
await assert.rejects(
() => service.pushDraft({ triggeredBy: 'admin-1' }),
/微信服务号未启用/u,
);
const runs = await service.listRuns();
assert.equal(runs[0].status, 'failed');
});
test('internals normalize booleans consistently', () => {
assert.equal(wechatNewsMorningDraftInternals.normalizeBoolean('1', false), true);
assert.equal(wechatNewsMorningDraftInternals.normalizeBoolean('off', true), false);
});