| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- const test = require('node:test');
- const assert = require('node:assert/strict');
- const {
- buildHotmailMailApiLatestUrl,
- extractVerificationCodeFromMessage,
- filterHotmailAccountsByUsage,
- extractVerificationCode,
- getLatestHotmailMessage,
- getHotmailBulkActionLabel,
- getHotmailListToggleLabel,
- getHotmailMailApiRequestConfig,
- getHotmailVerificationPollConfig,
- getHotmailVerificationRequestTimestamp,
- normalizeHotmailMailApiMessages,
- parseHotmailImportText,
- pickHotmailAccountForRun,
- pickVerificationMessage,
- pickVerificationMessageWithFallback,
- pickVerificationMessageWithTimeFallback,
- shouldClearHotmailCurrentSelection,
- upsertHotmailAccountInList,
- } = require('../hotmail-utils.js');
- test('pickHotmailAccountForRun prefers authorized account with oldest lastUsedAt', () => {
- const now = Date.UTC(2026, 3, 10, 10, 0, 0);
- const accounts = [
- {
- id: 'recent',
- email: 'recent@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-recent',
- lastUsedAt: now - 1_000,
- },
- {
- id: 'oldest',
- email: 'oldest@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-oldest',
- lastUsedAt: now - 50_000,
- },
- {
- id: 'pending',
- email: 'pending@hotmail.com',
- status: 'pending',
- refreshToken: '',
- lastUsedAt: 0,
- },
- ];
- const selected = pickHotmailAccountForRun(accounts, { now });
- assert.equal(selected.id, 'oldest');
- });
- test('pickHotmailAccountForRun skips used accounts but ignores legacy enabled flag', () => {
- const accounts = [
- {
- id: 'disabled',
- email: 'disabled@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-disabled',
- enabled: false,
- used: false,
- lastUsedAt: 1,
- },
- {
- id: 'used',
- email: 'used@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-used',
- enabled: true,
- used: true,
- lastUsedAt: 0,
- },
- {
- id: 'available',
- email: 'available@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-available',
- enabled: true,
- used: false,
- lastUsedAt: 2,
- },
- ];
- const selected = pickHotmailAccountForRun(accounts, {});
- assert.equal(selected.id, 'disabled');
- });
- test('pickHotmailAccountForRun returns null for used-only pools', () => {
- assert.equal(
- pickHotmailAccountForRun([{
- id: 'used-only',
- email: 'used-only@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-used-only',
- used: true,
- lastUsedAt: 0,
- }], {}),
- null
- );
- });
- test('pickHotmailAccountForRun falls back to never-used authorized account first', () => {
- const accounts = [
- {
- id: 'used',
- email: 'used@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-used',
- lastUsedAt: Date.UTC(2026, 3, 10, 9, 0, 0),
- },
- {
- id: 'fresh',
- email: 'fresh@hotmail.com',
- status: 'authorized',
- refreshToken: 'rt-fresh',
- lastUsedAt: 0,
- },
- ];
- const selected = pickHotmailAccountForRun(accounts, { now: Date.UTC(2026, 3, 10, 10, 0, 0) });
- assert.equal(selected.id, 'fresh');
- });
- test('upsertHotmailAccountInList replaces matching account state by id', () => {
- const accounts = [
- {
- id: 'active',
- email: 'active@hotmail.com',
- status: 'authorized',
- used: false,
- },
- {
- id: 'other',
- email: 'other@hotmail.com',
- status: 'authorized',
- used: false,
- },
- ];
- const nextAccounts = upsertHotmailAccountInList(accounts, {
- id: 'active',
- email: 'active@hotmail.com',
- status: 'authorized',
- used: true,
- });
- assert.deepEqual(nextAccounts, [
- {
- id: 'active',
- email: 'active@hotmail.com',
- status: 'authorized',
- used: true,
- },
- {
- id: 'other',
- email: 'other@hotmail.com',
- status: 'authorized',
- used: false,
- },
- ]);
- });
- test('shouldClearHotmailCurrentSelection returns true only when account becomes used', () => {
- assert.equal(shouldClearHotmailCurrentSelection({
- id: 'used',
- used: true,
- }), true);
- assert.equal(shouldClearHotmailCurrentSelection({
- id: 'available',
- used: false,
- }), false);
- });
- test('extractVerificationCode returns first six-digit code from multilingual mail text', () => {
- assert.equal(extractVerificationCode('你的 ChatGPT 验证码为 370794,请勿泄露。'), '370794');
- assert.equal(extractVerificationCode('Your verification code is 654321.'), '654321');
- assert.equal(extractVerificationCode('No code here'), null);
- });
- test('extractVerificationCodeFromMessage reads code from the latest message subject or preview', () => {
- assert.equal(
- extractVerificationCodeFromMessage({
- subject: '你的 ChatGPT 代码为 192742',
- bodyPreview: 'OpenAI 验证邮件',
- from: { emailAddress: { address: 'noreply@openai.com' } },
- }),
- '192742'
- );
- assert.equal(
- extractVerificationCodeFromMessage({
- subject: 'OpenAI security message',
- bodyPreview: 'Your verification code is 654321.',
- from: { emailAddress: { address: 'noreply@openai.com' } },
- }),
- '654321'
- );
- });
- test('getHotmailListToggleLabel reflects expanded state and account count', () => {
- assert.equal(getHotmailListToggleLabel(false, 0), '展开列表');
- assert.equal(getHotmailListToggleLabel(false, 7), '展开列表(7)');
- assert.equal(getHotmailListToggleLabel(true, 7), '收起列表(7)');
- });
- test('filterHotmailAccountsByUsage can pick only used accounts or return all accounts', () => {
- const accounts = [
- { id: 'used-1', email: 'used-1@hotmail.com', used: true },
- { id: 'fresh-1', email: 'fresh-1@hotmail.com', used: false },
- { id: 'used-2', email: 'used-2@hotmail.com', used: true },
- ];
- assert.deepEqual(
- filterHotmailAccountsByUsage(accounts, 'used').map((account) => account.id),
- ['used-1', 'used-2']
- );
- assert.deepEqual(
- filterHotmailAccountsByUsage(accounts, 'all').map((account) => account.id),
- ['used-1', 'fresh-1', 'used-2']
- );
- });
- test('getHotmailBulkActionLabel reflects action type and count', () => {
- assert.equal(getHotmailBulkActionLabel('used', 0), '清空已用');
- assert.equal(getHotmailBulkActionLabel('used', 3), '清空已用(3)');
- assert.equal(getHotmailBulkActionLabel('all', 5), '全部删除(5)');
- });
- test('getLatestHotmailMessage picks the newest received mail', () => {
- const latest = getLatestHotmailMessage([
- {
- id: 'older',
- subject: 'older',
- receivedDateTime: '2026-04-11T00:01:00.000Z',
- },
- {
- id: 'newest',
- subject: 'newest',
- receivedDateTime: '2026-04-11T00:05:00.000Z',
- },
- {
- id: 'middle',
- subject: 'middle',
- receivedDateTime: '2026-04-11T00:03:00.000Z',
- },
- ]);
- assert.equal(latest.id, 'newest');
- });
- test('pickVerificationMessage filters by time, sender, subject, and excluded codes', () => {
- const messages = [
- {
- id: 'old-mail',
- subject: 'Your code is 111111',
- from: { emailAddress: { address: 'noreply@openai.com' } },
- bodyPreview: '111111',
- receivedDateTime: '2026-04-10T09:00:00.000Z',
- },
- {
- id: 'wrong-sender',
- subject: 'Your code is 222222',
- from: { emailAddress: { address: 'noreply@example.com' } },
- bodyPreview: '222222',
- receivedDateTime: '2026-04-10T10:01:00.000Z',
- },
- {
- id: 'good-mail',
- subject: 'ChatGPT verification code 333333',
- from: { emailAddress: { address: 'noreply@openai.com' } },
- bodyPreview: 'Use 333333 to continue',
- receivedDateTime: '2026-04-10T10:02:00.000Z',
- },
- {
- id: 'excluded-mail',
- subject: 'ChatGPT verification code 444444',
- from: { emailAddress: { address: 'noreply@openai.com' } },
- bodyPreview: 'Use 444444 to continue',
- receivedDateTime: '2026-04-10T10:03:00.000Z',
- },
- ];
- const match = pickVerificationMessage(messages, {
- afterTimestamp: Date.UTC(2026, 3, 10, 10, 0, 0),
- senderFilters: ['openai', 'noreply'],
- subjectFilters: ['verification', 'code', 'chatgpt'],
- excludeCodes: ['444444'],
- });
- assert.equal(match.message.id, 'good-mail');
- assert.equal(match.code, '333333');
- });
- test('pickVerificationMessageWithFallback returns relaxed match when strict filters miss but recent code exists', () => {
- const messages = [
- {
- id: 'login-mail',
- subject: 'Use this security code to continue 555666',
- from: { emailAddress: { address: 'account-security@openai.com' } },
- bodyPreview: 'Your one-time security code is 555666',
- receivedDateTime: '2026-04-10T10:05:00.000Z',
- },
- ];
- const result = pickVerificationMessageWithFallback(messages, {
- afterTimestamp: Date.UTC(2026, 3, 10, 10, 0, 0),
- senderFilters: ['noreply'],
- subjectFilters: ['verification'],
- excludeCodes: [],
- });
- assert.equal(result.match.message.id, 'login-mail');
- assert.equal(result.match.code, '555666');
- assert.equal(result.usedRelaxedFilters, true);
- assert.equal(result.usedTimeFallback, false);
- });
- test('pickVerificationMessageWithTimeFallback can ignore afterTimestamp when latest code mail is just outside flow window', () => {
- const messages = [
- {
- id: 'slightly-old-mail',
- subject: '你的 ChatGPT 代码为 141735',
- from: { emailAddress: { address: 'unknown' } },
- bodyPreview: 'OpenAI logo ...',
- receivedDateTime: '2026-04-10T10:00:02.000Z',
- },
- ];
- const result = pickVerificationMessageWithTimeFallback(messages, {
- afterTimestamp: Date.UTC(2026, 3, 10, 10, 0, 10),
- senderFilters: ['openai', 'noreply'],
- subjectFilters: ['verify', 'verification', 'code'],
- excludeCodes: [],
- });
- assert.equal(result.match.message.id, 'slightly-old-mail');
- assert.equal(result.match.code, '141735');
- assert.equal(result.usedRelaxedFilters, true);
- assert.equal(result.usedTimeFallback, true);
- });
- test('buildHotmailMailApiLatestUrl includes email, client id, refresh token, and mailbox', () => {
- const url = new URL(buildHotmailMailApiLatestUrl({
- clientId: 'client-123',
- email: 'user@hotmail.com',
- refreshToken: 'refresh-token-xyz',
- mailbox: 'Junk',
- }));
- assert.equal(url.origin + url.pathname, 'https://apple.882263.xyz/api/mail-new');
- assert.equal(url.searchParams.get('client_id'), 'client-123');
- assert.equal(url.searchParams.get('email'), 'user@hotmail.com');
- assert.equal(url.searchParams.get('refresh_token'), 'refresh-token-xyz');
- assert.equal(url.searchParams.get('mailbox'), 'Junk');
- assert.equal(url.searchParams.get('response_type'), 'json');
- });
- test('normalizeHotmailMailApiMessages maps third-party payload fields into verification message shape', () => {
- const messages = normalizeHotmailMailApiMessages([
- {
- id: 'mail-1',
- from: 'noreply@openai.com',
- subject: 'ChatGPT verification code',
- text: 'Use 135790 to continue',
- date: '2026-04-10T10:02:00.000Z',
- },
- {
- message_id: 'mail-2',
- sender_email: 'alerts@example.com',
- title: 'Ignored',
- body: 'No code here',
- received_at: '2026-04-10T10:03:00.000Z',
- },
- ]);
- assert.deepEqual(messages, [
- {
- id: 'mail-1',
- subject: 'ChatGPT verification code',
- from: { emailAddress: { address: 'noreply@openai.com' } },
- bodyPreview: 'Use 135790 to continue',
- receivedDateTime: '2026-04-10T10:02:00.000Z',
- },
- {
- id: 'mail-2',
- subject: 'Ignored',
- from: { emailAddress: { address: 'alerts@example.com' } },
- bodyPreview: 'No code here',
- receivedDateTime: '2026-04-10T10:03:00.000Z',
- },
- ]);
- });
- test('getHotmailVerificationPollConfig gives Hotmail a slower initial wait and longer polling window', () => {
- assert.deepEqual(getHotmailVerificationPollConfig(4), {
- initialDelayMs: 5000,
- maxAttempts: 12,
- intervalMs: 5000,
- requestFreshCodeFirst: false,
- ignorePersistedLastCode: true,
- });
- assert.deepEqual(getHotmailVerificationPollConfig(7), {
- initialDelayMs: 5000,
- maxAttempts: 12,
- intervalMs: 5000,
- requestFreshCodeFirst: false,
- ignorePersistedLastCode: true,
- });
- });
- test('getHotmailVerificationRequestTimestamp prefers actual request timestamps with a safety buffer', () => {
- const signupRequestedAt = Date.UTC(2026, 3, 10, 12, 0, 30);
- const loginRequestedAt = Date.UTC(2026, 3, 10, 12, 5, 45);
- assert.equal(
- getHotmailVerificationRequestTimestamp(4, {
- signupVerificationRequestedAt: signupRequestedAt,
- flowStartTime: signupRequestedAt - 60_000,
- }),
- signupRequestedAt - 15_000
- );
- assert.equal(
- getHotmailVerificationRequestTimestamp(7, {
- loginVerificationRequestedAt: loginRequestedAt,
- lastEmailTimestamp: loginRequestedAt - 120_000,
- flowStartTime: loginRequestedAt - 300_000,
- }),
- loginRequestedAt - 15_000
- );
- });
- test('getHotmailMailApiRequestConfig defines a finite timeout for mailbox polling', () => {
- assert.deepEqual(getHotmailMailApiRequestConfig(), {
- timeoutMs: 15000,
- });
- });
- test('parseHotmailImportText parses account lines in email----password----clientId----token format', () => {
- const parsed = parseHotmailImportText(`
- 账号----密码----ID----Token
- JohnRodriguez5425@hotmail.com----nb4ta1OK----9e5f94bc-e8a4-4e73-b8be-63364c29d753----refresh-token-1
- alice@hotmail.com----pass-2----client-2----refresh-token-2
- `.trim());
- assert.deepEqual(parsed, [
- {
- email: 'JohnRodriguez5425@hotmail.com',
- password: 'nb4ta1OK',
- clientId: '9e5f94bc-e8a4-4e73-b8be-63364c29d753',
- refreshToken: 'refresh-token-1',
- },
- {
- email: 'alice@hotmail.com',
- password: 'pass-2',
- clientId: 'client-2',
- refreshToken: 'refresh-token-2',
- },
- ]);
- });
|