@@ -50,6 +50,8 @@ describe('Integration | rate-limiting behaviour', () => {
5050 replay . loadSession ( { expiry : 0 } ) ;
5151
5252 mockSendReplayRequest . mockClear ( ) ;
53+
54+ replay [ '_rateLimits' ] = { } ;
5355 } ) ;
5456
5557 afterEach ( async ( ) => {
@@ -171,4 +173,87 @@ describe('Integration | rate-limiting behaviour', () => {
171173 expect ( replay . eventBuffer ?. length ) . toBe ( 0 ) ;
172174 } ,
173175 ) ;
176+
177+ it ( 'handles rate-limits from a plain 429 response without any retry time' , async ( ) => {
178+ expect ( replay . session ?. segmentId ) . toBe ( 0 ) ;
179+ jest . spyOn ( replay , 'sendReplay' ) ;
180+ jest . spyOn ( replay , 'pause' ) ;
181+ jest . spyOn ( replay , 'resume' ) ;
182+ // @ts -ignore private API
183+ jest . spyOn ( replay , '_handleRateLimit' ) ;
184+
185+ const TEST_EVENT = { data : { } , timestamp : BASE_TIMESTAMP , type : 2 } ;
186+
187+ mockTransportSend . mockImplementationOnce ( ( ) => {
188+ return Promise . resolve ( { statusCode : 429 } ) ;
189+ } ) ;
190+
191+ mockRecord . _emitter ( TEST_EVENT ) ;
192+
193+ // T = base + 5
194+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
195+
196+ expect ( mockRecord . takeFullSnapshot ) . not . toHaveBeenCalled ( ) ;
197+ expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
198+ expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT ] ) } ) ;
199+
200+ expect ( replay [ '_handleRateLimit' ] ) . toHaveBeenCalledTimes ( 1 ) ;
201+ // resume() was called once before we even started
202+ expect ( replay . resume ) . not . toHaveBeenCalled ( ) ;
203+ expect ( replay . pause ) . toHaveBeenCalledTimes ( 1 ) ;
204+
205+ // No user activity to trigger an update
206+ expect ( replay . session ?. lastActivity ) . toBe ( BASE_TIMESTAMP ) ;
207+ expect ( replay . session ?. segmentId ) . toBe ( 1 ) ;
208+
209+ // let's simulate the rate-limit time of inactivity (60secs) and check that we don't do anything in the meantime
210+ // 60secs are the default we fall back to in the plain 429 case in updateRateLimits()
211+ const TEST_EVENT2 = { data : { } , timestamp : BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY , type : 3 } ;
212+ for ( let i = 0 ; i < 11 ; i ++ ) {
213+ const ev = {
214+ ...TEST_EVENT2 ,
215+ timestamp : BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY * ( i + 1 ) ,
216+ } ;
217+ mockRecord . _emitter ( ev ) ;
218+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
219+ expect ( replay . isPaused ( ) ) . toBe ( true ) ;
220+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 1 ) ;
221+ expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
222+ }
223+
224+ // T = base + 60
225+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
226+
227+ // now, recording should resume and first, we expect a checkout event to be sent, as resume()
228+ // should trigger a full snapshot
229+ expect ( replay . resume ) . toHaveBeenCalledTimes ( 1 ) ;
230+ expect ( replay . isPaused ( ) ) . toBe ( false ) ;
231+
232+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 2 ) ;
233+ expect ( replay ) . toHaveLastSentReplay ( {
234+ events : '[{"data":{"isCheckout":true},"timestamp":1580598065000,"type":2}]' ,
235+ } ) ;
236+
237+ // and let's also emit a new event and check that it is recorded
238+ const TEST_EVENT3 = {
239+ data : { } ,
240+ timestamp : BASE_TIMESTAMP + 7 * DEFAULT_FLUSH_MIN_DELAY ,
241+ type : 3 ,
242+ } ;
243+ mockRecord . _emitter ( TEST_EVENT3 ) ;
244+
245+ // T = base + 65
246+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
247+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 3 ) ;
248+ expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT3 ] ) } ) ;
249+
250+ // nothing should happen afterwards
251+ // T = base + 85
252+ await advanceTimers ( 20_000 ) ;
253+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 3 ) ;
254+ expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT3 ] ) } ) ;
255+
256+ // events array should be empty
257+ expect ( replay . eventBuffer ?. length ) . toBe ( 0 ) ;
258+ } ) ;
174259} ) ;
0 commit comments