mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-08-12 13:55:51 -07:00
106 lines
4.8 KiB
TypeScript
106 lines
4.8 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
|
|
import type { AnkiConnectConfig } from '../types/anki';
|
|
import {
|
|
DEFAULT_MATURE_INTERVAL_THRESHOLD_DAYS,
|
|
buildKnownWordMaturityTierQueries,
|
|
classifyKnownWordNoteTier,
|
|
getKnownWordMaturityEnabled,
|
|
getMatureIntervalThresholdDays,
|
|
maxKnownWordMaturityTier,
|
|
sanitizeKnownWordMaturityTier,
|
|
} from './known-word-maturity';
|
|
|
|
function makeConfig(knownWords: AnkiConnectConfig['knownWords']): AnkiConnectConfig {
|
|
return { url: 'http://127.0.0.1:8765', knownWords } as AnkiConnectConfig;
|
|
}
|
|
|
|
test('maturity is enabled only when both highlight and maturity flags are on', () => {
|
|
assert.equal(
|
|
getKnownWordMaturityEnabled(makeConfig({ highlightEnabled: true, maturityEnabled: true })),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
getKnownWordMaturityEnabled(makeConfig({ highlightEnabled: false, maturityEnabled: true })),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
getKnownWordMaturityEnabled(makeConfig({ highlightEnabled: true, maturityEnabled: false })),
|
|
false,
|
|
);
|
|
assert.equal(getKnownWordMaturityEnabled(makeConfig({ highlightEnabled: true })), false);
|
|
assert.equal(getKnownWordMaturityEnabled(makeConfig(undefined)), false);
|
|
});
|
|
|
|
test('mature threshold falls back to default for invalid values', () => {
|
|
assert.equal(DEFAULT_MATURE_INTERVAL_THRESHOLD_DAYS, 21);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 30 })), 30);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 14.9 })), 14);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: 0 })), 21);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: -5 })), 21);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({ matureThresholdDays: Number.NaN })), 21);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig({})), 21);
|
|
assert.equal(getMatureIntervalThresholdDays(makeConfig(undefined)), 21);
|
|
});
|
|
|
|
test('tier queries append Anki search props to a deck scope query', () => {
|
|
const queries = buildKnownWordMaturityTierQueries('deck:"Mining"', 21);
|
|
assert.equal(queries.mature, 'deck:"Mining" prop:ivl>=21 -is:learn');
|
|
assert.equal(queries.young, 'deck:"Mining" prop:ivl>=1 prop:ivl<21 -is:learn');
|
|
assert.equal(queries.learning, 'deck:"Mining" is:learn');
|
|
});
|
|
|
|
test('interval tiers exclude (re)learning cards so the buckets stay disjoint', () => {
|
|
const queries = buildKnownWordMaturityTierQueries('deck:"Mining"', 21);
|
|
// A lapsed card keeps an interval of at least the lapse minInt (>= 1), so
|
|
// without the exclusion the young query would claim every relearning card
|
|
// and the learning tier could only ever match brand-new cards mid-step.
|
|
for (const intervalQuery of [queries.mature, queries.young]) {
|
|
assert.ok(intervalQuery.includes('-is:learn'));
|
|
}
|
|
assert.equal(queries.learning, 'deck:"Mining" is:learn');
|
|
});
|
|
|
|
test('tier queries with an empty scope query have no leading space', () => {
|
|
const queries = buildKnownWordMaturityTierQueries('', 30);
|
|
assert.equal(queries.mature, 'prop:ivl>=30 -is:learn');
|
|
assert.equal(queries.young, 'prop:ivl>=1 prop:ivl<30 -is:learn');
|
|
assert.equal(queries.learning, 'is:learn');
|
|
});
|
|
|
|
test('note classification picks the most mature matching tier', () => {
|
|
const sets = {
|
|
mature: new Set([1, 4]),
|
|
young: new Set([2, 4]),
|
|
learning: new Set([3, 4, 2]),
|
|
};
|
|
assert.equal(classifyKnownWordNoteTier(1, sets), 'mature');
|
|
assert.equal(classifyKnownWordNoteTier(2, sets), 'young');
|
|
assert.equal(classifyKnownWordNoteTier(3, sets), 'learning');
|
|
// Note with mature, young, and learning cards: most mature card wins.
|
|
assert.equal(classifyKnownWordNoteTier(4, sets), 'mature');
|
|
assert.equal(classifyKnownWordNoteTier(99, sets), 'new');
|
|
});
|
|
|
|
test('maxKnownWordMaturityTier picks the higher tier and tolerates null', () => {
|
|
assert.equal(maxKnownWordMaturityTier('mature', 'new'), 'mature');
|
|
assert.equal(maxKnownWordMaturityTier('learning', 'young'), 'young');
|
|
assert.equal(maxKnownWordMaturityTier('new', null), 'new');
|
|
assert.equal(maxKnownWordMaturityTier(null, 'learning'), 'learning');
|
|
assert.equal(maxKnownWordMaturityTier(null, null), null);
|
|
assert.equal(maxKnownWordMaturityTier(undefined, undefined), null);
|
|
});
|
|
|
|
test('sanitizeKnownWordMaturityTier accepts only valid tiers', () => {
|
|
assert.equal(sanitizeKnownWordMaturityTier('mature'), 'mature');
|
|
assert.equal(sanitizeKnownWordMaturityTier('young'), 'young');
|
|
assert.equal(sanitizeKnownWordMaturityTier('learning'), 'learning');
|
|
assert.equal(sanitizeKnownWordMaturityTier('new'), 'new');
|
|
assert.equal(sanitizeKnownWordMaturityTier('MATURE'), null);
|
|
assert.equal(sanitizeKnownWordMaturityTier(''), null);
|
|
assert.equal(sanitizeKnownWordMaturityTier(21), null);
|
|
assert.equal(sanitizeKnownWordMaturityTier(null), null);
|
|
assert.equal(sanitizeKnownWordMaturityTier(undefined), null);
|
|
});
|