Files
FlowPilot/tests/mail-polling-operation-delay-exclusion.test.js
T
2026-05-21 07:21:34 +08:00

84 lines
3.9 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
function extractPollEmailHandler(source) {
const start = source.indexOf("message.type === 'POLL_EMAIL'");
assert.notEqual(start, -1, 'missing POLL_EMAIL handler');
const nextHandler = source.indexOf("message.type === '", start + 1);
return nextHandler === -1 ? source.slice(start) : source.slice(start, nextHandler);
}
function extractFunction(source, name) {
const markers = [`async function ${name}(`, `function ${name}(`];
const start = markers.map((marker) => source.indexOf(marker)).find((index) => index >= 0);
assert.notEqual(start, -1, `missing function ${name}`);
let signatureDepth = 0;
let signatureEnded = false;
let bodyStart = -1;
for (let i = start; i < source.length; i += 1) {
const ch = source[i];
if (ch === '(') signatureDepth += 1;
if (ch === ')') {
signatureDepth -= 1;
if (signatureDepth === 0) signatureEnded = true;
}
if (ch === '{' && signatureEnded) {
bodyStart = i;
break;
}
}
assert.notEqual(bodyStart, -1, `missing body for ${name}`);
let depth = 0;
for (let i = bodyStart; i < source.length; i += 1) {
const ch = source[i];
if (ch === '{') depth += 1;
if (ch === '}') {
depth -= 1;
if (depth === 0) return source.slice(start, i + 1);
}
}
throw new Error(`unterminated function ${name}`);
}
test('mail polling handlers and cleanup handlers are not wrapped by operation delay', () => {
const protectedFunctions = {
'content/mail-163.js': ['handlePollEmail', 'returnToInbox', 'openMailAndGetMessageText', 'deleteEmail', 'refreshInbox'],
'content/qq-mail.js': ['handlePollEmail', 'refreshInbox'],
'content/icloud-mail.js': ['openMailItemAndRead', 'refreshInbox', 'handlePollEmail'],
'content/mail-2925.js': ['handlePollEmail', 'openMailAndGetMessageText', 'deleteCurrentMailboxEmail', 'openMailAndDeleteAfterRead', 'deleteAllMailboxEmails', 'refreshInbox'],
'content/gmail-mail.js': ['refreshInbox', 'openRowAndGetMessageText', 'handlePollEmail'],
'content/inbucket-mail.js': ['refreshMailbox', 'openMailboxEntry', 'deleteCurrentMailboxMessage', 'handlePollEmail'],
};
for (const [file, functionNames] of Object.entries(protectedFunctions)) {
const source = fs.readFileSync(file, 'utf8');
const pollHandler = extractPollEmailHandler(source);
assert.doesNotMatch(pollHandler, /performOperationWithDelay\(/, `${file} POLL_EMAIL handler must stay delay-free`);
for (const functionName of functionNames) {
const functionBody = extractFunction(source, functionName);
assert.doesNotMatch(functionBody, /performOperationWithDelay\(/, `${file} ${functionName} must stay delay-free`);
}
}
const mail2925Source = fs.readFileSync('content/mail-2925.js', 'utf8');
const deleteAllStart = mail2925Source.indexOf("message.type === 'DELETE_ALL_EMAILS'");
assert.notEqual(deleteAllStart, -1, 'missing DELETE_ALL_EMAILS handler');
const deleteAllBlock = mail2925Source.slice(deleteAllStart, mail2925Source.indexOf('return false;', deleteAllStart));
assert.doesNotMatch(deleteAllBlock, /performOperationWithDelay\(/, '2925 cleanup must stay delay-free');
});
test('mail polling bundles do not load operation delay module', () => {
const manifest = JSON.parse(fs.readFileSync('manifest.json', 'utf8'));
for (const file of ['content/mail-163.js', 'content/qq-mail.js', 'content/icloud-mail.js']) {
const bundle = manifest.content_scripts.find((entry) => entry.js.includes(file))?.js || [];
assert.equal(bundle.includes('content/operation-delay.js'), false, `${file} bundle must not include operation delay`);
}
});
test('WhatsApp code reader remains polling-only and delay-free', () => {
const source = fs.readFileSync('flows/openai/content/whatsapp-flow.js', 'utf8');
assert.doesNotMatch(source, /performOperationWithDelay\(/);
});