import test from 'node:test'; import assert from 'node:assert/strict'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { AnkiIntegration } from './anki-integration'; interface IntegrationTestContext { integration: AnkiIntegration; calls: { findNotes: number; notesInfo: number; }; stateDir: string; } function createIntegrationTestContext( options: { highlightEnabled?: boolean; onFindNotes?: () => Promise; onNotesInfo?: () => Promise; stateDirPrefix?: string; } = {}, ): IntegrationTestContext { const calls = { findNotes: 0, notesInfo: 0, }; const stateDir = fs.mkdtempSync( path.join(os.tmpdir(), options.stateDirPrefix ?? 'subminer-anki-integration-'), ); const knownWordCacheStatePath = path.join(stateDir, 'known-words-cache.json'); const client = { findNotes: async () => { calls.findNotes += 1; if (options.onFindNotes) { return options.onFindNotes(); } return [] as number[]; }, notesInfo: async () => { calls.notesInfo += 1; if (options.onNotesInfo) { return options.onNotesInfo(); } return [] as unknown[]; }, } as { findNotes: () => Promise; notesInfo: () => Promise; }; const integration = new AnkiIntegration( { nPlusOne: { highlightEnabled: options.highlightEnabled ?? true, }, }, {} as never, {} as never, undefined, undefined, undefined, knownWordCacheStatePath, ); const integrationWithClient = integration as unknown as { client: { findNotes: () => Promise; notesInfo: () => Promise; }; }; integrationWithClient.client = client; const privateState = integration as unknown as { knownWordsScope: string; knownWordsLastRefreshedAtMs: number; }; privateState.knownWordsScope = 'is:note'; privateState.knownWordsLastRefreshedAtMs = Date.now(); return { integration, calls, stateDir, }; } function cleanupIntegrationTestContext(ctx: IntegrationTestContext): void { fs.rmSync(ctx.stateDir, { recursive: true, force: true }); } test('AnkiIntegration.refreshKnownWordCache bypasses stale checks', async () => { const ctx = createIntegrationTestContext(); try { await ctx.integration.refreshKnownWordCache(); assert.equal(ctx.calls.findNotes, 1); assert.equal(ctx.calls.notesInfo, 0); } finally { cleanupIntegrationTestContext(ctx); } }); test('AnkiIntegration.refreshKnownWordCache skips work when highlight mode is disabled', async () => { const ctx = createIntegrationTestContext({ highlightEnabled: false, stateDirPrefix: 'subminer-anki-integration-disabled-', }); try { await ctx.integration.refreshKnownWordCache(); assert.equal(ctx.calls.findNotes, 0); assert.equal(ctx.calls.notesInfo, 0); } finally { cleanupIntegrationTestContext(ctx); } }); test('AnkiIntegration.refreshKnownWordCache deduplicates concurrent refreshes', async () => { let releaseFindNotes: (() => void) | undefined; const findNotesPromise = new Promise((resolve) => { releaseFindNotes = resolve; }); const ctx = createIntegrationTestContext({ onFindNotes: async () => { await findNotesPromise; return [] as number[]; }, stateDirPrefix: 'subminer-anki-integration-concurrent-', }); const first = ctx.integration.refreshKnownWordCache(); await Promise.resolve(); const second = ctx.integration.refreshKnownWordCache(); if (releaseFindNotes !== undefined) { releaseFindNotes(); } await Promise.all([first, second]); try { assert.equal(ctx.calls.findNotes, 1); assert.equal(ctx.calls.notesInfo, 0); } finally { cleanupIntegrationTestContext(ctx); } });