test: cover generation orchestration

Change-Id: Iff416f01c0f09111e67e1ad67957b85fdb19623a
This commit is contained in:
wujunchen 2026-04-26 17:59:35 +08:00
parent 2d4bef9539
commit fe05ed1a2d
4 changed files with 73 additions and 14 deletions

24
main.js

File diff suppressed because one or more lines are too long

View file

@ -749,6 +749,7 @@ export const __test = {
shouldConfirmRegenerate,
shouldSkipBatchFile,
selectBatchFiles,
summarizeDocument,
summarizeViaApi,
touchCacheEntry,
translate,

View file

@ -60,6 +60,7 @@ assert.ok(!/function addTextButton/.test(mainSource), 'UI text-button helper sho
assert.ok(!/function copyToClipboard/.test(mainSource), 'clipboard helper should live outside main.ts');
assert.strictEqual(typeof t.cardsToMarkdown, 'function');
assert.strictEqual(typeof t.cancellationNoticeKey, 'function');
assert.strictEqual(typeof t.summarizeDocument, 'function');
assert.strictEqual(typeof t.addIconButton, 'function');
assert.strictEqual(typeof t.addTextButton, 'function');
assert.strictEqual(typeof t.copyToClipboard, 'function');

View file

@ -2,6 +2,7 @@ const assert = require('assert');
const Module = require('module');
const originalLoad = Module._load;
let requestUrlMock = async () => ({ status: 200, json: {}, text: '{}' });
Module._load = function load(request, parent, isMain) {
if (request === 'obsidian') {
class Plugin {}
@ -16,7 +17,7 @@ Module._load = function load(request, parent, isMain) {
return {
Plugin, ItemView, PluginSettingTab, Setting, Notice, MarkdownView, TFile, Menu, Modal,
MarkdownRenderer: { render: async () => {} },
requestUrl: async () => ({ status: 200, json: {}, text: '{}' }),
requestUrl: (params) => requestUrlMock(params),
setIcon: () => {},
};
}
@ -25,6 +26,17 @@ Module._load = function load(request, parent, isMain) {
const t = require('../main.js').__test;
function openAiCardsResponse(cards) {
const json = {
choices: [{
message: {
content: JSON.stringify({ cards }),
},
}],
};
return { status: 200, json, text: JSON.stringify(json) };
}
// ── anchor.ts ──
assert.strictEqual(t.findLineForAnchor('', 'hello'), -1, 'empty content returns -1');
@ -298,6 +310,44 @@ assert.strictEqual(
);
assert.strictEqual(t.shouldSkipBatchFile(null, 'test', baseSettings), false, 'missing cache entry is not skipped');
async function testSummarizeDocumentAnchorSorting() {
const previousRequestUrlMock = requestUrlMock;
requestUrlMock = async () =>
openAiCardsResponse([
{ title: 'Second', anchor: 'second anchor', gist: 'G2', bullets: ['B2'] },
{ title: 'First', anchor: 'first anchor', gist: 'G1', bullets: ['B1'] },
]);
let sections;
try {
sections = await t.summarizeDocument(
'intro\nfirst anchor\nmiddle\nsecond anchor\nend',
{
...baseSettings,
streaming: false,
promptLanguage: 'en',
minCards: 1,
maxCards: 3,
maxDocChars: 10000,
},
{ phase: 'queued', onCancel: () => {}, throwIfCancelled: () => {}, setPhase: () => {} },
);
} finally {
requestUrlMock = previousRequestUrlMock;
}
assert.deepStrictEqual(
sections.map((section) => section.title),
['First', 'Second'],
'summarizeDocument sorts API cards by resolved anchor line',
);
assert.deepStrictEqual(
sections.map((section) => section.startLine),
[1, 3],
'summarizeDocument resolves anchor lines from source content',
);
}
// pruneCacheEntries
const cache = {
'a.md': { generatedAt: '2024-01-01' },
@ -554,4 +604,11 @@ try {
fsModule.existsSync = origExistsSync;
}
console.log('modules tests passed');
// Wrap the tail in an async runner so module-level tests can include async cases.
(async () => {
await testSummarizeDocumentAnchorSorting();
console.log('modules tests passed');
})().catch((e) => {
console.error(e);
process.exit(1);
});