@@ -462,3 +462,198 @@ class DefaultCmabServiceTests: XCTestCase {
462
462
}
463
463
}
464
464
465
+ extension DefaultCmabServiceTests {
466
+ func testSyncFetchDecision( ) {
467
+ cmabClient. fetchDecisionResult = . success( " variation-123 " )
468
+
469
+ let result = cmabService. getDecision (
470
+ config: config,
471
+ userContext: userContext,
472
+ ruleId: " exp-123 " ,
473
+ options: [ ]
474
+ )
475
+
476
+ switch result {
477
+ case . success( let decision) :
478
+ XCTAssertEqual ( decision. variationId, " variation-123 " )
479
+ XCTAssertEqual ( self . cmabClient. lastRuleId, " exp-123 " )
480
+ XCTAssertEqual ( self . cmabClient. lastUserId, " test-user " )
481
+ XCTAssertEqual ( self . cmabClient. lastAttributes? . count, 2 )
482
+ XCTAssertEqual ( self . cmabClient. lastAttributes ? [ " age " ] as? Int , 25 )
483
+ XCTAssertEqual ( self . cmabClient. lastAttributes ? [ " location " ] as? String , " San Francisco " )
484
+
485
+ // Verify it was cached
486
+ let cacheKey = " 9-test-user-exp-123 "
487
+ XCTAssertNotNil ( self . cmabCache. lookup ( key: cacheKey) )
488
+
489
+ case . failure( let error) :
490
+ XCTFail ( " Expected success but got error: \( error) " )
491
+ }
492
+ }
493
+
494
+ func testSyncCachedDecision( ) {
495
+ // First, put something in the cache
496
+ let attributesHash = cmabService. hashAttributes ( [ " age " : 25 , " location " : " San Francisco " ] )
497
+ let cacheKey = " 9-test-user-exp-123 "
498
+ let cacheValue = CmabCacheValue (
499
+ attributesHash: attributesHash,
500
+ variationId: " cached-variation " ,
501
+ cmabUUID: " cached-uuid "
502
+ )
503
+ cmabCache. save ( key: cacheKey, value: cacheValue)
504
+
505
+ let result = cmabService. getDecision (
506
+ config: config,
507
+ userContext: userContext,
508
+ ruleId: " exp-123 " ,
509
+ options: [ ]
510
+ )
511
+
512
+ switch result {
513
+ case . success( let decision) :
514
+ XCTAssertEqual ( decision. variationId, " cached-variation " )
515
+ XCTAssertEqual ( decision. cmabUUID, " cached-uuid " )
516
+ XCTAssertFalse ( self . cmabClient. fetchDecisionCalled, " Should not call API when cache hit " )
517
+
518
+ case . failure( let error) :
519
+ XCTFail ( " Expected success but got error: \( error) " )
520
+ }
521
+ }
522
+
523
+ func testSyncFailedFetch( ) {
524
+ let testError = CmabClientError . fetchFailed ( " Test error " )
525
+ cmabClient. fetchDecisionResult = . failure( testError)
526
+
527
+ let result = cmabService. getDecision (
528
+ config: config,
529
+ userContext: userContext,
530
+ ruleId: " exp-123 " ,
531
+ options: [ ]
532
+ )
533
+
534
+ switch result {
535
+ case . success:
536
+ XCTFail ( " Expected failure but got success " )
537
+
538
+ case . failure( let error) :
539
+ XCTAssertEqual ( ( error as? CmabClientError ) ? . message, " Test error " )
540
+
541
+ // Verify no caching of failed results
542
+ let cacheKey = " 9-test-user-exp-123 "
543
+ XCTAssertNil ( self . cmabCache. lookup ( key: cacheKey) )
544
+ }
545
+ }
546
+
547
+ func testSyncIgnoreCmabCacheOption( ) {
548
+ // First, put something in the cache
549
+ let attributesHash = cmabService. hashAttributes ( [ " age " : 25 , " location " : " San Francisco " ] )
550
+ let cacheKey = " 9-test-user-exp-123 "
551
+ let cacheValue = CmabCacheValue (
552
+ attributesHash: attributesHash,
553
+ variationId: " cached-variation " ,
554
+ cmabUUID: " cached-uuid "
555
+ )
556
+ cmabCache. save ( key: cacheKey, value: cacheValue)
557
+
558
+ cmabClient. fetchDecisionResult = . success( " new-variation " )
559
+
560
+ let result = cmabService. getDecision (
561
+ config: config,
562
+ userContext: userContext,
563
+ ruleId: " exp-123 " ,
564
+ options: [ . ignoreCmabCache]
565
+ )
566
+
567
+ switch result {
568
+ case . success( let decision) :
569
+ XCTAssertEqual ( decision. variationId, " new-variation " )
570
+ XCTAssertTrue ( self . cmabClient. fetchDecisionCalled, " Should always call API when ignoreCmabCache option is set " )
571
+
572
+ case . failure( let error) :
573
+ XCTFail ( " Expected success but got error: \( error) " )
574
+ }
575
+ }
576
+
577
+ func testSyncResetCmabCacheOption( ) {
578
+ // First, put something in the cache
579
+ let attributesHash = cmabService. hashAttributes ( [ " age " : 25 , " location " : " San Francisco " ] )
580
+ let cacheKey = " 9-test-user-exp-123 "
581
+ let cacheValue = CmabCacheValue (
582
+ attributesHash: attributesHash,
583
+ variationId: " cached-variation " ,
584
+ cmabUUID: " cached-uuid "
585
+ )
586
+ cmabCache. save ( key: cacheKey, value: cacheValue)
587
+
588
+ // Also add another item to verify it's cleared
589
+ let otherCacheKey = " other-key "
590
+ cmabCache. save ( key: otherCacheKey, value: cacheValue)
591
+
592
+ cmabClient. fetchDecisionResult = . success( " new-variation " )
593
+
594
+ let result = cmabService. getDecision (
595
+ config: config,
596
+ userContext: userContext,
597
+ ruleId: " exp-123 " ,
598
+ options: [ . resetCmabCache]
599
+ )
600
+
601
+ switch result {
602
+ case . success( let decision) :
603
+ XCTAssertEqual ( decision. variationId, " new-variation " )
604
+ XCTAssertTrue ( self . cmabClient. fetchDecisionCalled, " Should call API after resetting cache " )
605
+
606
+ // Verify the entire cache was reset
607
+ XCTAssertNil ( self . cmabCache. lookup ( key: otherCacheKey) )
608
+
609
+ // But the new decision should be cached
610
+ XCTAssertNotNil ( self . cmabCache. lookup ( key: cacheKey) )
611
+
612
+ case . failure( let error) :
613
+ XCTFail ( " Expected success but got error: \( error) " )
614
+ }
615
+ }
616
+
617
+ func testSyncInvalidateUserCmabCacheOption( ) {
618
+ // First, put something in the cache
619
+ let attributesHash = cmabService. hashAttributes ( [ " age " : 25 , " location " : " San Francisco " ] )
620
+ let userCacheKey = " 9-test-user-exp-123 "
621
+ let otherUserCacheKey = " other-user-key "
622
+
623
+ let cacheValue = CmabCacheValue (
624
+ attributesHash: attributesHash,
625
+ variationId: " cached-variation " ,
626
+ cmabUUID: " cached-uuid "
627
+ )
628
+
629
+ // Cache for both current user and another user
630
+ cmabCache. save ( key: userCacheKey, value: cacheValue)
631
+ cmabCache. save ( key: otherUserCacheKey, value: cacheValue)
632
+
633
+ cmabClient. fetchDecisionResult = . success( " new-variation " )
634
+
635
+ let result = cmabService. getDecision (
636
+ config: config,
637
+ userContext: userContext,
638
+ ruleId: " exp-123 " ,
639
+ options: [ . invalidateUserCmabCache]
640
+ )
641
+
642
+ switch result {
643
+ case . success( let decision) :
644
+ XCTAssertEqual ( decision. variationId, " new-variation " )
645
+ XCTAssertTrue ( self . cmabClient. fetchDecisionCalled, " Should call API after invalidating user cache " )
646
+
647
+ // Verify only the specific user's cache was invalidated
648
+ XCTAssertNotNil ( self . cmabCache. lookup ( key: otherUserCacheKey) , " Other users' cache should remain intact " )
649
+
650
+ // The new decision should be cached for the current user
651
+ XCTAssertNotNil ( self . cmabCache. lookup ( key: userCacheKey) )
652
+ XCTAssertEqual ( self . cmabCache. lookup ( key: userCacheKey) ? . variationId, " new-variation " )
653
+
654
+ case . failure( let error) :
655
+ XCTFail ( " Expected success but got error: \( error) " )
656
+ }
657
+ }
658
+
659
+ }
0 commit comments