4a26db5e84
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>
166 lines
5.4 KiB
JavaScript
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/);
|
|
});
|