#!/usr/bin/env node import assert from 'node:assert/strict'; import { formatRankedNewsCandidates, resolveNewsEngineEndpoint, } from '../news-engine-client.mjs'; import { prefetchNewsMorningSearxngBundle } from '../wechat-news-morning-search.mjs'; const env = process.env; const newsEngineUrl = resolveNewsEngineEndpoint(env); const searxngUrl = String(env.TKMIND_SEARCH_SEARXNG_URL ?? '').trim(); if (!newsEngineUrl) { console.error('[verify-news-engine] 缺少 MEMIND_NEWS_ENGINE_URL'); process.exit(1); } if (!searxngUrl) { console.error('[verify-news-engine] 缺少 TKMIND_SEARCH_SEARXNG_URL'); process.exit(1); } console.log('[verify-news-engine] news-engine:', newsEngineUrl); console.log('[verify-news-engine] searxng:', searxngUrl); const health = await fetch(`${newsEngineUrl}/health`).catch(() => null); assert.ok(health?.ok, 'news-engine /health 不可用'); const healthBody = await health.json(); assert.equal(healthBody.service, 'memind-news-engine'); const started = Date.now(); const bundle = await prefetchNewsMorningSearxngBundle({ env, limit: 3, }); const elapsedMs = Date.now() - started; assert.ok(Array.isArray(bundle.groups), 'bundle.groups 必须是数组'); assert.ok(bundle.groups.some((group) => group.results?.length > 0), '至少一个栏目应有预取结果'); assert.ok(bundle.ranking, 'ranking 不应为空'); assert.ok(Number(bundle.ranking.eventCount) >= 1, 'eventCount 应 >= 1'); assert.ok(Array.isArray(bundle.ranking.selected), 'selected 必须是数组'); assert.ok(bundle.ranking.selected.length > 0, 'news-engine 应返回至少 1 个选中事件'); const rankedBlock = formatRankedNewsCandidates(bundle.ranking); assert.match(bundle.brief, /News Engine 编辑候选/u); assert.match(rankedBlock, /must_include=/u); console.log('[verify-news-engine] ok', { elapsedMs, candidateCount: bundle.ranking.candidateCount, eventCount: bundle.ranking.eventCount, selectedCount: bundle.ranking.selected.length, dedupeRemovedCount: bundle.dedupeRemovedCount, dateLabel: bundle.dateLabel?.iso, }); for (const event of bundle.ranking.selected.slice(0, 5)) { console.log( ` #${event.rank} [${event.category}] score=${event.finalScore} sources=${event.sourceCount} must=${event.mustInclude} ${event.canonicalTitle}`, ); }