@@ -2,47 +2,19 @@ import { UploadStateMachine } from '../UploadStateMachine';
22import type { Persistor } from '@segment/sovran-react-native' ;
33import type { RateLimitConfig } from '../../types' ;
44import { getMockLogger } from '../../test-helpers' ;
5+ import { createMockStore , createTestPersistor } from './test-helpers' ;
56
6- // Mock sovran-react-native
7- jest . mock ( '@segment/sovran-react-native' , ( ) => {
8- const actualModule = jest . requireActual ( '@segment/sovran-react-native' ) ;
9- return {
10- ...actualModule ,
11- createStore : jest . fn ( ( initialState : unknown ) => {
12- let state = initialState ;
13- return {
14- getState : jest . fn ( ( ) => Promise . resolve ( state ) ) ,
15- dispatch : jest . fn ( ( action : unknown ) => {
16- // Handle functional dispatch
17- if ( typeof action === 'function' ) {
18- state = action ( state ) ;
19- } else {
20- // Handle action object dispatch - add type guard for payload
21- const typedAction = action as { type : string ; payload : unknown } ;
22- state = typedAction . payload ;
23- }
24- return Promise . resolve ( ) ;
25- } ) ,
26- } ;
27- } ) ,
28- } ;
29- } ) ;
7+ jest . mock ( '@segment/sovran-react-native' , ( ) => ( {
8+ ...jest . requireActual ( '@segment/sovran-react-native' ) ,
9+ createStore : jest . fn ( ( initialState : unknown ) =>
10+ createMockStore ( initialState )
11+ ) ,
12+ } ) ) ;
3013
3114describe ( 'UploadStateMachine' , ( ) => {
32- // Shared storage for all persistors to simulate real persistence
33- let sharedStorage : Record < string , unknown > = { } ;
34-
35- const createMockPersistor = ( ) : Persistor => {
36- return {
37- get : async < T > ( key : string ) : Promise < T | undefined > => {
38- return Promise . resolve ( sharedStorage [ key ] as T ) ;
39- } ,
40- set : async < T > ( key : string , state : T ) : Promise < void > => {
41- sharedStorage [ key ] = state ;
42- return Promise . resolve ( ) ;
43- } ,
44- } ;
45- } ;
15+ let sharedStorage : Record < string , unknown > ;
16+ let mockPersistor : Persistor ;
17+ let mockLogger : ReturnType < typeof getMockLogger > ;
4618
4719 const defaultConfig : RateLimitConfig = {
4820 enabled : true ,
@@ -51,12 +23,9 @@ describe('UploadStateMachine', () => {
5123 maxRateLimitDuration : 43200 ,
5224 } ;
5325
54- let mockPersistor : Persistor ;
55- let mockLogger : ReturnType < typeof getMockLogger > ;
56-
5726 beforeEach ( ( ) => {
58- sharedStorage = { } ; // Reset shared storage
59- mockPersistor = createMockPersistor ( ) ;
27+ sharedStorage = { } ;
28+ mockPersistor = createTestPersistor ( sharedStorage ) ;
6029 mockLogger = getMockLogger ( ) ;
6130 jest . clearAllMocks ( ) ;
6231 } ) ;
@@ -70,48 +39,40 @@ describe('UploadStateMachine', () => {
7039 mockLogger
7140 ) ;
7241
73- const canUpload = await stateMachine . canUpload ( ) ;
74- expect ( canUpload ) . toBe ( true ) ;
42+ expect ( await stateMachine . canUpload ( ) ) . toBe ( true ) ;
7543 } ) ;
7644
77- it ( 'returns false when in WAITING state and waitUntilTime not passed' , async ( ) => {
45+ it ( 'returns false when in RATE_LIMITED state and waitUntilTime not passed' , async ( ) => {
7846 const stateMachine = new UploadStateMachine (
7947 'test-key' ,
8048 mockPersistor ,
8149 defaultConfig ,
8250 mockLogger
8351 ) ;
8452
85- // Set to waiting state with future time
8653 await stateMachine . handle429 ( 60 ) ;
8754
88- const canUpload = await stateMachine . canUpload ( ) ;
89- expect ( canUpload ) . toBe ( false ) ;
55+ expect ( await stateMachine . canUpload ( ) ) . toBe ( false ) ;
9056 expect ( mockLogger . info ) . toHaveBeenCalledWith (
9157 expect . stringContaining ( 'Upload blocked: rate limited' )
9258 ) ;
9359 } ) ;
9460
9561 it ( 'transitions to READY and returns true when waitUntilTime has passed' , async ( ) => {
62+ const now = 1000000 ;
63+ jest . spyOn ( Date , 'now' ) . mockReturnValue ( now ) ;
64+
9665 const stateMachine = new UploadStateMachine (
9766 'test-key' ,
9867 mockPersistor ,
9968 defaultConfig ,
10069 mockLogger
10170 ) ;
10271
103- // Mock Date.now to control time
104- const now = 1000000 ;
105- jest . spyOn ( Date , 'now' ) . mockReturnValue ( now ) ;
106-
107- // Set to waiting state
10872 await stateMachine . handle429 ( 60 ) ;
109-
110- // Advance time past waitUntilTime
11173 jest . spyOn ( Date , 'now' ) . mockReturnValue ( now + 61000 ) ;
11274
113- const canUpload = await stateMachine . canUpload ( ) ;
114- expect ( canUpload ) . toBe ( true ) ;
75+ expect ( await stateMachine . canUpload ( ) ) . toBe ( true ) ;
11576 expect ( mockLogger . info ) . toHaveBeenCalledWith (
11677 'Upload state transitioned to READY'
11778 ) ;
@@ -131,17 +92,10 @@ describe('UploadStateMachine', () => {
13192 ) ;
13293
13394 await stateMachine . handle429 ( 60 ) ;
134-
135- // Wait for store update
13695 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
13796
138- const globalRetryCount = await stateMachine . getGlobalRetryCount ( ) ;
139- expect ( globalRetryCount ) . toBe ( 1 ) ;
140-
141- // Verify it's in WAITING state
142- const canUpload = await stateMachine . canUpload ( ) ;
143- expect ( canUpload ) . toBe ( false ) ;
144-
97+ expect ( await stateMachine . getGlobalRetryCount ( ) ) . toBe ( 1 ) ;
98+ expect ( await stateMachine . canUpload ( ) ) . toBe ( false ) ;
14599 expect ( mockLogger . info ) . toHaveBeenCalledWith (
146100 'Rate limited (429): waiting 60s before retry 1/100'
147101 ) ;
@@ -165,17 +119,13 @@ describe('UploadStateMachine', () => {
165119 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
166120 await stateMachine . handle429 ( 10 ) ;
167121 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
168-
169- // 4th attempt should reset
170122 await stateMachine . handle429 ( 10 ) ;
171123 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
172124
173125 expect ( mockLogger . warn ) . toHaveBeenCalledWith (
174126 'Max retry count exceeded (3), resetting rate limiter'
175127 ) ;
176-
177- const retryCount = await stateMachine . getGlobalRetryCount ( ) ;
178- expect ( retryCount ) . toBe ( 0 ) ; // Reset to 0
128+ expect ( await stateMachine . getGlobalRetryCount ( ) ) . toBe ( 0 ) ;
179129 } ) ;
180130
181131 it ( 'resets state when max total backoff duration exceeded' , async ( ) => {
@@ -184,7 +134,7 @@ describe('UploadStateMachine', () => {
184134
185135 const limitedConfig : RateLimitConfig = {
186136 ...defaultConfig ,
187- maxRateLimitDuration : 10 , // Only 10 seconds allowed
137+ maxRateLimitDuration : 10 ,
188138 } ;
189139 const stateMachine = new UploadStateMachine (
190140 'test-key' ,
@@ -194,18 +144,13 @@ describe('UploadStateMachine', () => {
194144 ) ;
195145
196146 await stateMachine . handle429 ( 5 ) ;
197-
198- // Advance time beyond maxRateLimitDuration
199147 jest . spyOn ( Date , 'now' ) . mockReturnValue ( now + 11000 ) ;
200-
201148 await stateMachine . handle429 ( 5 ) ;
202149
203150 expect ( mockLogger . warn ) . toHaveBeenCalledWith (
204151 'Max backoff duration exceeded (10s), resetting rate limiter'
205152 ) ;
206-
207- const retryCount = await stateMachine . getGlobalRetryCount ( ) ;
208- expect ( retryCount ) . toBe ( 0 ) ;
153+ expect ( await stateMachine . getGlobalRetryCount ( ) ) . toBe ( 0 ) ;
209154 } ) ;
210155 } ) ;
211156
@@ -218,12 +163,10 @@ describe('UploadStateMachine', () => {
218163 mockLogger
219164 ) ;
220165
221- // Put into WAITING state
222166 await stateMachine . handle429 ( 60 ) ;
223167 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
224168 expect ( await stateMachine . getGlobalRetryCount ( ) ) . toBe ( 1 ) ;
225169
226- // Reset
227170 await stateMachine . reset ( ) ;
228171 await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
229172
0 commit comments