@@ -57,98 +57,125 @@ describe('Integration | rate-limiting behaviour', () => {
5757 await new Promise ( process . nextTick ) ;
5858 jest . setSystemTime ( new Date ( BASE_TIMESTAMP ) ) ;
5959 clearSession ( replay ) ;
60+ jest . clearAllMocks ( ) ;
6061 replay . loadSession ( { expiry : SESSION_IDLE_DURATION } ) ;
6162 } ) ;
6263
6364 afterAll ( ( ) => {
6465 replay && replay . stop ( ) ;
6566 } ) ;
6667
67- it ( 'pauses recording and flushing a rate limit is hit and resumes both after the rate limit duration is over' , async ( ) => {
68- expect ( replay . session ?. segmentId ) . toBe ( 0 ) ;
69- jest . spyOn ( replay , 'sendReplay' ) ;
70- jest . spyOn ( replay , 'pause' ) ;
71- jest . spyOn ( replay , 'resume' ) ;
72- // @ts -ignore private API
73- jest . spyOn ( replay , '_handleRateLimit' ) ;
74-
75- const TEST_EVENT = { data : { } , timestamp : BASE_TIMESTAMP , type : 2 } ;
76-
77- mockTransportSend . mockImplementationOnce ( ( ) => {
78- return Promise . resolve ( {
79- statusCode : 429 ,
80- headers : {
81- 'x-sentry-rate-limits' : null ,
82- 'retry-after' : `30` ,
83- } ,
84- } as TransportMakeRequestResponse ) ;
85- } ) ;
86-
87- mockRecord . _emitter ( TEST_EVENT ) ;
88-
89- // T = base + 5
90- await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
91-
92- expect ( mockRecord . takeFullSnapshot ) . not . toHaveBeenCalled ( ) ;
93- expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
94- expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT ] ) } ) ;
95-
96- expect ( replay [ '_handleRateLimit' ] ) . toHaveBeenCalledTimes ( 1 ) ;
97- // resume() was called once before we even started
98- expect ( replay . resume ) . not . toHaveBeenCalled ( ) ;
99- expect ( replay . pause ) . toHaveBeenCalledTimes ( 1 ) ;
100-
101- // No user activity to trigger an update
102- expect ( replay . session ?. lastActivity ) . toBe ( BASE_TIMESTAMP ) ;
103- expect ( replay . session ?. segmentId ) . toBe ( 1 ) ;
104-
105- // let's simulate the rate-limit time of inactivity (30secs) and check that we don't do anything in the meantime
106- const TEST_EVENT2 = { data : { } , timestamp : BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY , type : 3 } ;
107- for ( let i = 0 ; i < 5 ; i ++ ) {
108- const ev = {
109- ...TEST_EVENT2 ,
110- timestamp : BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY * ( i + 1 ) ,
111- } ;
112- mockRecord . _emitter ( ev ) ;
68+ it . each ( [
69+ {
70+ statusCode : 429 ,
71+ headers : {
72+ 'x-sentry-rate-limits' : '30' ,
73+ 'retry-after' : null ,
74+ } ,
75+ } ,
76+ {
77+ statusCode : 429 ,
78+ headers : {
79+ 'x-sentry-rate-limits' : '30:replay_event' ,
80+ 'retry-after' : null ,
81+ } ,
82+ } ,
83+ {
84+ statusCode : 429 ,
85+ headers : {
86+ 'x-sentry-rate-limits' : '30:replay_recording' ,
87+ 'retry-after' : null ,
88+ } ,
89+ } ,
90+ {
91+ statusCode : 429 ,
92+ headers : {
93+ 'x-sentry-rate-limits' : null ,
94+ 'retry-after' : '30' ,
95+ } ,
96+ } ,
97+ ] as TransportMakeRequestResponse [ ] ) (
98+ 'pauses recording and flushing a rate limit is hit and resumes both after the rate limit duration is over' ,
99+ async rateLimitResponse => {
100+ expect ( replay . session ?. segmentId ) . toBe ( 0 ) ;
101+ jest . spyOn ( replay , 'sendReplay' ) ;
102+ jest . spyOn ( replay , 'pause' ) ;
103+ jest . spyOn ( replay , 'resume' ) ;
104+ // @ts -ignore private API
105+ jest . spyOn ( replay , '_handleRateLimit' ) ;
106+
107+ const TEST_EVENT = { data : { } , timestamp : BASE_TIMESTAMP , type : 2 } ;
108+
109+ mockTransportSend . mockImplementationOnce ( ( ) => {
110+ return Promise . resolve ( rateLimitResponse ) ;
111+ } ) ;
112+
113+ mockRecord . _emitter ( TEST_EVENT ) ;
114+
115+ // T = base + 5
113116 await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
114- expect ( replay . isPaused ( ) ) . toBe ( true ) ;
115- expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 1 ) ;
117+
118+ expect ( mockRecord . takeFullSnapshot ) . not . toHaveBeenCalled ( ) ;
116119 expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
117- }
118-
119- // T = base + 35
120- await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
121-
122- // now, recording should resume and first, we expect a checkout event to be sent, as resume()
123- // should trigger a full snapshot
124- expect ( replay . resume ) . toHaveBeenCalledTimes ( 1 ) ;
125- expect ( replay . isPaused ( ) ) . toBe ( false ) ;
126-
127- expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 2 ) ;
128- expect ( replay ) . toHaveLastSentReplay ( {
129- events : '[{"data":{"isCheckout":true},"timestamp":1580598035000,"type":2}]' ,
130- } ) ;
131-
132- // and let's also emit a new event and check that it is recorded
133- const TEST_EVENT3 = {
134- data : { } ,
135- timestamp : BASE_TIMESTAMP + 7 * DEFAULT_FLUSH_MIN_DELAY ,
136- type : 3 ,
137- } ;
138- mockRecord . _emitter ( TEST_EVENT3 ) ;
139-
140- // T = base + 40
141- await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
142- expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 3 ) ;
143- expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT3 ] ) } ) ;
144-
145- // nothing should happen afterwards
146- // T = base + 60
147- await advanceTimers ( 20_000 ) ;
148- expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 3 ) ;
149- expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT3 ] ) } ) ;
150-
151- // events array should be empty
152- expect ( replay . eventBuffer ?. length ) . toBe ( 0 ) ;
153- } ) ;
120+ expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT ] ) } ) ;
121+
122+ expect ( replay [ '_handleRateLimit' ] ) . toHaveBeenCalledTimes ( 1 ) ;
123+ // resume() was called once before we even started
124+ expect ( replay . resume ) . not . toHaveBeenCalled ( ) ;
125+ expect ( replay . pause ) . toHaveBeenCalledTimes ( 1 ) ;
126+
127+ // No user activity to trigger an update
128+ expect ( replay . session ?. lastActivity ) . toBe ( BASE_TIMESTAMP ) ;
129+ expect ( replay . session ?. segmentId ) . toBe ( 1 ) ;
130+
131+ // let's simulate the rate-limit time of inactivity (30secs) and check that we don't do anything in the meantime
132+ const TEST_EVENT2 = { data : { } , timestamp : BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY , type : 3 } ;
133+ for ( let i = 0 ; i < 5 ; i ++ ) {
134+ const ev = {
135+ ...TEST_EVENT2 ,
136+ timestamp : BASE_TIMESTAMP + DEFAULT_FLUSH_MIN_DELAY * ( i + 1 ) ,
137+ } ;
138+ mockRecord . _emitter ( ev ) ;
139+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
140+ expect ( replay . isPaused ( ) ) . toBe ( true ) ;
141+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 1 ) ;
142+ expect ( mockTransportSend ) . toHaveBeenCalledTimes ( 1 ) ;
143+ }
144+
145+ // T = base + 35
146+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
147+
148+ // now, recording should resume and first, we expect a checkout event to be sent, as resume()
149+ // should trigger a full snapshot
150+ expect ( replay . resume ) . toHaveBeenCalledTimes ( 1 ) ;
151+ expect ( replay . isPaused ( ) ) . toBe ( false ) ;
152+
153+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 2 ) ;
154+ expect ( replay ) . toHaveLastSentReplay ( {
155+ events : '[{"data":{"isCheckout":true},"timestamp":1580598035000,"type":2}]' ,
156+ } ) ;
157+
158+ // and let's also emit a new event and check that it is recorded
159+ const TEST_EVENT3 = {
160+ data : { } ,
161+ timestamp : BASE_TIMESTAMP + 7 * DEFAULT_FLUSH_MIN_DELAY ,
162+ type : 3 ,
163+ } ;
164+ mockRecord . _emitter ( TEST_EVENT3 ) ;
165+
166+ // T = base + 40
167+ await advanceTimers ( DEFAULT_FLUSH_MIN_DELAY ) ;
168+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 3 ) ;
169+ expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT3 ] ) } ) ;
170+
171+ // nothing should happen afterwards
172+ // T = base + 60
173+ await advanceTimers ( 20_000 ) ;
174+ expect ( replay . sendReplay ) . toHaveBeenCalledTimes ( 3 ) ;
175+ expect ( replay ) . toHaveLastSentReplay ( { events : JSON . stringify ( [ TEST_EVENT3 ] ) } ) ;
176+
177+ // events array should be empty
178+ expect ( replay . eventBuffer ?. length ) . toBe ( 0 ) ;
179+ } ,
180+ ) ;
154181} ) ;
0 commit comments