1
1
import { getCurrentHub } from '@sentry/core' ;
2
- import type { Transport } from '@sentry/types' ;
2
+ import type { Transport , TransportMakeRequestResponse } from '@sentry/types' ;
3
3
4
4
import { DEFAULT_FLUSH_MIN_DELAY } from '../../src/constants' ;
5
5
import type { ReplayContainer } from '../../src/replay' ;
6
6
import { clearSession } from '../../src/session/clearSession' ;
7
- import * as SendReplayRequest from '../../src/util/sendReplayRequest' ;
8
7
import { BASE_TIMESTAMP , mockSdk } from '../index' ;
9
- import { mockRrweb } from '../mocks/mockRrweb' ;
10
- import { getTestEventCheckout , getTestEventIncremental } from '../utils/getTestEvent' ;
11
8
import { useFakeTimers } from '../utils/use-fake-timers' ;
12
9
13
10
useFakeTimers ( ) ;
@@ -22,93 +19,91 @@ type MockTransportSend = jest.MockedFunction<Transport['send']>;
22
19
describe ( 'Integration | rate-limiting behaviour' , ( ) => {
23
20
let replay : ReplayContainer ;
24
21
let mockTransportSend : MockTransportSend ;
25
- let mockSendReplayRequest : jest . MockedFunction < any > ;
26
- const { record : mockRecord } = mockRrweb ( ) ;
27
22
28
- beforeAll ( async ( ) => {
23
+ beforeEach ( async ( ) => {
29
24
jest . setSystemTime ( new Date ( BASE_TIMESTAMP ) ) ;
30
25
31
26
( { replay } = await mockSdk ( {
27
+ autoStart : false ,
32
28
replayOptions : {
33
29
stickySession : false ,
34
30
} ,
35
31
} ) ) ;
36
32
37
- jest . runAllTimers ( ) ;
38
33
mockTransportSend = getCurrentHub ( ) ?. getClient ( ) ?. getTransport ( ) ?. send as MockTransportSend ;
39
- mockSendReplayRequest = jest . spyOn ( SendReplayRequest , 'sendReplayRequest' ) ;
40
- } ) ;
41
-
42
- beforeEach ( ( ) => {
43
- jest . setSystemTime ( new Date ( BASE_TIMESTAMP ) ) ;
44
- mockRecord . takeFullSnapshot . mockClear ( ) ;
45
- mockTransportSend . mockClear ( ) ;
46
-
47
- // Create a new session and clear mocks because a segment (from initial
48
- // checkout) will have already been uploaded by the time the tests run
49
- clearSession ( replay ) ;
50
- replay [ '_initializeSessionForSampling' ] ( ) ;
51
- replay . setInitialState ( ) ;
52
-
53
- mockSendReplayRequest . mockClear ( ) ;
54
34
} ) ;
55
35
56
36
afterEach ( async ( ) => {
57
- jest . runAllTimers ( ) ;
58
- await new Promise ( process . nextTick ) ;
59
- jest . setSystemTime ( new Date ( BASE_TIMESTAMP ) ) ;
60
37
clearSession ( replay ) ;
61
38
jest . clearAllMocks ( ) ;
62
- } ) ;
63
39
64
- afterAll ( ( ) => {
65
40
replay && replay . stop ( ) ;
66
41
} ) ;
67
42
68
- it ( 'handles rate-limit 429 responses by stopping the replay' , async ( ) => {
69
- expect ( replay . session ?. segmentId ) . toBe ( 0 ) ;
70
- jest . spyOn ( replay , 'stop' ) ;
71
-
72
- const TEST_EVENT = getTestEventCheckout ( { timestamp : BASE_TIMESTAMP } ) ;
43
+ it . each ( [
44
+ [ '429 status code' , { statusCode : 429 , headers : { } } as TransportMakeRequestResponse ] ,
45
+ [
46
+ '200 status code with x-sentry-rate-limits header' ,
47
+ {
48
+ statusCode : 200 ,
49
+ headers : {
50
+ 'x-sentry-rate-limits' : '30' ,
51
+ } ,
52
+ } as TransportMakeRequestResponse ,
53
+ ] ,
54
+ [
55
+ '200 status code with x-sentry-rate-limits replay header' ,
56
+ {
57
+ statusCode : 200 ,
58
+ headers : {
59
+ 'x-sentry-rate-limits' : '30:replay' ,
60
+ } ,
61
+ } as TransportMakeRequestResponse ,
62
+ ] ,
63
+ ] ) ( 'handles %s responses by stopping the replay' , async ( _name , { statusCode, headers } ) => {
64
+ const mockStop = jest . spyOn ( replay , 'stop' ) ;
73
65
74
66
mockTransportSend . mockImplementationOnce ( ( ) => {
75
- return Promise . resolve ( { statusCode : 429 } ) ;
67
+ return Promise . resolve ( { statusCode, headers } ) ;
76
68
} ) ;
77
69
78
- mockRecord . _emitter ( TEST_EVENT ) ;
79
-
80
- // T = base + 5
70
+ replay . start ( ) ;
81
71
await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
82
72
83
- expect ( mockRecord . takeFullSnapshot ) . not . toHaveBeenCalled ( ) ;
84
- expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
85
- expect ( replay ) . toHaveLastSentReplay ( { recordingData : JSON . stringify ( [ TEST_EVENT ] ) } ) ;
86
-
87
- expect ( replay . stop ) . toHaveBeenCalledTimes ( 1 ) ;
88
-
89
- // No user activity to trigger an update
90
- expect ( replay . session ) . toBe ( undefined ) ;
91
-
92
- // let's simulate the default rate-limit time of inactivity (60secs) and check that we
93
- // don't do anything in the meantime or after the time has passed
94
- // 60secs are the default we fall back to in the plain 429 case in updateRateLimits()
95
-
96
- // T = base + 60
97
- await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY * 12 ) ;
73
+ expect ( mockStop ) . toHaveBeenCalledTimes ( 1 ) ;
74
+ expect ( replay . session ) . toBeUndefined ( ) ;
75
+ expect ( replay . isEnabled ( ) ) . toBe ( false ) ;
76
+ } ) ;
98
77
99
- expect ( mockSendReplayRequest ) . toHaveBeenCalledTimes ( 1 ) ;
100
- expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
78
+ it . each ( [
79
+ [
80
+ '200 status code without x-sentry-rate-limits header' ,
81
+ {
82
+ statusCode : 200 ,
83
+ headers : { } ,
84
+ } as TransportMakeRequestResponse ,
85
+ ] ,
86
+ [
87
+ '200 status code with x-sentry-rate-limits profile header' ,
88
+ {
89
+ statusCode : 200 ,
90
+ headers : {
91
+ 'x-sentry-rate-limits' : '30:profile' ,
92
+ } ,
93
+ } as TransportMakeRequestResponse ,
94
+ ] ,
95
+ ] ) ( 'handles %s responses by not stopping' , async ( _name , { statusCode, headers } ) => {
96
+ const mockStop = jest . spyOn ( replay , 'stop' ) ;
101
97
102
- // and let's also emit a new event and check that it is not recorded
103
- const TEST_EVENT3 = getTestEventIncremental ( {
104
- data : { } ,
105
- timestamp : BASE_TIMESTAMP + 7 * DEFAULT_FLUSH_MIN_DELAY ,
98
+ mockTransportSend . mockImplementationOnce ( ( ) => {
99
+ return Promise . resolve ( { statusCode, headers } ) ;
106
100
} ) ;
107
- mockRecord . _emitter ( TEST_EVENT3 ) ;
108
101
109
- // T = base + 80
110
- await advanceTimers ( 20_000 ) ;
111
- expect ( mockSendReplayRequest ) . toHaveBeenCalledTimes ( 1 ) ;
112
- expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
102
+ replay . start ( ) ;
103
+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
104
+
105
+ expect ( mockStop ) . not . toHaveBeenCalled ( ) ;
106
+ expect ( replay . session ) . toBeDefined ( ) ;
107
+ expect ( replay . isEnabled ( ) ) . toBe ( true ) ;
113
108
} ) ;
114
109
} ) ;
0 commit comments