83 lines
3.9 KiB
JavaScript
83 lines
3.9 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import vm from 'node:vm';
|
|
import { readFile } from 'node:fs/promises';
|
|
|
|
// Run the actual background rule/queue functions with Thunderbird services mocked.
|
|
const source = await readFile(new URL('../background.js', import.meta.url), 'utf8');
|
|
const functions = source.slice(source.indexOf('async function processMessage('), source.indexOf('async function clearCacheForMessages('));
|
|
function runner({ fail = false, changeSettings = false } = {}) {
|
|
const actions = [], evaluated = [], errors = [];
|
|
const rules = [
|
|
{ criterion: 'first', decisionThreshold: 0.8, actions: [{ type: 'flag', flagged: true }], stopProcessing: true },
|
|
{ criterion: 'second', actions: [{ type: 'read', read: true }] }
|
|
];
|
|
let currentConfig = { apiType: 'systemone', inputCharacterLimit: 5, decisionThreshold: 0.5 };
|
|
const context = vm.createContext({
|
|
structuredClone, Date, Error,
|
|
aiRules: rules, queue: Promise.resolve(), queuedCount: 0, processing: false,
|
|
currentStart: 0, tokenReduction: true, showDebugTab: false, timingStats: {},
|
|
updateActionIcon() {}, updateTimingStats() {}, showTransientIcon() {},
|
|
buildEmailText() { return 'email contents'; }, ICONS: { circle: 'circle' },
|
|
logger: { aiLog() {} }, recordError(...args) { errors.push(args); },
|
|
ERROR_NOTIFICATION_ID: 'error',
|
|
storage: { local: { async set() {} } },
|
|
messenger: {
|
|
messages: {
|
|
async getFull() { return {}; },
|
|
async get() {
|
|
if (changeSettings) {
|
|
rules[0].decisionThreshold = 0.1;
|
|
currentConfig = { apiType: 'chat-completions', decisionThreshold: 0.1 };
|
|
}
|
|
return { tags: [], folder: { accountId: 'account' } };
|
|
},
|
|
async update(id, action) { actions.push({ id, ...action }); }
|
|
}, identities: { async list() { return []; } }
|
|
},
|
|
browser: { runtime: { getURL: x => x }, notifications: { async create() {} } },
|
|
AiClassifier: {
|
|
getConfig() { return structuredClone(currentConfig); },
|
|
async buildCacheKey(id, criterion) { return `${id}:${criterion}`; },
|
|
async *classifyRules(text, candidates, snapshot) {
|
|
for (const { rule, cacheKey } of candidates) {
|
|
evaluated.push({ text, criterion: rule.criterion, cacheKey, snapshot,
|
|
threshold: rule.decisionThreshold ?? snapshot.decisionThreshold });
|
|
if (fail && cacheKey.startsWith('1:')) throw new Error('inference failed');
|
|
yield { rule, result: { matched: true } };
|
|
}
|
|
}
|
|
}
|
|
});
|
|
vm.runInContext(functions, context);
|
|
return { context, actions, evaluated, errors };
|
|
}
|
|
|
|
test('failure stops remaining rules for one email, next queued email still acts', async () => {
|
|
const { context, actions, evaluated, errors } = runner({ fail: true });
|
|
await vm.runInContext('applyAiRules([1, 2])', context);
|
|
assert.deepEqual(evaluated.map(x => x.cacheKey), ['1:first', '2:first']);
|
|
assert.deepEqual(actions, [{ id: 2, flagged: true }]);
|
|
assert.equal(errors.length, 1);
|
|
assert.equal(context.processing, false);
|
|
assert.equal(context.queuedCount, 0);
|
|
});
|
|
|
|
test('settings and rules remain fixed during an email; reduction uses characters', async () => {
|
|
const { context, evaluated, actions } = runner({ changeSettings: true });
|
|
await vm.runInContext('applyAiRules([1])', context);
|
|
assert.equal(evaluated[0].text, 'email');
|
|
assert.equal(evaluated[0].threshold, 0.8);
|
|
assert.equal(evaluated[0].snapshot.apiType, 'systemone');
|
|
assert.equal(evaluated.length, 1); // stop after first match
|
|
assert.equal(actions.length, 1);
|
|
});
|
|
|
|
test('inherited thresholds and eligibility preserve ordered rule processing', async () => {
|
|
const { context, evaluated, actions } = runner();
|
|
context.aiRules[0].enabled = false;
|
|
await vm.runInContext('applyAiRules([1])', context);
|
|
assert.equal(evaluated[0].criterion, 'second');
|
|
assert.equal(evaluated[0].threshold, 0.5);
|
|
assert.deepEqual(actions, [{ id: 1, read: true }]);
|
|
});
|