Files
memind/news-engine-client.test.mjs
T
john 4a26db5e84 feat(news): ship news002 fill gate and full WeChat draft push
Lock scheduled morning news to news002 with section-fill gates, headline lead-card normalization, SearXNG top-up to max, and push that keeps imageless cards as text while requiring uploaded images where present.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 14:05:40 +08:00

166 lines
5.4 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyRankingImagesToGroups,
downloadNewsEngineMediaBytes,
fetchLatestCollectionFromEngine,
formatRankedNewsCandidates,
isNewsEngineMediaUrl,
rankNewsWithEngine,
resolveNewsEngineEndpoint,
shouldUseNewsEngineLatestCollection,
} from './news-engine-client.mjs';
test('news engine client is disabled without an endpoint', async () => {
assert.equal(resolveNewsEngineEndpoint({}), '');
const ranking = await rankNewsWithEngine([], {
env: {},
fetchImpl: async () => { throw new Error('must not fetch'); },
});
assert.deepEqual(ranking.selected, []);
});
test('news engine client sends groups and authentication to the independent service', async () => {
const calls = [];
const ranking = await rankNewsWithEngine([{ id: 'tech', results: [] }], {
now: 123,
env: {
MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092/',
MEMIND_NEWS_ENGINE_API_TOKEN: 'service-token',
MEMIND_NEWS_ENGINE_DAILY_LIMIT: '12',
},
fetchImpl: async (url, init) => {
calls.push({ url, init, body: JSON.parse(init.body) });
return {
ok: true,
async json() {
return {
candidateCount: 1,
eventCount: 1,
events: [],
selected: [{
rank: 1,
category: 'ai_tech',
canonicalTitle: '模型发布',
finalScore: 8.8,
sourceCount: 2,
mustInclude: true,
primaryArticle: { sourceDomain: 'example.com', url: 'https://example.com/a' },
}],
};
},
};
},
});
assert.equal(calls[0].url, 'http://127.0.0.1:8092/v1/rank');
assert.equal(calls[0].init.headers.authorization, 'Bearer service-token');
assert.equal(calls[0].body.options.limit, 12);
assert.equal(ranking.selected[0].mustInclude, true);
assert.match(formatRankedNewsCandidates(ranking), /模型发布/);
});
test('isNewsEngineMediaUrl detects local news-engine media paths', () => {
assert.equal(isNewsEngineMediaUrl('/media/abc.jpg'), true);
assert.equal(
isNewsEngineMediaUrl('http://127.0.0.1:8092/media/abc.jpg', { MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092' }),
true,
);
assert.equal(isNewsEngineMediaUrl('https://cdn.example.com/a.jpg'), false);
});
test('applyRankingImagesToGroups merges primaryArticle images by url', () => {
const groups = applyRankingImagesToGroups([{
id: 'tech',
results: [{ title: 'A', url: 'https://example.com/a' }],
}], {
selected: [{
primaryArticle: {
url: 'https://example.com/a',
image: 'http://127.0.0.1:8092/media/abc.jpg',
},
}],
});
assert.equal(groups[0].results[0].image, 'http://127.0.0.1:8092/media/abc.jpg');
assert.equal(groups[0].results[0].imageSource, 'news-engine');
});
test('shouldUseNewsEngineLatestCollection requires endpoint', () => {
assert.equal(shouldUseNewsEngineLatestCollection({}), false);
assert.equal(
shouldUseNewsEngineLatestCollection({ MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092' }),
true,
);
assert.equal(
shouldUseNewsEngineLatestCollection({
MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092',
MEMIND_NEWS_ENGINE_USE_LATEST_COLLECTION: 'false',
}),
false,
);
});
test('fetchLatestCollectionFromEngine returns latest snapshot', async () => {
const collection = await fetchLatestCollectionFromEngine({
env: {
MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092',
MEMIND_NEWS_ENGINE_API_TOKEN: 'token',
},
fetchImpl: async (url, init) => {
assert.equal(url, 'http://127.0.0.1:8092/v1/collections/latest');
assert.equal(init.headers.authorization, 'Bearer token');
return {
ok: true,
status: 200,
async json() {
return { id: 'c1', status: 'ok', dateLabel: '2026-09-22', groups: [{ id: 'tech', results: [{}] }] };
},
};
},
});
assert.equal(collection.id, 'c1');
});
test('downloadNewsEngineMediaBytes fetches /media assets from news-engine', async () => {
const bytes = await downloadNewsEngineMediaBytes('http://127.0.0.1:8092/media/abc.jpg', {
env: { MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092' },
fetchImpl: async (url) => {
assert.equal(url, 'http://127.0.0.1:8092/media/abc.jpg');
return {
ok: true,
arrayBuffer: async () => Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0, 0, 0, 0, 0]),
};
},
});
assert.ok(Buffer.isBuffer(bytes));
});
test('formatRankedNewsCandidates includes local image urls', () => {
const block = formatRankedNewsCandidates({
selected: [{
rank: 1,
category: 'tech',
canonicalTitle: '测试',
finalScore: 8,
sourceCount: 1,
mustInclude: false,
primaryArticle: {
url: 'https://example.com/a',
image: 'http://127.0.0.1:8092/media/abc.jpg',
sourceDomain: 'example.com',
},
}],
});
assert.match(block, /图:http:\/\/127\.0\.0\.1:8092\/media\/abc\.jpg/);
});
test('news engine client fails open when the service is unavailable', async () => {
const warnings = [];
const ranking = await rankNewsWithEngine([], {
env: { MEMIND_NEWS_ENGINE_URL: 'http://127.0.0.1:8092' },
fetchImpl: async () => { throw new Error('offline'); },
logger: { warn: (...args) => warnings.push(args) },
});
assert.deepEqual(ranking.selected, []);
assert.match(String(warnings[0]), /offline/);
});