background-step6-retry-limit.test.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. const test = require('node:test');
  2. const assert = require('node:assert/strict');
  3. const fs = require('node:fs');
  4. const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
  5. const globalScope = {};
  6. const api = new Function('self', `${source}; return self.MultiPageBackgroundStep6;`)(globalScope);
  7. test('step 6 retries up to configured limit and then fails', async () => {
  8. const events = {
  9. cleanupCalls: 0,
  10. refreshCalls: 0,
  11. sendCalls: 0,
  12. completed: 0,
  13. };
  14. const executor = api.createStep6Executor({
  15. addLog: async () => {},
  16. completeStepFromBackground: async () => {
  17. events.completed += 1;
  18. },
  19. getErrorMessage: (error) => error?.message || String(error || ''),
  20. getLoginAuthStateLabel: (state) => state || 'unknown',
  21. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  22. isStep6RecoverableResult: (result) => result?.step6Outcome === 'recoverable',
  23. isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
  24. refreshOAuthUrlBeforeStep6: async () => {
  25. events.refreshCalls += 1;
  26. return `https://oauth.example/${events.refreshCalls}`;
  27. },
  28. reuseOrCreateTab: async () => {},
  29. runPreStep6CookieCleanup: async () => {
  30. events.cleanupCalls += 1;
  31. },
  32. sendToContentScriptResilient: async () => {
  33. events.sendCalls += 1;
  34. return {
  35. step6Outcome: 'recoverable',
  36. state: 'email_page',
  37. message: '当前仍停留在邮箱页',
  38. };
  39. },
  40. shouldSkipLoginVerificationForCpaCallback: () => false,
  41. skipLoginVerificationStepsForCpaCallback: async () => {},
  42. STEP6_MAX_ATTEMPTS: 3,
  43. throwIfStopped: () => {},
  44. });
  45. await assert.rejects(
  46. () => executor.executeStep6({ email: 'user@example.com', password: 'secret' }),
  47. /已重试 2 次,仍未成功/
  48. );
  49. assert.equal(events.cleanupCalls, 1);
  50. assert.equal(events.refreshCalls, 3);
  51. assert.equal(events.sendCalls, 3);
  52. assert.equal(events.completed, 0);
  53. });
  54. test('step 6 can skip pre-login cleanup during step 7 recovery replay', async () => {
  55. const events = {
  56. cleanupCalls: 0,
  57. completedPayloads: [],
  58. };
  59. const executor = api.createStep6Executor({
  60. addLog: async () => {},
  61. completeStepFromBackground: async (step, payload) => {
  62. events.completedPayloads.push({ step, payload });
  63. },
  64. getErrorMessage: (error) => error?.message || String(error || ''),
  65. getLoginAuthStateLabel: (state) => state || 'unknown',
  66. getState: async () => ({ email: 'user@example.com', password: 'secret' }),
  67. isStep6RecoverableResult: () => false,
  68. isStep6SuccessResult: (result) => result?.step6Outcome === 'success',
  69. refreshOAuthUrlBeforeStep6: async () => 'https://oauth.example/latest',
  70. reuseOrCreateTab: async () => {},
  71. runPreStep6CookieCleanup: async () => {
  72. events.cleanupCalls += 1;
  73. },
  74. sendToContentScriptResilient: async () => ({
  75. step6Outcome: 'success',
  76. loginVerificationRequestedAt: 123,
  77. }),
  78. shouldSkipLoginVerificationForCpaCallback: () => false,
  79. skipLoginVerificationStepsForCpaCallback: async () => {},
  80. STEP6_MAX_ATTEMPTS: 3,
  81. throwIfStopped: () => {},
  82. });
  83. await executor.executeStep6(
  84. { email: 'user@example.com', password: 'secret' },
  85. { skipPreLoginCleanup: true }
  86. );
  87. assert.equal(events.cleanupCalls, 0);
  88. assert.deepStrictEqual(events.completedPayloads, [
  89. {
  90. step: 6,
  91. payload: { loginVerificationRequestedAt: 123 },
  92. },
  93. ]);
  94. });