import GuJumpgate snapshot from GitHub
This commit is contained in:
@@ -0,0 +1,638 @@
|
||||
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 || ''),
|
||||
getNodeTitleForState: (nodeId) => ({
|
||||
'fetch-login-code': '获取登录验证码',
|
||||
'oauth-login': '刷新 OAuth 并登录',
|
||||
})[nodeId] || nodeId,
|
||||
getState: async () => ({
|
||||
flowId: 'openai',
|
||||
runId: 'run-2',
|
||||
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(
|
||||
{
|
||||
flowId: 'openai',
|
||||
runId: 'run-2',
|
||||
email: ' latest@example.com ',
|
||||
password: ' secret ',
|
||||
autoRunning: true,
|
||||
autoRunCurrentRun: 2,
|
||||
autoRunTotalRuns: 10,
|
||||
autoRunAttemptRun: 3,
|
||||
},
|
||||
'node:fetch-login-code:failed',
|
||||
'步骤 8:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。'
|
||||
);
|
||||
assert.deepStrictEqual(record, {
|
||||
recordId: 'latest@example.com',
|
||||
flowId: 'openai',
|
||||
runId: 'run-2',
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: 'latest@example.com',
|
||||
email: 'latest@example.com',
|
||||
phoneNumber: '',
|
||||
password: 'secret',
|
||||
finalStatus: 'failed',
|
||||
finishedAt: record.finishedAt,
|
||||
retryCount: 2,
|
||||
failureLabel: '出现手机号验证',
|
||||
failureDetail: '步骤 8:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。',
|
||||
failedNodeId: 'fetch-login-code',
|
||||
failedStep: null,
|
||||
source: 'auto',
|
||||
autoRunContext: {
|
||||
currentRun: 2,
|
||||
totalRuns: 10,
|
||||
attemptRun: 3,
|
||||
},
|
||||
plusModeEnabled: false,
|
||||
contributionMode: false,
|
||||
});
|
||||
|
||||
const appended = await helpers.appendAccountRunRecord('node:fetch-login-code:failed', null, '步骤 8:认证页进入了手机号页面,当前不是 OAuth 同意页,无法继续自动授权。');
|
||||
assert.equal(appended.email, 'latest@example.com');
|
||||
assert.equal(appended.finalStatus, 'failed');
|
||||
assert.equal(appended.flowId, 'openai');
|
||||
assert.equal(appended.runId, 'run-2');
|
||||
assert.equal(appended.failedNodeId, 'fetch-login-code');
|
||||
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(
|
||||
{ flowId: 'openai', runId: 'manual-run', email: 'a@b.com', password: 'x' },
|
||||
'node:oauth-login:stopped',
|
||||
'节点 oauth-login 已被用户停止'
|
||||
);
|
||||
assert.equal(stoppedRecord.recordId, 'a@b.com');
|
||||
assert.equal(stoppedRecord.flowId, 'openai');
|
||||
assert.equal(stoppedRecord.runId, 'manual-run');
|
||||
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, '节点 刷新 OAuth 并登录 停止');
|
||||
assert.equal(stoppedRecord.failureDetail, '节点 oauth-login 已被用户停止');
|
||||
assert.equal(stoppedRecord.failedNodeId, 'oauth-login');
|
||||
assert.equal(stoppedRecord.failedStep, null);
|
||||
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 runningRecord = helpers.buildAccountRunHistoryRecord({
|
||||
email: 'run@b.com',
|
||||
password: 'z',
|
||||
autoRunning: true,
|
||||
autoRunCurrentRun: 1,
|
||||
autoRunTotalRuns: 2,
|
||||
autoRunAttemptRun: 1,
|
||||
}, 'running', '正在运行');
|
||||
assert.equal(runningRecord.finalStatus, 'running');
|
||||
assert.equal(runningRecord.failureLabel, '正在运行');
|
||||
assert.equal(runningRecord.failureDetail, '');
|
||||
assert.equal(runningRecord.failedStep, null);
|
||||
assert.equal(runningRecord.source, 'auto');
|
||||
|
||||
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 已被用户停止。',
|
||||
failedNodeId: 'oauth-login',
|
||||
failedStep: 7,
|
||||
source: 'manual',
|
||||
autoRunContext: null,
|
||||
});
|
||||
assert.equal(normalizedStoppedRecord.failureLabel, '节点 刷新 OAuth 并登录 停止');
|
||||
assert.equal(normalizedStoppedRecord.failedNodeId, 'oauth-login');
|
||||
assert.equal(normalizedStoppedRecord.failedStep, 7);
|
||||
});
|
||||
|
||||
test('account run history helper accepts phone-only records without forcing email or password', () => {
|
||||
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({
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+6612345',
|
||||
signupPhoneNumber: '+6612345',
|
||||
password: '',
|
||||
}, 'success');
|
||||
|
||||
assert.deepStrictEqual(record, {
|
||||
recordId: 'phone:+6612345',
|
||||
flowId: '',
|
||||
runId: '',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+6612345',
|
||||
email: '',
|
||||
phoneNumber: '+6612345',
|
||||
password: '',
|
||||
finalStatus: 'success',
|
||||
finishedAt: record.finishedAt,
|
||||
retryCount: 0,
|
||||
failureLabel: '流程完成',
|
||||
failureDetail: '',
|
||||
failedNodeId: '',
|
||||
failedStep: null,
|
||||
source: 'manual',
|
||||
autoRunContext: null,
|
||||
plusModeEnabled: false,
|
||||
contributionMode: false,
|
||||
});
|
||||
|
||||
const normalized = helpers.normalizeAccountRunHistoryRecord({
|
||||
recordId: 'phone:+6612345',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+6612345',
|
||||
phoneNumber: '+6612345',
|
||||
finalStatus: 'failed',
|
||||
failureDetail: '步骤 8:手机号验证码超时。',
|
||||
});
|
||||
|
||||
assert.equal(normalized.recordId, 'phone:+6612345');
|
||||
assert.equal(normalized.accountIdentifierType, 'phone');
|
||||
assert.equal(normalized.accountIdentifier, '+6612345');
|
||||
assert.equal(normalized.email, '');
|
||||
assert.equal(normalized.phoneNumber, '+6612345');
|
||||
assert.equal(normalized.password, '');
|
||||
assert.equal(normalized.finalStatus, 'failed');
|
||||
});
|
||||
|
||||
test('account run history does not turn prerequisite guidance into a fake step 2 failure', () => {
|
||||
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 genericFailedRecord = helpers.buildAccountRunHistoryRecord({
|
||||
email: 'late@example.com',
|
||||
password: 'secret',
|
||||
autoRunning: true,
|
||||
autoRunCurrentRun: 1,
|
||||
autoRunTotalRuns: 3,
|
||||
autoRunAttemptRun: 2,
|
||||
}, 'failed', '缺少登录账号:请先完成步骤 2,或在侧栏填写账号后再执行当前步骤。');
|
||||
|
||||
assert.equal(genericFailedRecord.failedStep, null);
|
||||
assert.equal(genericFailedRecord.failureLabel, '流程失败');
|
||||
|
||||
const explicitFailedRecord = helpers.buildAccountRunHistoryRecord({
|
||||
email: 'late@example.com',
|
||||
password: 'secret',
|
||||
autoRunning: true,
|
||||
autoRunCurrentRun: 1,
|
||||
autoRunTotalRuns: 3,
|
||||
autoRunAttemptRun: 2,
|
||||
}, 'node:platform-verify:failed', '缺少登录账号:请先完成步骤 2,或在侧栏填写账号后再执行当前步骤。');
|
||||
|
||||
assert.equal(explicitFailedRecord.failedNodeId, 'platform-verify');
|
||||
assert.equal(explicitFailedRecord.failedStep, null);
|
||||
assert.equal(explicitFailedRecord.failureLabel, '节点 platform-verify 失败');
|
||||
|
||||
const migratedOldRecord = helpers.normalizeAccountRunHistoryRecord({
|
||||
email: 'old@example.com',
|
||||
password: 'secret',
|
||||
finalStatus: 'failed',
|
||||
failureLabel: '步骤 2 失败',
|
||||
failureDetail: '缺少登录账号:请先完成步骤 2,或在侧栏填写账号后再执行当前步骤。',
|
||||
failedStep: 2,
|
||||
});
|
||||
|
||||
assert.equal(migratedOldRecord.failedStep, null);
|
||||
assert.equal(migratedOldRecord.failureLabel, '流程失败');
|
||||
});
|
||||
|
||||
test('account run history merges email and phone identities from the same run', 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: 'phone:+447799342687',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+447799342687',
|
||||
phoneNumber: '+44 7799 342687',
|
||||
email: '',
|
||||
password: '',
|
||||
finalStatus: 'stopped',
|
||||
finishedAt: '2026-04-17T04:30:00.000Z',
|
||||
failureDetail: '步骤 2 已使用手机号,流程尚未完成。',
|
||||
},
|
||||
{
|
||||
recordId: 'tmp@example.com',
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: 'tmp@example.com',
|
||||
email: 'tmp@example.com',
|
||||
phoneNumber: '',
|
||||
password: 'old',
|
||||
finalStatus: 'stopped',
|
||||
finishedAt: '2026-04-17T04:31:00.000Z',
|
||||
failureDetail: '步骤 2 已使用邮箱,流程尚未完成。',
|
||||
},
|
||||
];
|
||||
|
||||
const helpers = api.createAccountRunHistoryHelpers({
|
||||
chrome: {
|
||||
storage: {
|
||||
local: {
|
||||
get: async () => ({ accountRunHistory: storedHistory }),
|
||||
set: async (payload) => {
|
||||
storedHistory = payload.accountRunHistory;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
getState: async () => ({}),
|
||||
normalizeAccountRunHistoryHelperBaseUrl: () => '',
|
||||
});
|
||||
|
||||
const failedRecord = helpers.buildAccountRunHistoryRecord({
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: 'tmp@example.com',
|
||||
email: 'tmp@example.com',
|
||||
password: 'secret',
|
||||
currentPhoneActivation: {
|
||||
activationId: 'a1',
|
||||
phoneNumber: '+44 7799 342687',
|
||||
},
|
||||
}, 'node:confirm-oauth:failed', '步骤 9:手机号验证失败。');
|
||||
assert.equal(failedRecord.accountIdentifierType, 'email');
|
||||
assert.equal(failedRecord.accountIdentifier, 'tmp@example.com');
|
||||
assert.equal(failedRecord.phoneNumber, '+44 7799 342687');
|
||||
assert.equal(failedRecord.failedNodeId, 'confirm-oauth');
|
||||
assert.equal(failedRecord.failedStep, null);
|
||||
|
||||
const successRecord = await helpers.appendAccountRunRecord('success', {
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: 'tmp@example.com',
|
||||
email: 'tmp@example.com',
|
||||
phoneNumber: '447799342687',
|
||||
password: 'secret',
|
||||
accountRunHistoryHelperBaseUrl: '',
|
||||
});
|
||||
|
||||
assert.equal(successRecord.recordId, 'tmp@example.com');
|
||||
assert.equal(successRecord.email, 'tmp@example.com');
|
||||
assert.equal(successRecord.phoneNumber, '447799342687');
|
||||
assert.equal(storedHistory.length, 1);
|
||||
assert.equal(storedHistory[0].recordId, 'tmp@example.com');
|
||||
assert.equal(storedHistory[0].finalStatus, 'success');
|
||||
});
|
||||
|
||||
test('account run history keeps phone as primary identity when phone signup later binds email', 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: 'phone:+447700900123',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+447700900123',
|
||||
phoneNumber: '+447700900123',
|
||||
email: '',
|
||||
finalStatus: 'stopped',
|
||||
finishedAt: '2026-04-17T04:31:00.000Z',
|
||||
failureDetail: '步骤 2 已使用手机号,流程尚未完成。',
|
||||
}];
|
||||
|
||||
const helpers = api.createAccountRunHistoryHelpers({
|
||||
chrome: {
|
||||
storage: {
|
||||
local: {
|
||||
get: async () => ({ accountRunHistory: storedHistory }),
|
||||
set: async (payload) => {
|
||||
storedHistory = payload.accountRunHistory;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
getState: async () => ({}),
|
||||
normalizeAccountRunHistoryHelperBaseUrl: () => '',
|
||||
});
|
||||
|
||||
const record = await helpers.appendAccountRunRecord('success', {
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+447700900123',
|
||||
signupPhoneNumber: '+447700900123',
|
||||
email: 'bound@example.com',
|
||||
password: 'secret',
|
||||
accountRunHistoryHelperBaseUrl: '',
|
||||
});
|
||||
|
||||
assert.equal(record.recordId, 'phone:+447700900123');
|
||||
assert.equal(record.accountIdentifierType, 'phone');
|
||||
assert.equal(record.accountIdentifier, '+447700900123');
|
||||
assert.equal(record.email, 'bound@example.com');
|
||||
assert.equal(record.phoneNumber, '+447700900123');
|
||||
assert.equal(storedHistory.length, 1);
|
||||
assert.equal(storedHistory[0].recordId, 'phone:+447700900123');
|
||||
assert.equal(storedHistory[0].finalStatus, 'success');
|
||||
});
|
||||
|
||||
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,
|
||||
running: 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,
|
||||
running: 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,
|
||||
running: 0,
|
||||
failed: 0,
|
||||
stopped: 0,
|
||||
retryTotal: 0,
|
||||
},
|
||||
records: storedHistory,
|
||||
});
|
||||
assert.equal(logs[0].message, '账号记录快照已同步到本地:C:/tmp/account-run-history.json');
|
||||
});
|
||||
Reference in New Issue
Block a user