step8-restart-step7-error.test.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. const test = require('node:test');
  2. const assert = require('node:assert/strict');
  3. const fs = require('node:fs');
  4. const backgroundSource = fs.readFileSync('background.js', 'utf8');
  5. const step8Source = fs.readFileSync('background/steps/fetch-login-code.js', 'utf8');
  6. const step8GlobalScope = {};
  7. const step8Api = new Function('self', `${step8Source}; return self.MultiPageBackgroundStep8;`)(step8GlobalScope);
  8. function extractFunction(source, name) {
  9. const markers = [`async function ${name}(`, `function ${name}(`];
  10. const start = markers
  11. .map((marker) => source.indexOf(marker))
  12. .find((index) => index >= 0);
  13. if (start < 0) {
  14. throw new Error(`missing function ${name}`);
  15. }
  16. let parenDepth = 0;
  17. let signatureEnded = false;
  18. let braceStart = -1;
  19. for (let i = start; i < source.length; i += 1) {
  20. const ch = source[i];
  21. if (ch === '(') {
  22. parenDepth += 1;
  23. } else if (ch === ')') {
  24. parenDepth -= 1;
  25. if (parenDepth === 0) {
  26. signatureEnded = true;
  27. }
  28. } else if (ch === '{' && signatureEnded) {
  29. braceStart = i;
  30. break;
  31. }
  32. }
  33. if (braceStart < 0) {
  34. throw new Error(`missing body for function ${name}`);
  35. }
  36. let depth = 0;
  37. let end = braceStart;
  38. for (; end < source.length; end += 1) {
  39. const ch = source[end];
  40. if (ch === '{') depth += 1;
  41. if (ch === '}') {
  42. depth -= 1;
  43. if (depth === 0) {
  44. end += 1;
  45. break;
  46. }
  47. }
  48. }
  49. return source.slice(start, end);
  50. }
  51. test('ensureStep8VerificationPageReady throws explicit restart-step7 error on login timeout page', async () => {
  52. const api = new Function(`
  53. function getLoginAuthStateLabel(state) {
  54. return state === 'login_timeout_error_page' ? '登录超时报错页' : '未知页面';
  55. }
  56. async function getLoginAuthStateFromContent() {
  57. return {
  58. state: 'login_timeout_error_page',
  59. url: 'https://auth.openai.com/log-in',
  60. };
  61. }
  62. ${extractFunction(backgroundSource, 'ensureStep8VerificationPageReady')}
  63. return {
  64. run() {
  65. return ensureStep8VerificationPageReady({});
  66. },
  67. };
  68. `)();
  69. await assert.rejects(
  70. () => api.run(),
  71. /STEP8_RESTART_STEP7::步骤 8:当前认证页进入登录超时报错页/
  72. );
  73. });
  74. test('step 8 reruns step 7 when auth page enters login timeout retry state', async () => {
  75. const calls = {
  76. executeStep7: 0,
  77. ensureReady: 0,
  78. logs: [],
  79. sleeps: [],
  80. resolveCalls: 0,
  81. };
  82. const executor = step8Api.createStep8Executor({
  83. addLog: async (message, level) => {
  84. calls.logs.push({ message, level });
  85. },
  86. chrome: {
  87. tabs: {
  88. update: async () => {},
  89. },
  90. },
  91. CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
  92. confirmCustomVerificationStepBypass: async () => {},
  93. ensureStep8VerificationPageReady: async () => {
  94. calls.ensureReady += 1;
  95. if (calls.ensureReady === 1) {
  96. throw new Error('STEP8_RESTART_STEP7::步骤 8:当前认证页进入登录超时报错页,请回到步骤 7 重新开始。 URL: https://auth.openai.com/log-in');
  97. }
  98. return { state: 'verification_page' };
  99. },
  100. executeStep7: async () => {
  101. calls.executeStep7 += 1;
  102. },
  103. getOAuthFlowRemainingMs: async () => 8000,
  104. getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => Math.min(defaultTimeoutMs, 8000),
  105. getMailConfig: () => ({
  106. provider: 'qq',
  107. label: 'QQ 邮箱',
  108. source: 'mail-qq',
  109. url: 'https://mail.qq.com',
  110. navigateOnReuse: false,
  111. }),
  112. getState: async () => ({ email: 'user@example.com', password: 'secret', oauthUrl: 'https://oauth.example/latest' }),
  113. getTabId: async () => 1,
  114. HOTMAIL_PROVIDER: 'hotmail-api',
  115. isTabAlive: async () => true,
  116. isVerificationMailPollingError: () => false,
  117. LUCKMAIL_PROVIDER: 'luckmail-api',
  118. resolveVerificationStep: async () => {
  119. calls.resolveCalls += 1;
  120. },
  121. reuseOrCreateTab: async () => {},
  122. setState: async () => {},
  123. setStepStatus: async () => {},
  124. shouldUseCustomRegistrationEmail: () => false,
  125. sleepWithStop: async (ms) => {
  126. calls.sleeps.push(ms);
  127. },
  128. STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
  129. STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 3,
  130. throwIfStopped: () => {},
  131. });
  132. await executor.executeStep8({
  133. email: 'user@example.com',
  134. password: 'secret',
  135. oauthUrl: 'https://oauth.example/latest',
  136. });
  137. assert.equal(calls.executeStep7, 1);
  138. assert.equal(calls.ensureReady, 2);
  139. assert.equal(calls.resolveCalls, 1);
  140. assert.equal(calls.logs.some(({ message }) => /准备从步骤 7 重新开始/.test(message)), true);
  141. assert.deepStrictEqual(calls.sleeps, [3000]);
  142. });