@@ -27,6 +27,26 @@ class BatchEventBuilderTests_Events: XCTestCase {
27
27
var project : Project !
28
28
let datafile = OTUtils . loadJSONDatafile ( " api_datafile " ) !
29
29
30
+ var sampleHoldout : [ String : Any ] {
31
+ return [
32
+ " status " : " Running " ,
33
+ " id " : " holdout_4444444 " ,
34
+ " key " : " holdout_key " ,
35
+ " layerId " : " 10420273888 " ,
36
+ " trafficAllocation " : [
37
+ [ " entityId " : " holdout_variation_a11 " , " endOfRange " : 10000 ] // 100% traffic allocation
38
+ ] ,
39
+ " audienceIds " : [ ] ,
40
+ " variations " : [
41
+ [
42
+ " variables " : [ ] ,
43
+ " id " : " holdout_variation_a11 " ,
44
+ " key " : " holdout_a "
45
+ ]
46
+ ]
47
+ ]
48
+ }
49
+
30
50
override func setUp( ) {
31
51
eventDispatcher = MockEventDispatcher ( )
32
52
optimizely = OTUtils . createOptimizely ( datafileName: " audience_targeting " ,
@@ -38,6 +58,10 @@ class BatchEventBuilderTests_Events: XCTestCase {
38
58
override func tearDown( ) {
39
59
Utils . sdkVersion = OPTIMIZELYSDKVERSION
40
60
Utils . swiftSdkClientName = " swift-sdk "
61
+ optimizely? . close ( )
62
+ optimizely = nil
63
+ optimizely? . eventDispatcher = nil
64
+ super. tearDown ( )
41
65
}
42
66
43
67
func testCreateImpressionEvent( ) {
@@ -461,6 +485,164 @@ extension BatchEventBuilderTests_Events {
461
485
}
462
486
}
463
487
488
+ // MARK:- Holdouts
489
+
490
+ extension BatchEventBuilderTests_Events {
491
+ func testImpressionEvent_UserInHoldout( ) {
492
+ let eventDispatcher2 = MockEventDispatcher ( )
493
+ var optimizely : OptimizelyClient ! = OptimizelyClient ( sdkKey: " 12345 " , eventDispatcher: eventDispatcher2)
494
+
495
+ try ! optimizely. start ( datafile: datafile)
496
+
497
+ let holdout : Holdout = try ! OTUtils . model ( from: sampleHoldout)
498
+ optimizely. config? . project. holdouts = [ holdout]
499
+
500
+ let exp = expectation ( description: " Wait for event to dispatch " )
501
+ let user = optimizely. createUserContext ( userId: userId)
502
+ _ = user. decide ( key: featureKey)
503
+
504
+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.1 ) {
505
+ exp. fulfill ( )
506
+ }
507
+
508
+ let result = XCTWaiter . wait ( for: [ exp] , timeout: 0.2 )
509
+ if result == XCTWaiter . Result. completed {
510
+ let event = getFirstEventJSON ( client: optimizely) !
511
+ let visitor = ( event [ " visitors " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
512
+ let snapshot = ( visitor [ " snapshots " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
513
+ let decision = ( snapshot [ " decisions " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
514
+
515
+ let metaData = decision [ " metadata " ] as! Dictionary < String , Any >
516
+ XCTAssertEqual ( metaData [ " rule_type " ] as! String , Constants . DecisionSource. holdout. rawValue)
517
+ XCTAssertEqual ( metaData [ " rule_key " ] as! String , " holdout_key " )
518
+ XCTAssertEqual ( metaData [ " flag_key " ] as! String , " feature_1 " )
519
+ XCTAssertEqual ( metaData [ " variation_key " ] as! String , " holdout_a " )
520
+ XCTAssertFalse ( metaData [ " enabled " ] as! Bool )
521
+ } else {
522
+ XCTFail ( " No event found " )
523
+ }
524
+
525
+ }
526
+
527
+ func testImpressionEvent_UserInHoldout_IncludedFlags( ) {
528
+ let eventDispatcher2 = MockEventDispatcher ( )
529
+ var optimizely : OptimizelyClient ! = OptimizelyClient ( sdkKey: " 12345 " , eventDispatcher: eventDispatcher2)
530
+
531
+ try ! optimizely. start ( datafile: datafile)
532
+
533
+ var holdout : Holdout = try ! OTUtils . model ( from: sampleHoldout)
534
+ holdout. includedFlags = [ " 4482920077 " ]
535
+ optimizely. config? . project. holdouts = [ holdout]
536
+
537
+ let exp = expectation ( description: " Wait for event to dispatch " )
538
+
539
+ let user = optimizely. createUserContext ( userId: userId)
540
+ _ = user. decide ( key: featureKey)
541
+
542
+
543
+ // Add a delay before evaluating getFirstEventJSON
544
+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.1 ) {
545
+ exp. fulfill ( ) // Fulfill the expectation after the delay
546
+ }
547
+
548
+ let result = XCTWaiter . wait ( for: [ exp] , timeout: 0.2 )
549
+ if result == XCTWaiter . Result. completed {
550
+ let event = getFirstEventJSON ( client: optimizely) !
551
+ let visitor = ( event [ " visitors " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
552
+ let snapshot = ( visitor [ " snapshots " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
553
+ let decision = ( snapshot [ " decisions " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
554
+
555
+ let metaData = decision [ " metadata " ] as! Dictionary < String , Any >
556
+ XCTAssertEqual ( metaData [ " rule_type " ] as! String , Constants . DecisionSource. holdout. rawValue)
557
+ XCTAssertEqual ( metaData [ " rule_key " ] as! String , " holdout_key " )
558
+ XCTAssertEqual ( metaData [ " flag_key " ] as! String , " feature_1 " )
559
+ XCTAssertEqual ( metaData [ " variation_key " ] as! String , " holdout_a " )
560
+ XCTAssertFalse ( metaData [ " enabled " ] as! Bool )
561
+ } else {
562
+ XCTFail ( " No event found " )
563
+ }
564
+ optimizely = nil
565
+
566
+ }
567
+
568
+ func testImpressionEvent_UserNotInHoldout_ExcludedFlags( ) {
569
+ let eventDispatcher2 = MockEventDispatcher ( )
570
+ var optimizely : OptimizelyClient ! = OptimizelyClient ( sdkKey: " 123456 " , eventDispatcher: eventDispatcher2)
571
+
572
+ try ! optimizely. start ( datafile: datafile)
573
+
574
+ var holdout : Holdout = try ! OTUtils . model ( from: sampleHoldout)
575
+ holdout. excludedFlags = [ " 4482920077 " ]
576
+ optimizely. config? . project. holdouts = [ holdout]
577
+
578
+ let exp = expectation ( description: " Wait for event to dispatch " )
579
+
580
+ let user = optimizely. createUserContext ( userId: userId)
581
+ _ = user. decide ( key: featureKey)
582
+
583
+ // Add a delay before evaluating getFirstEventJSON
584
+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.1 ) {
585
+ exp. fulfill ( ) // Fulfill the expectation after the delay
586
+ }
587
+
588
+ let result = XCTWaiter . wait ( for: [ exp] , timeout: 0.2 )
589
+ if result == XCTWaiter . Result. completed {
590
+ let event = getFirstEventJSON ( client: optimizely) !
591
+ let visitor = ( event [ " visitors " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
592
+ let snapshot = ( visitor [ " snapshots " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
593
+ let decision = ( snapshot [ " decisions " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
594
+
595
+ let metaData = decision [ " metadata " ] as! Dictionary < String , Any >
596
+ XCTAssertEqual ( metaData [ " rule_type " ] as! String , Constants . DecisionSource. featureTest. rawValue)
597
+ XCTAssertEqual ( metaData [ " rule_key " ] as! String , " exp_with_audience " )
598
+ XCTAssertEqual ( metaData [ " flag_key " ] as! String , " feature_1 " )
599
+ XCTAssertEqual ( metaData [ " variation_key " ] as! String , " a " )
600
+ XCTAssertTrue ( metaData [ " enabled " ] as! Bool )
601
+ } else {
602
+ XCTFail ( " No event found " )
603
+ }
604
+ }
605
+
606
+ func testImpressionEvent_UserNotInHoldout_MissesTrafficAllocation( ) {
607
+ let eventDispatcher2 = MockEventDispatcher ( )
608
+ var optimizely : OptimizelyClient ! = OptimizelyClient ( sdkKey: " 123457 " , eventDispatcher: eventDispatcher2)
609
+
610
+ try ! optimizely. start ( datafile: datafile)
611
+
612
+ var holdout : Holdout = try ! OTUtils . model ( from: sampleHoldout)
613
+ /// Set traffic allocation to gero
614
+ holdout. trafficAllocation [ 0 ] . endOfRange = 0
615
+ holdout. includedFlags = [ " 4482920077 " ]
616
+ optimizely. config? . project. holdouts = [ holdout]
617
+
618
+ let exp = expectation ( description: " Wait for event to dispatch " )
619
+
620
+ let user = optimizely. createUserContext ( userId: userId)
621
+ _ = user. decide ( key: featureKey)
622
+
623
+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.1 ) {
624
+ exp. fulfill ( ) // Fulfill the expectation after the delay
625
+ }
626
+
627
+ let result = XCTWaiter . wait ( for: [ exp] , timeout: 0.2 )
628
+ if result == XCTWaiter . Result. completed {
629
+ let event = getFirstEventJSON ( client: optimizely) !
630
+ let visitor = ( event [ " visitors " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
631
+ let snapshot = ( visitor [ " snapshots " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
632
+ let decision = ( snapshot [ " decisions " ] as! Array < Dictionary < String , Any > > ) [ 0 ]
633
+
634
+ let metaData = decision [ " metadata " ] as! Dictionary < String , Any >
635
+ XCTAssertEqual ( metaData [ " rule_type " ] as! String , Constants . DecisionSource. featureTest. rawValue)
636
+ XCTAssertEqual ( metaData [ " rule_key " ] as! String , " exp_with_audience " )
637
+ XCTAssertEqual ( metaData [ " flag_key " ] as! String , " feature_1 " )
638
+ XCTAssertEqual ( metaData [ " variation_key " ] as! String , " a " )
639
+ XCTAssertTrue ( metaData [ " enabled " ] as! Bool )
640
+ } else {
641
+ XCTFail ( " No event found " )
642
+ }
643
+ }
644
+ }
645
+
464
646
// MARK: - Utils
465
647
466
648
extension BatchEventBuilderTests_Events {
@@ -477,7 +659,14 @@ extension BatchEventBuilderTests_Events {
477
659
return json
478
660
}
479
661
480
- func getEventJSON( data: Data ) -> [ String : Any ] ? {
662
+ func getFirstEventJSON( client: OptimizelyClient ) -> [ String : Any ] ? {
663
+ guard let event = getFirstEvent ( dispatcher: client. eventDispatcher as! MockEventDispatcher ) else { return nil }
664
+
665
+ let json = try ! JSONSerialization . jsonObject ( with: event. body, options: . allowFragments) as! [ String : Any ]
666
+ return json
667
+ }
668
+
669
+ func getEventJSON( data: Data ) -> [ String : Any ] ? {
481
670
let json = try ! JSONSerialization . jsonObject ( with: data, options: . allowFragments) as! [ String : Any ]
482
671
return json
483
672
}
0 commit comments