返回 AiToEarn
poll.util.spec.ts
根目录 / project / aitoearn-backend / libs / common / src / utils / poll.util.spec.ts
1 import { describe, expect, it, vi } from 'vitest'
2 import { poll } from './poll.util'
3
4 async function runWithTimers<T>(operation: () => Promise<T>): Promise<T> {
5 vi.useFakeTimers()
6 try {
7 const result = operation()
8 .then(value => ({ value }))
9 .catch((error: unknown) => ({ error }))
10 await vi.runAllTimersAsync()
11 const settled = await result
12 if ('error' in settled) {
13 throw settled.error
14 }
15 return settled.value
16 }
17 finally {
18 vi.useRealTimers()
19 }
20 }
21
22 describe('poll', () => {
23 it('maps timeout errors when errorMapper is provided', async () => {
24 const mappedError = new Error('mapped timeout')
25 const mapperInputs: unknown[] = []
26
27 await expect(runWithTimers(() => poll(
28 async () => ({ done: false }),
29 {
30 intervalMs: 1000,
31 maxPollingMs: 1000,
32 taskName: 'Media processing',
33 errorMapper: (data) => {
34 mapperInputs.push(data)
35 return mappedError
36 },
37 },
38 ))).rejects.toBe(mappedError)
39 expect(mapperInputs).toEqual([{
40 type: 'timeout',
41 taskName: 'Media processing',
42 maxPollingMs: 1000,
43 }])
44 })
45
46 it('maps result errors with the original error data when errorMapper is provided', async () => {
47 const mappedError = new Error('mapped failure')
48 const mapperInputs: unknown[] = []
49 const platformError = { code: 'platform_error', raw: { id: 'error-id' } }
50
51 await expect(runWithTimers(() => poll<string, typeof platformError>(
52 async () => ({ done: true, error: platformError }),
53 {
54 intervalMs: 1000,
55 maxPollingMs: 10_000,
56 taskName: 'Media processing',
57 errorMapper: (data) => {
58 mapperInputs.push(data)
59 return mappedError
60 },
61 },
62 ))).rejects.toBe(mappedError)
63 expect(mapperInputs).toEqual([{
64 type: 'failed',
65 taskName: 'Media processing',
66 error: platformError,
67 }])
68 })
69
70 it('maps completed without data when errorMapper is provided', async () => {
71 const mappedError = new Error('mapped missing data')
72 const mapperInputs: unknown[] = []
73
74 await expect(runWithTimers(() => poll(
75 async () => ({ done: true }),
76 {
77 intervalMs: 1000,
78 maxPollingMs: 10_000,
79 taskName: 'Media processing',
80 errorMapper: (data) => {
81 mapperInputs.push(data)
82 return mappedError
83 },
84 },
85 ))).rejects.toBe(mappedError)
86 expect(mapperInputs).toEqual([{
87 type: 'completed_without_data',
88 taskName: 'Media processing',
89 }])
90 })
91
92 it('does not map errors thrown by pollFn', async () => {
93 const thrownError = new Error('platform exception')
94 const errorMapper = vi.fn(() => new Error('mapped error'))
95
96 await expect(runWithTimers(() => poll(
97 async () => {
98 throw thrownError
99 },
100 {
101 intervalMs: 1000,
102 maxPollingMs: 10_000,
103 taskName: 'Media processing',
104 errorMapper,
105 },
106 ))).rejects.toBe(thrownError)
107 expect(errorMapper).not.toHaveBeenCalled()
108 })
109
110 it('keeps default failed errors when no errorMapper is provided', async () => {
111 await expect(runWithTimers(() => poll(
112 async () => ({ done: true, error: 'failed by platform' }),
113 {
114 intervalMs: 1000,
115 maxPollingMs: 10_000,
116 taskName: 'Media processing',
117 },
118 ))).rejects.toThrow('Media processing failed: failed by platform')
119 })
120
121 it('keeps default completed-without-data errors when no errorMapper is provided', async () => {
122 await expect(runWithTimers(() => poll(
123 async () => ({ done: true }),
124 {
125 intervalMs: 1000,
126 maxPollingMs: 10_000,
127 taskName: 'Media processing',
128 },
129 ))).rejects.toThrow('Media processing completed without data')
130 })
131
132 it('keeps default timeout errors when no errorMapper is provided', async () => {
133 await expect(runWithTimers(() => poll(
134 async () => ({ done: false }),
135 {
136 intervalMs: 1000,
137 maxPollingMs: 1000,
138 taskName: 'Media processing',
139 },
140 ))).rejects.toThrow('Media processing timed out after 0 minutes')
141 })
142
143 it('does not drop falsy result error data', async () => {
144 const mapperInputs: unknown[] = []
145 const mappedError = new Error('mapped falsy failure')
146
147 await expect(runWithTimers(() => poll<string, number>(
148 async () => ({ done: true, error: 0 }),
149 {
150 intervalMs: 1000,
151 maxPollingMs: 10_000,
152 taskName: 'Media processing',
153 errorMapper: (data) => {
154 mapperInputs.push(data)
155 return mappedError
156 },
157 },
158 ))).rejects.toBe(mappedError)
159 expect(mapperInputs).toEqual([{
160 type: 'failed',
161 taskName: 'Media processing',
162 error: 0,
163 }])
164 })
165 })
166
166 lines TYPESCRIPT