douyin-release-gate / verify (push) Failing after 18m54s
Add Douyin notification polling, event details, and manual multi-account private messaging. Refine environment memory settings, account operations, login collection recovery, and message UI; update tests and documentation.
137 lines
7.6 KiB
JavaScript
137 lines
7.6 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const { readFileSync } = require('node:fs');
|
|
const { resolve } = require('node:path');
|
|
const { Module } = require('node:module');
|
|
const test = require('node:test');
|
|
const ts = require('typescript');
|
|
|
|
function compile(file, imports = {}) {
|
|
const loaded = new Module(file, module);
|
|
loaded.require = id => imports[id] || require(id);
|
|
loaded._compile(ts.transpileModule(readFileSync(file, 'utf8'), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText, file);
|
|
return loaded.exports;
|
|
}
|
|
const collection = compile(resolve(__dirname, '../src/utils/account-collection-status.ts'));
|
|
const running = { alias: 'environment', state: 'running', status: 'ready', gateway_reachable: true };
|
|
const stopped = { ...running, state: 'runtime_missing', status: 'stopped' };
|
|
const account = { id: 'account', login_status: 'logged_in', runtime_status: 'active', environment: { alias: 'environment', runtime_id: 'old-record' } };
|
|
|
|
function setup({ current = account, snapshots = [running], result = { status: 'logged_in', account_id: 'account' }, actionError } = {}) {
|
|
const calls = [];
|
|
const queue = [...snapshots];
|
|
const api = {
|
|
creatorGet: async path => {
|
|
calls.push(['get', path]);
|
|
if (path === '/phase-a/accounts/account') return current;
|
|
if (path === '/browsers/environment') return queue.length > 1 ? queue.shift() : queue[0];
|
|
throw new Error('Unexpected request: ' + path);
|
|
},
|
|
creatorAction: async (path, body) => {
|
|
calls.push(['post', path, body]);
|
|
if (actionError) throw actionError;
|
|
if (path === '/creator/accounts/account/verify') return result;
|
|
if (path === '/browsers/environment/start' || path === '/phase-a/accounts/account/start') return {};
|
|
throw new Error('Unexpected action: ' + path);
|
|
},
|
|
};
|
|
return { calls, ...compile(resolve(__dirname, '../src/components/accounts/account-actions.ts'), {
|
|
'@/services/api': api, '@/utils/account-collection-status': collection,
|
|
}) };
|
|
}
|
|
|
|
for (const [row, runtime, expected] of [
|
|
[{ pending_environment: true }, undefined, ['login', '登录并同步账号']],
|
|
[{ ...account, runtime_status: 'paused' }, running, ['resume', '恢复运行']],
|
|
[account, undefined, ['check', '检查状态']],
|
|
[account, { ...running, gateway_reachable: false }, ['check', '检查状态']],
|
|
[account, { ...running, state: 'recorded' }, ['check', '检查状态']],
|
|
[account, { ...running, cleanup_pending: true }, ['check', '检查状态']],
|
|
[account, stopped, ['start', '启动环境']],
|
|
[account, running, ['sync', '同步资料']],
|
|
[{ ...account, login_status: 'manual_login' }, running, ['login', '重新登录']],
|
|
[{ ...account, works_collection: { status: 'blocked', last_error: 'browser is awaiting user login' } }, running, ['login', '重新登录']],
|
|
]) {
|
|
test(`primary action ${expected.join('/')} uses actual browser state`, () => {
|
|
const s = setup();
|
|
assert.deepEqual(s.accountPrimaryAction(row, runtime), { key: expected[0], label: expected[1] });
|
|
});
|
|
}
|
|
|
|
test('latest successful verification overrides an older login-required collection result', () => {
|
|
const s = setup();
|
|
assert.equal(s.accountPrimaryAction({ ...account, works_collection: { status: 'blocked', last_error: 'browser is awaiting user login' } }, running, 'logged_in').key, 'sync');
|
|
});
|
|
|
|
test('normal sync checks live state and verifies identity without restarting or requesting QR', async () => {
|
|
const s = setup();
|
|
assert.equal((await s.performAccountPrimaryAction('account', 'sync')).status, 'logged_in');
|
|
assert.deepEqual(s.calls.map(call => call.slice(0, 2)), [
|
|
['get', '/phase-a/accounts/account'], ['get', '/browsers/environment'], ['post', '/creator/accounts/account/verify'],
|
|
]);
|
|
});
|
|
|
|
test('login-required result is explicit; other failures must not be disguised as QR login', async () => {
|
|
const s = setup({ result: { status: 'manual_login', account_id: 'account', reason: 'awaiting_login' } });
|
|
assert.equal((await s.performAccountPrimaryAction('account', 'login')).status, 'manual_login');
|
|
assert.ok(s.calls.every(call => !call[1].includes('/qr')));
|
|
await assert.rejects(setup({ actionError: new Error('identity UID mismatch') }).performAccountPrimaryAction('account', 'sync'), /UID mismatch/);
|
|
await assert.rejects(setup({ result: { status: 'unknown' } }).performAccountPrimaryAction('account', 'sync'), /未识别/);
|
|
await assert.rejects(setup({ result: { status: 'manual_login', reason: 'platform_restricted', account_id: 'account' } }).performAccountPrimaryAction('account', 'login'), /未识别/);
|
|
});
|
|
|
|
test('starting is not reported successful until the gateway reports a running browser', async () => {
|
|
const s = setup({ snapshots: [stopped, stopped] });
|
|
await assert.rejects(s.performAccountPrimaryAction('account', 'start'), /启动尚未完成/);
|
|
assert.ok(!s.calls.some(call => call[1].endsWith('/verify')));
|
|
});
|
|
|
|
test('starting a stopped browser does not restore or overwrite the account', async () => {
|
|
const s = setup({ snapshots: [stopped, running] });
|
|
await s.performAccountPrimaryAction('account', 'start');
|
|
assert.deepEqual(s.calls.filter(call => call[0] === 'post').map(call => call[1]), ['/browsers/environment/start', '/creator/accounts/account/verify']);
|
|
});
|
|
|
|
test('starting an already running browser only verifies it', async () => {
|
|
const s = setup();
|
|
await s.performAccountPrimaryAction('account', 'start');
|
|
assert.ok(!s.calls.some(call => call[1] === '/browsers/environment/start'));
|
|
});
|
|
|
|
test('restoring a paused account is explicit and uses the dedicated account start route', async () => {
|
|
const s = setup({ current: { ...account, runtime_status: 'paused' } });
|
|
await s.performAccountPrimaryAction('account', 'resume');
|
|
assert.equal(s.calls[1][1], '/phase-a/accounts/account/start');
|
|
assert.ok(!s.calls.some(call => call[1] === '/browsers/environment/start'));
|
|
});
|
|
|
|
test('stale login or sync buttons never implicitly restore a paused account', async () => {
|
|
for (const intent of ['sync', 'login', 'start']) {
|
|
const s = setup({ current: { ...account, runtime_status: 'paused' } });
|
|
await assert.rejects(s.performAccountPrimaryAction('account', intent), /先恢复运行/);
|
|
assert.equal(s.calls.length, 1);
|
|
}
|
|
});
|
|
|
|
test('a stopped browser is checked without mutation and stale sync does not start it', async () => {
|
|
const s = setup({ snapshots: [stopped] });
|
|
assert.equal((await s.performAccountPrimaryAction('account', 'check')).status, 'stopped');
|
|
assert.ok(s.calls.every(call => call[0] === 'get'));
|
|
await assert.rejects(setup({ snapshots: [stopped] }).performAccountPrimaryAction('account', 'sync'), /启动环境/);
|
|
});
|
|
|
|
test('gateway failure, wrong alias and unexpected browser state stop the action', async () => {
|
|
for (const runtime of [{ ...running, gateway_reachable: false }, { ...running, alias: 'other' }, { ...running, state: 'recorded' }, { ...running, cleanup_pending: true }]) {
|
|
const s = setup({ snapshots: [runtime] });
|
|
await assert.rejects(s.performAccountPrimaryAction('account', 'start'));
|
|
assert.ok(s.calls.every(call => call[0] === 'get'));
|
|
}
|
|
});
|
|
|
|
test('wrong account responses, missing binding and changed verification identity fail visibly', async () => {
|
|
await assert.rejects(setup({ current: { ...account, id: 'other' } }).performAccountPrimaryAction('account', 'sync'), /账号/);
|
|
await assert.rejects(setup({ current: { ...account, environment: null } }).performAccountPrimaryAction('account', 'sync'), /环境/);
|
|
await assert.rejects(setup({ result: { status: 'logged_in', account_id: 'other' } }).performAccountPrimaryAction('account', 'sync'), /账号/);
|
|
});
|