mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 06:22:44 -08:00
feat(anki): add proxy transport and tokenizer annotation controls
This commit is contained in:
133
src/anki-integration/anki-connect-proxy.test.ts
Normal file
133
src/anki-integration/anki-connect-proxy.test.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { AnkiConnectProxyServer } from './anki-connect-proxy';
|
||||
|
||||
async function waitForCondition(
|
||||
condition: () => boolean,
|
||||
timeoutMs = 2000,
|
||||
intervalMs = 10,
|
||||
): Promise<void> {
|
||||
const startedAt = Date.now();
|
||||
while (Date.now() - startedAt < timeoutMs) {
|
||||
if (condition()) return;
|
||||
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
||||
}
|
||||
throw new Error('Timed out waiting for condition');
|
||||
}
|
||||
|
||||
test('proxy enqueues addNote result for enrichment', async () => {
|
||||
const processed: number[] = [];
|
||||
const proxy = new AnkiConnectProxyServer({
|
||||
shouldAutoUpdateNewCards: () => true,
|
||||
processNewCard: async (noteId) => {
|
||||
processed.push(noteId);
|
||||
},
|
||||
logInfo: () => undefined,
|
||||
logWarn: () => undefined,
|
||||
logError: () => undefined,
|
||||
});
|
||||
|
||||
(proxy as unknown as {
|
||||
maybeEnqueueFromRequest: (request: Record<string, unknown>, responseBody: Buffer) => void;
|
||||
}).maybeEnqueueFromRequest(
|
||||
{ action: 'addNote' },
|
||||
Buffer.from(JSON.stringify({ result: 42, error: null }), 'utf8'),
|
||||
);
|
||||
|
||||
await waitForCondition(() => processed.length === 1);
|
||||
assert.deepEqual(processed, [42]);
|
||||
});
|
||||
|
||||
test('proxy de-duplicates addNotes IDs within the same response', async () => {
|
||||
const processed: number[] = [];
|
||||
const proxy = new AnkiConnectProxyServer({
|
||||
shouldAutoUpdateNewCards: () => true,
|
||||
processNewCard: async (noteId) => {
|
||||
processed.push(noteId);
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
},
|
||||
logInfo: () => undefined,
|
||||
logWarn: () => undefined,
|
||||
logError: () => undefined,
|
||||
});
|
||||
|
||||
(proxy as unknown as {
|
||||
maybeEnqueueFromRequest: (request: Record<string, unknown>, responseBody: Buffer) => void;
|
||||
}).maybeEnqueueFromRequest(
|
||||
{ action: 'addNotes' },
|
||||
Buffer.from(JSON.stringify({ result: [101, 102, 101, null], error: null }), 'utf8'),
|
||||
);
|
||||
|
||||
await waitForCondition(() => processed.length === 2);
|
||||
assert.deepEqual(processed, [101, 102]);
|
||||
});
|
||||
|
||||
test('proxy skips auto-enrichment when auto-update is disabled', async () => {
|
||||
const processed: number[] = [];
|
||||
const proxy = new AnkiConnectProxyServer({
|
||||
shouldAutoUpdateNewCards: () => false,
|
||||
processNewCard: async (noteId) => {
|
||||
processed.push(noteId);
|
||||
},
|
||||
logInfo: () => undefined,
|
||||
logWarn: () => undefined,
|
||||
logError: () => undefined,
|
||||
});
|
||||
|
||||
(proxy as unknown as {
|
||||
maybeEnqueueFromRequest: (request: Record<string, unknown>, responseBody: Buffer) => void;
|
||||
}).maybeEnqueueFromRequest(
|
||||
{ action: 'addNote' },
|
||||
Buffer.from(JSON.stringify({ result: 303, error: null }), 'utf8'),
|
||||
);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 30));
|
||||
assert.deepEqual(processed, []);
|
||||
});
|
||||
|
||||
test('proxy ignores addNote when upstream response reports error', async () => {
|
||||
const processed: number[] = [];
|
||||
const proxy = new AnkiConnectProxyServer({
|
||||
shouldAutoUpdateNewCards: () => true,
|
||||
processNewCard: async (noteId) => {
|
||||
processed.push(noteId);
|
||||
},
|
||||
logInfo: () => undefined,
|
||||
logWarn: () => undefined,
|
||||
logError: () => undefined,
|
||||
});
|
||||
|
||||
(proxy as unknown as {
|
||||
maybeEnqueueFromRequest: (request: Record<string, unknown>, responseBody: Buffer) => void;
|
||||
}).maybeEnqueueFromRequest(
|
||||
{ action: 'addNote' },
|
||||
Buffer.from(JSON.stringify({ result: 123, error: 'duplicate' }), 'utf8'),
|
||||
);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 30));
|
||||
assert.deepEqual(processed, []);
|
||||
});
|
||||
|
||||
test('proxy detects self-referential loop configuration', () => {
|
||||
const proxy = new AnkiConnectProxyServer({
|
||||
shouldAutoUpdateNewCards: () => true,
|
||||
processNewCard: async () => undefined,
|
||||
logInfo: () => undefined,
|
||||
logWarn: () => undefined,
|
||||
logError: () => undefined,
|
||||
});
|
||||
|
||||
const result = (proxy as unknown as {
|
||||
isSelfReferentialProxy: (options: {
|
||||
host: string;
|
||||
port: number;
|
||||
upstreamUrl: string;
|
||||
}) => boolean;
|
||||
}).isSelfReferentialProxy({
|
||||
host: '127.0.0.1',
|
||||
port: 8766,
|
||||
upstreamUrl: 'http://localhost:8766',
|
||||
});
|
||||
|
||||
assert.equal(result, true);
|
||||
});
|
||||
Reference in New Issue
Block a user