@@ -57,98 +57,125 @@ describe('Integration | rate-limiting behaviour', () => {
57
57
await new Promise ( process . nextTick ) ;
58
58
jest . setSystemTime ( new Date ( BASE_TIMESTAMP ) ) ;
59
59
clearSession ( replay ) ;
60
+ jest . clearAllMocks ( ) ;
60
61
replay . loadSession ( { expiry : SESSION_IDLE_DURATION } ) ;
61
62
} ) ;
62
63
63
64
afterAll ( ( ) => {
64
65
replay && replay . stop ( ) ;
65
66
} ) ;
66
67
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
113
116
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 ( ) ;
116
119
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
+ ) ;
154
181
} ) ;
0 commit comments