Files
FlowPilot/tests/background-account-run-history-module.test.js
T

365 lines
13 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
test('background imports account run history module', () => {
const source = fs.readFileSync('background.js', 'utf8');
assert.match(source, /background\/account-run-history\.js/);
});
test('account run history module exposes a factory', () => {
const source = fs.readFileSync('background/account-run-history.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundAccountRunHistory;`)(globalScope);
assert.equal(typeof api?.createAccountRunHistoryHelpers, 'function');
});
test('account run history helper upgrades old records, keeps stopped items and stores normalized failed snapshot records', async () => {
const source = fs.readFileSync('background/account-run-history.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundAccountRunHistory;`)(globalScope);
let storedHistory = [
{ email: 'old@example.com', password: 'old-pass', status: 'success', recordedAt: '2026-04-17T00:00:00.000Z' },
{ email: 'stop@example.com', password: 'stop-pass', status: 'stopped', recordedAt: '2026-04-17T00:10:00.000Z' },
];
let fetchCalled = false;
global.fetch = async () => {
fetchCalled = true;
throw new Error('should not call fetch');
};
const helpers = api.createAccountRunHistoryHelpers({
ACCOUNT_RUN_HISTORY_STORAGE_KEY: 'accountRunHistory',
addLog: async () => {},
buildLocalHelperEndpoint: (baseUrl, path) => `${baseUrl}${path}`,
chrome: {
storage: {
local: {
get: async () => ({ accountRunHistory: storedHistory }),
set: async (payload) => {
storedHistory = payload.accountRunHistory;
},
},
},
},
getErrorMessage: (error) => error?.message || String(error || ''),
getState: async () => ({
email: ' latest@example.com ',
password: ' secret ',
autoRunning: true,
autoRunCurrentRun: 2,
autoRunTotalRuns: 10,
autoRunAttemptRun: 3,
accountRunHistoryTextEnabled: false,
accountRunHistoryHelperBaseUrl: 'http://127.0.0.1:17373',
}),
normalizeAccountRunHistoryHelperBaseUrl: (value) => String(value || '').trim(),
});
const record = helpers.buildAccountRunHistoryRecord(
{
email: ' latest@example.com ',
password: ' secret ',
autoRunning: true,
autoRunCurrentRun: 2,
autoRunTotalRuns: 10,
autoRunAttemptRun: 3,
},
'step8_failed',
'步骤 8:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。'
);
assert.deepStrictEqual(record, {
recordId: 'latest@example.com',
email: 'latest@example.com',
password: 'secret',
finalStatus: 'failed',
finishedAt: record.finishedAt,
retryCount: 2,
failureLabel: '出现手机号验证',
failureDetail: '步骤 8:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。',
failedStep: 8,
source: 'auto',
autoRunContext: {
currentRun: 2,
totalRuns: 10,
attemptRun: 3,
},
plusModeEnabled: false,
contributionMode: false,
});
const appended = await helpers.appendAccountRunRecord('step8_failed', null, '步骤 8:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。');
assert.equal(appended.email, 'latest@example.com');
assert.equal(appended.finalStatus, 'failed');
assert.equal(appended.failureLabel, '出现手机号验证');
assert.equal(storedHistory.length, 3, '旧的 stopped 记录应在新结构中保留');
assert.equal(storedHistory.some((item) => item.email === 'stop@example.com' && item.finalStatus === 'stopped'), true);
assert.equal(storedHistory.some((item) => item.email === 'latest@example.com' && item.retryCount === 2), true);
assert.equal(storedHistory.some((item) => item.email === 'old@example.com'), true);
assert.equal(fetchCalled, true);
assert.equal(helpers.shouldAppendAccountRunTextFile({ accountRunHistoryTextEnabled: false, accountRunHistoryHelperBaseUrl: 'http://127.0.0.1:17373' }), true);
assert.equal(helpers.shouldAppendAccountRunTextFile({ accountRunHistoryTextEnabled: true, accountRunHistoryHelperBaseUrl: 'http://127.0.0.1:17373' }), true);
const stoppedRecord = helpers.buildAccountRunHistoryRecord(
{ email: 'a@b.com', password: 'x' },
'step7_stopped',
'步骤 7 已被用户停止'
);
assert.equal(stoppedRecord.recordId, 'a@b.com');
assert.equal(stoppedRecord.email, 'a@b.com');
assert.equal(stoppedRecord.password, 'x');
assert.equal(stoppedRecord.finalStatus, 'stopped');
assert.equal(stoppedRecord.retryCount, 0);
assert.equal(stoppedRecord.failureLabel, '步骤 7 停止');
assert.equal(stoppedRecord.failureDetail, '步骤 7 已被用户停止');
assert.equal(stoppedRecord.failedStep, 7);
assert.equal(stoppedRecord.source, 'manual');
assert.equal(stoppedRecord.autoRunContext, null);
assert.ok(stoppedRecord.finishedAt);
const genericStoppedRecord = helpers.buildAccountRunHistoryRecord({ email: 'stop@b.com', password: 'y' }, 'stopped', 'stop');
assert.equal(genericStoppedRecord.failureLabel, '流程已停止');
assert.equal(genericStoppedRecord.failedStep, null);
const normalizedStoppedRecord = helpers.normalizeAccountRunHistoryRecord({
recordId: 'legacy-stop@example.com',
email: 'legacy-stop@example.com',
password: 'secret',
finalStatus: 'stopped',
finishedAt: '2026-04-17T00:12:00.000Z',
retryCount: 0,
failureLabel: '流程已停止',
failureDetail: '步骤 7 已被用户停止。',
failedStep: 7,
source: 'manual',
autoRunContext: null,
});
assert.equal(normalizedStoppedRecord.failureLabel, '步骤 7 停止');
assert.equal(normalizedStoppedRecord.failedStep, 7);
});
test('account run history records preserve Plus and contribution mode flags', () => {
const source = fs.readFileSync('background/account-run-history.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundAccountRunHistory;`)(globalScope);
const helpers = api.createAccountRunHistoryHelpers({
chrome: { storage: { local: { get: async () => ({}), set: async () => {} } } },
getState: async () => ({}),
normalizeAccountRunHistoryHelperBaseUrl: (value) => String(value || '').trim(),
});
const record = helpers.buildAccountRunHistoryRecord({
email: 'plus@example.com',
password: 'secret',
plusModeEnabled: true,
contributionMode: true,
}, 'success');
assert.equal(record.plusModeEnabled, true);
assert.equal(record.contributionMode, true);
const normalized = helpers.normalizeAccountRunHistoryRecord({
email: 'plus@example.com',
password: 'secret',
finalStatus: 'success',
plusModeEnabled: true,
contributionMode: true,
});
assert.equal(normalized.plusModeEnabled, true);
assert.equal(normalized.contributionMode, true);
});
test('account run history helper clears persisted records and syncs full snapshot payload to local helper', async () => {
const source = fs.readFileSync('background/account-run-history.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundAccountRunHistory;`)(globalScope);
let storedHistory = [{
recordId: 'user@example.com',
email: 'user@example.com',
password: 'secret',
finalStatus: 'failed',
finishedAt: '2026-04-17T01:00:00.000Z',
retryCount: 1,
failureLabel: '步骤 6 失败',
failureDetail: '步骤 6:判断失败后已重试 2 次,仍未成功。',
failedStep: 6,
source: 'auto',
autoRunContext: {
currentRun: 1,
totalRuns: 5,
attemptRun: 2,
},
}];
const fetchCalls = [];
global.fetch = async (url, options = {}) => {
fetchCalls.push({
url,
options,
});
return {
ok: true,
json: async () => ({
ok: true,
filePath: 'C:/tmp/account-run-history.json',
}),
};
};
const logs = [];
const helpers = api.createAccountRunHistoryHelpers({
ACCOUNT_RUN_HISTORY_STORAGE_KEY: 'accountRunHistory',
addLog: async (message, level) => {
logs.push({ message, level });
},
buildLocalHelperEndpoint: (baseUrl, path) => `${baseUrl}${path}`,
chrome: {
storage: {
local: {
get: async () => ({ accountRunHistory: storedHistory }),
set: async (payload) => {
storedHistory = payload.accountRunHistory;
},
},
},
},
getErrorMessage: (error) => error?.message || String(error || ''),
getState: async () => ({
accountRunHistoryTextEnabled: true,
accountRunHistoryHelperBaseUrl: 'http://127.0.0.1:17373',
}),
normalizeAccountRunHistoryHelperBaseUrl: (value) => String(value || '').trim(),
});
const payload = helpers.buildAccountRunHistorySnapshotPayload(storedHistory);
assert.deepStrictEqual(payload.summary, {
total: 1,
success: 0,
failed: 1,
stopped: 0,
retryTotal: 1,
});
const clearResult = await helpers.clearAccountRunHistory();
assert.deepStrictEqual(clearResult, { clearedCount: 1 });
assert.deepStrictEqual(storedHistory, []);
assert.equal(fetchCalls.length, 1);
assert.equal(fetchCalls[0].url, 'http://127.0.0.1:17373/sync-account-run-records');
assert.deepStrictEqual(JSON.parse(fetchCalls[0].options.body), {
generatedAt: JSON.parse(fetchCalls[0].options.body).generatedAt,
summary: {
total: 0,
success: 0,
failed: 0,
stopped: 0,
retryTotal: 0,
},
records: [],
});
assert.equal(logs[0].message, '账号记录快照已同步到本地:C:/tmp/account-run-history.json');
});
test('account run history helper deletes selected records and syncs remaining snapshot payload', async () => {
const source = fs.readFileSync('background/account-run-history.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundAccountRunHistory;`)(globalScope);
let storedHistory = [
{
recordId: 'keep@example.com',
email: 'keep@example.com',
password: 'secret',
finalStatus: 'success',
finishedAt: '2026-04-17T01:10:00.000Z',
retryCount: 0,
failureLabel: '流程完成',
failureDetail: '',
failedStep: null,
source: 'manual',
autoRunContext: null,
},
{
recordId: 'remove@example.com',
email: 'remove@example.com',
password: 'secret',
finalStatus: 'failed',
finishedAt: '2026-04-17T01:00:00.000Z',
retryCount: 2,
failureLabel: '步骤 8 失败',
failureDetail: '步骤 8:认证页异常',
failedStep: 8,
source: 'auto',
autoRunContext: {
currentRun: 1,
totalRuns: 5,
attemptRun: 3,
},
},
];
const fetchCalls = [];
global.fetch = async (url, options = {}) => {
fetchCalls.push({
url,
options,
});
return {
ok: true,
json: async () => ({
ok: true,
filePath: 'C:/tmp/account-run-history.json',
}),
};
};
const logs = [];
const helpers = api.createAccountRunHistoryHelpers({
ACCOUNT_RUN_HISTORY_STORAGE_KEY: 'accountRunHistory',
addLog: async (message, level) => {
logs.push({ message, level });
},
buildLocalHelperEndpoint: (baseUrl, path) => `${baseUrl}${path}`,
chrome: {
storage: {
local: {
get: async () => ({ accountRunHistory: storedHistory }),
set: async (payload) => {
storedHistory = payload.accountRunHistory;
},
},
},
},
getErrorMessage: (error) => error?.message || String(error || ''),
getState: async () => ({
accountRunHistoryTextEnabled: true,
accountRunHistoryHelperBaseUrl: 'http://127.0.0.1:17373',
}),
normalizeAccountRunHistoryHelperBaseUrl: (value) => String(value || '').trim(),
});
const result = await helpers.deleteAccountRunHistoryRecords(['remove@example.com']);
assert.deepStrictEqual(result, {
deletedCount: 1,
remainingCount: 1,
});
assert.equal(storedHistory.length, 1);
assert.equal(storedHistory[0].email, 'keep@example.com');
assert.equal(fetchCalls.length, 1);
assert.equal(fetchCalls[0].url, 'http://127.0.0.1:17373/sync-account-run-records');
assert.deepStrictEqual(JSON.parse(fetchCalls[0].options.body), {
generatedAt: JSON.parse(fetchCalls[0].options.body).generatedAt,
summary: {
total: 1,
success: 1,
failed: 0,
stopped: 0,
retryTotal: 0,
},
records: storedHistory,
});
assert.equal(logs[0].message, '账号记录快照已同步到本地:C:/tmp/account-run-history.json');
});