@@ -17,6 +17,8 @@ import unboundedMessage from '../mocks/message.V2.UNBOUNDED.1457552650000.json';
1717import boundedZlibMessage from '../mocks/message.V2.BOUNDED.ZLIB.1457552651000.json' ;
1818import keylistGzipMessage from '../mocks/message.V2.KEYLIST.GZIP.1457552652000.json' ;
1919import segmentRemovalMessage from '../mocks/message.V2.SEGMENT_REMOVAL.1457552653000.json' ;
20+ import unboundedMyLargeSegmentsMessage from '../mocks/message.MY_LARGE_SEGMENTS_UPDATE.UNBOUNDED.1457552650000.json' ;
21+ import myLargeSegmentRemovalMessage from '../mocks/message.MY_LARGE_SEGMENTS_UPDATE.SEGMENT_REMOVAL.1457552653000.json' ;
2022
2123import authPushEnabledNicolas from '../mocks/auth.pushEnabled.nicolas@split.io.json' ;
2224import authPushEnabledNicolasAndMarcio from '../mocks/auth.pushEnabled.nicolas@split.io.marcio@split.io.json' ;
@@ -49,6 +51,9 @@ const config = {
4951 } ,
5052 urls : baseUrls ,
5153 streamingEnabled : true ,
54+ sync : {
55+ largeSegmentsEnabled : true
56+ }
5257} ;
5358const settings = settingsFactory ( config ) ;
5459
@@ -68,17 +73,19 @@ const MILLIS_KEYLIST_FALLBACK = 1300;
6873const MILLIS_BOUNDED = 1400 ;
6974const MILLIS_KEYLIST = 1500 ;
7075const MILLIS_SEGMENT_REMOVAL = 1600 ;
76+ const MILLIS_UNBOUNDED_FETCH_LS = 1700 ;
77+ const MILLIS_SEGMENT_REMOVAL_LS = 2100 ;
7178
7279/**
7380 * Sequence of calls:
7481 * 0.0 secs: initial SyncAll (/splitChanges, /mySegments/*), auth, SSE connection
75- * 0.1 secs: SSE connection opened -> syncAll (/splitChanges, /mySegments/*)
82+ * 0.1 secs: SSE connection opened -> syncAll (/splitChanges, /mySegments/*, /myLargeSegments/* )
7683 * 0.2 secs: SPLIT_UPDATE event -> /splitChanges
7784 * 0.3 secs: SPLIT_UPDATE event with old changeNumber
7885 * 0.4 secs: MY_SEGMENTS_UPDATE event -> /mySegments/nicolas@split.io
7986 * 0.5 secs: SPLIT_KILL event -> /splitChanges
8087 * 0.6 secs: creates a new client -> new auth and SSE connection
81- * 0.7 secs: SSE connection opened -> syncAll (/splitChanges, /mySegments/*)
88+ * 0.7 secs: SSE connection opened -> syncAll (/splitChanges, /mySegments/*, /myLargeSegments/* )
8289 * 0.8 secs: MY_SEGMENTS_UPDATE event for new client (with payload).
8390 * 0.9 secs: MY_SEGMENTS_UPDATE event for new client (with empty payload).
8491 * 1.0 secs: creates more clients
@@ -88,9 +95,12 @@ const MILLIS_SEGMENT_REMOVAL = 1600;
8895 * 1.4 secs: MY_SEGMENTS_UPDATE_V2 BoundedFetchRequest event.
8996 * 1.5 secs: MY_SEGMENTS_UPDATE_V2 KeyList event.
9097 * 1.6 secs: MY_SEGMENTS_UPDATE_V2 SegmentRemoval event.
98+ * 1.7 secs: MY_LARGE_SEGMENTS_UPDATE UnboundedFetchRequest event, with 241 ms delay for 'nicolas@split.io' (hash('nicolas@split.io') % 300)
99+ * 1.941 secs: /myLargeSegments/* fetch due to unbounded MY_LARGE_SEGMENTS_UPDATE event -> SPLIT_UPDATE event
100+ * 2.1 secs: MY_LARGE_SEGMENTS_UPDATE SegmentRemoval event -> SPLIT_UPDATE event
91101 */
92102export function testSynchronization ( fetchMock , assert ) {
93- assert . plan ( 38 ) ;
103+ assert . plan ( 44 ) ;
94104 fetchMock . reset ( ) ;
95105
96106 let start , splitio , client , otherClient , keylistAddClient , keylistRemoveClient , bitmapTrueClient , sharedClients = [ ] ;
@@ -236,6 +246,31 @@ export function testSynchronization(fetchMock, assert) {
236246 assert . deepEqual ( sharedClients . map ( c => c . getTreatment ( 'splitters' ) ) , [ 'off' , 'on' , 'off' , 'on' ] , 'evaluation before segment removal' ) ;
237247 bitmapTrueClient . once ( bitmapTrueClient . Event . SDK_UPDATE , ( ) => {
238248 assert . deepEqual ( sharedClients . map ( c => c . getTreatment ( 'splitters' ) ) , [ 'off' , 'off' , 'off' , 'off' ] , 'evaluation after segment removal' ) ;
249+ } ) ;
250+
251+ eventSourceInstance . emitMessage ( segmentRemovalMessage ) ;
252+ } , MILLIS_SEGMENT_REMOVAL - MILLIS_MORE_CLIENTS ) ;
253+
254+ setTimeout ( ( ) => {
255+ assert . equal ( client . getTreatment ( 'in_large_segment' ) , 'no' , 'evaluation before myLargeSegment fetch' ) ;
256+
257+ const timestampUnboundEvent = Date . now ( ) ;
258+ const EXPECTED_DELAY = 241 ;
259+
260+ client . once ( client . Event . SDK_UPDATE , ( ) => {
261+ assert . true ( nearlyEqual ( Date . now ( ) - timestampUnboundEvent , EXPECTED_DELAY ) , 'SDK_UPDATE after fetching myLargeSegments with a delay' ) ;
262+ assert . equal ( client . getTreatment ( 'in_large_segment' ) , 'yes' , 'evaluation after myLargeSegment fetch' ) ;
263+ } ) ;
264+
265+ eventSourceInstance . emitMessage ( unboundedMyLargeSegmentsMessage ) ;
266+ } , MILLIS_UNBOUNDED_FETCH_LS - MILLIS_MORE_CLIENTS ) ;
267+
268+ setTimeout ( ( ) => {
269+ assert . equal ( client . getTreatment ( 'in_large_segment' ) , 'yes' , 'evaluation before large segment removal' ) ;
270+ assert . deepEqual ( sharedClients . map ( c => c . getTreatment ( 'in_large_segment' ) ) , [ 'no' , 'no' , 'no' , 'no' ] , 'evaluation before segment removal' ) ;
271+
272+ client . once ( client . Event . SDK_UPDATE , ( ) => {
273+ assert . equal ( client . getTreatment ( 'in_large_segment' ) , 'no' , 'evaluation after large segment removal' ) ;
239274
240275 // destroy shared clients and then main client
241276 Promise . all ( sharedClients . map ( c => c . destroy ( ) ) )
@@ -252,8 +287,8 @@ export function testSynchronization(fetchMock, assert) {
252287 } ) ;
253288 } ) ;
254289
255- eventSourceInstance . emitMessage ( segmentRemovalMessage ) ;
256- } , MILLIS_SEGMENT_REMOVAL - MILLIS_MORE_CLIENTS ) ;
290+ eventSourceInstance . emitMessage ( myLargeSegmentRemovalMessage ) ;
291+ } , MILLIS_SEGMENT_REMOVAL_LS - MILLIS_MORE_CLIENTS ) ;
257292 } ) ;
258293 } , MILLIS_MORE_CLIENTS - MILLIS_NEW_CLIENT ) ;
259294
@@ -283,7 +318,7 @@ export function testSynchronization(fetchMock, assert) {
283318 authParams += `&users=${ encodeURIComponent ( keylistAddKey ) } &users=${ encodeURIComponent ( keylistRemoveKey ) } &users=${ encodeURIComponent ( bitmapTrueKey ) } ` ;
284319 fetchMock . getOnce ( url ( settings , `/v2/auth?s=1.1&${ authParams } ` ) , { status : 200 , body : authPushEnabledNicolasAndMarcio } ) ;
285320
286- // initial split and mySegments sync
321+ // initial sync
287322 fetchMock . getOnce ( url ( settings , '/splitChanges?s=1.1&since=-1' ) , function ( url , opts ) {
288323 const lapse = Date . now ( ) - start ;
289324 assert . true ( nearlyEqual ( lapse , 0 ) , 'initial sync' ) ;
@@ -294,6 +329,7 @@ export function testSynchronization(fetchMock, assert) {
294329 if ( hasNoCacheHeader ( opts ) ) assert . fail ( 'request must not include `Cache-Control` header' ) ;
295330 return { status : 200 , body : mySegmentsNicolasMock1 } ;
296331 } ) ;
332+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/nicolas%40split.io' ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
297333
298334 // split and segment sync after SSE opened
299335 fetchMock . getOnce ( url ( settings , '/splitChanges?s=1.1&since=1457552620999' ) , function ( url , opts ) {
@@ -306,6 +342,7 @@ export function testSynchronization(fetchMock, assert) {
306342 if ( hasNoCacheHeader ( opts ) ) assert . fail ( 'request must not include `Cache-Control` header' ) ;
307343 return { status : 200 , body : mySegmentsNicolasMock1 } ;
308344 } ) ;
345+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/nicolas%40split.io' ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
309346
310347 // fetch due to SPLIT_UPDATE event
311348 fetchMock . getOnce ( url ( settings , '/splitChanges?s=1.1&since=1457552620999' ) , function ( url , opts ) {
@@ -326,13 +363,15 @@ export function testSynchronization(fetchMock, assert) {
326363 return { status : 200 , body : splitChangesMock4 } ;
327364 } ) ;
328365
329- // initial fetch of mySegments for new client
366+ // initial fetch of mySegments and myLargeSegments for new client
330367 fetchMock . getOnce ( url ( settings , '/mySegments/marcio%40split.io' ) , function ( url , opts ) {
331368 if ( hasNoCacheHeader ( opts ) ) assert . fail ( 'request must not include `Cache-Control` header' ) ;
332369 return { status : 200 , body : mySegmentsMarcio } ;
333370 } ) ;
371+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/marcio%40split.io' ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
372+
334373
335- // split and mySegment sync after second SSE opened
374+ // sync after second SSE opened
336375 fetchMock . getOnce ( url ( settings , '/splitChanges?s=1.1&since=1457552650000' ) , function ( url , opts ) {
337376 const lapse = Date . now ( ) - start ;
338377 assert . true ( nearlyEqual ( lapse , MILLIS_SECOND_SSE_OPEN ) , 'sync after second SSE connection is opened' ) ;
@@ -347,6 +386,9 @@ export function testSynchronization(fetchMock, assert) {
347386 if ( hasNoCacheHeader ( opts ) ) assert . fail ( 'request must not include `Cache-Control` header' ) ;
348387 return { status : 200 , body : mySegmentsMarcio } ;
349388 } ) ;
389+ fetchMock . get ( { url : url ( settings , '/myLargeSegments/nicolas%40split.io' ) , repeat : 2 } , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
390+ fetchMock . get ( { url : url ( settings , '/myLargeSegments/marcio%40split.io' ) , repeat : 2 } , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
391+
350392 // 3 unbounded fetch requests
351393 fetchMock . get ( { url : url ( settings , '/mySegments/nicolas%40split.io' ) , repeat : 3 } , function ( url , opts ) {
352394 if ( ! hasNoCacheHeader ( opts ) ) assert . fail ( 'request must not include `Cache-Control` header' ) ;
@@ -357,15 +399,25 @@ export function testSynchronization(fetchMock, assert) {
357399 return { status : 200 , body : mySegmentsMarcio } ;
358400 } ) ;
359401
360- // initial fetch of mySegments for other clients + sync after third SSE opened + 3 unbounded fetch requests
402+ // initial fetch of mySegments and myLargeSegments for other clients + sync after third SSE opened + 3 unbounded fetch requests for mySegments
361403 fetchMock . getOnce ( url ( settings , '/splitChanges?s=1.1&since=1457552650000' ) , { status : 200 , body : { splits : [ ] , since : 1457552650000 , till : 1457552650000 } } ) ;
362404 fetchMock . get ( { url : url ( settings , '/mySegments/key1' ) , repeat : 5 } , { status : 200 , body : { mySegments : [ ] } } ) ;
363405 fetchMock . get ( { url : url ( settings , '/mySegments/key3' ) , repeat : 5 } , { status : 200 , body : { mySegments : [ { name : 'splitters' } ] } } ) ;
364406 fetchMock . get ( { url : url ( settings , `/mySegments/${ bitmapTrueKey } ` ) , repeat : 5 } , { status : 200 , body : { mySegments : [ ] } } ) ;
407+ fetchMock . get ( { url : url ( settings , '/myLargeSegments/key1' ) , repeat : 2 } , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
408+ fetchMock . get ( { url : url ( settings , '/myLargeSegments/key3' ) , repeat : 2 } , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
409+ fetchMock . get ( { url : url ( settings , `/myLargeSegments/${ bitmapTrueKey } ` ) , repeat : 2 } , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
365410
366411 // bounded fetch request
367412 fetchMock . get ( url ( settings , `/mySegments/${ bitmapTrueKey } ` ) , { status : 200 , body : { mySegments : [ { name : 'splitters' } ] } } ) ;
368413
414+ // unbounded myLargeSegments fetch requests
415+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/nicolas%40split.io' ) , { status : 200 , body : { myLargeSegments : [ 'employees' , 'splitters' ] } } ) ;
416+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/marcio%40split.io' ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
417+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/key1' ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
418+ fetchMock . getOnce ( url ( settings , '/myLargeSegments/key3' ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
419+ fetchMock . getOnce ( url ( settings , `/myLargeSegments/${ bitmapTrueKey } ` ) , { status : 200 , body : { myLargeSegments : [ ] } } ) ;
420+
369421 fetchMock . get ( new RegExp ( '.*' ) , function ( url ) {
370422 assert . fail ( 'unexpected GET request with url: ' + url ) ;
371423 } ) ;
0 commit comments