/** 轮询工作者:每 tick 调 pollDue;上一 tick 没跑完就跳过这一拍;出错的账号写 stderr;stop 后不再跑 */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { MailPollOutcome } from '@contracts'; import { pollMailOnce, startMailPoller } from '../workers/mail-poller.ts'; function fakeMail(outcomes: MailPollOutcome[], holdMs = 0) { const calls: Date[] = []; return { calls, mail: { pollDue: async (now: Date) => { calls.push(now); if (holdMs > 0) await new Promise((r) => setTimeout(r, holdMs)); return outcomes; }, }, }; } describe('pollMailOnce', () => { it('回用例的结果;出错的账号写一行 stderr', async () => { const write = vi.spyOn(process.stderr, 'write').mockImplementation(() => true); const { mail } = fakeMail([{ accountId: 'a', pulled: 1, stored: 1, dispatched: 1, more: false }, { accountId: 'b', pulled: 0, stored: 0, dispatched: 0, more: false, error: '连不上' }]); const out = await pollMailOnce({ container: { mail: mail as never } }); expect(out).toHaveLength(2); expect(write).toHaveBeenCalledWith(expect.stringContaining('[mail-poller] b:连不上')); write.mockRestore(); }); }); describe('startMailPoller', () => { beforeEach(() => vi.useFakeTimers()); afterEach(() => vi.useRealTimers()); it('按 tick 调;在跑时跳过;stop 后停', async () => { const { mail, calls } = fakeMail([], 1000); const poller = startMailPoller({ container: { mail: mail as never } }, 100); await vi.advanceTimersByTimeAsync(350); expect(calls).toHaveLength(1); // 第一拍 1000ms 没跑完,后面两拍跳过 await vi.advanceTimersByTimeAsync(1000); expect(calls.length).toBeGreaterThanOrEqual(2); poller.stop(); const before = calls.length; await vi.advanceTimersByTimeAsync(2000); expect(calls).toHaveLength(before); }); });