From 62ae49b93d743d7674030e95ea489066f1b40351 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam <129880873+muzahidul-opti@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:02:06 +0600 Subject: [PATCH] [FSSDK-10761] feat: make vuid as opt-in (#556) VUID optln added --- OptimizelySwiftSDK.xcodeproj/project.pbxproj | 78 ++++++++--------- Sources/ODP/OdpEventManager.swift | 9 +- Sources/ODP/OdpManager.swift | 32 +++---- Sources/ODP/OptimizelySdkSettings.swift | 5 ++ .../OptimizelyClient+Decide.swift | 6 +- .../OptimizelyUserContext.swift | 1 - Sources/Optimizely/OptimizelyClient.swift | 23 ++++- .../VuidManager.swift} | 42 +++++++--- .../OptimizelyClientTests_Decide.swift | 14 ++-- .../OptimizelyClientTests_ODP.swift | 13 ++- .../OdpEventManagerTests.swift | 8 +- .../OdpManagerTests.swift | 53 +++++++----- .../OdpVuidManagerTests.swift | 55 ------------ ...izelyUserContextTests_Decide_Reasons.swift | 83 +++++++++---------- .../OptimizelyUserContextTests_ODP_2.swift | 29 +++++-- .../VuidManagerTests.swift | 78 +++++++++++++++++ 16 files changed, 310 insertions(+), 219 deletions(-) rename Sources/{ODP/OdpVuidManager.swift => Optimizely/VuidManager.swift} (68%) delete mode 100644 Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift create mode 100644 Tests/OptimizelyTests-Common/VuidManagerTests.swift diff --git a/OptimizelySwiftSDK.xcodeproj/project.pbxproj b/OptimizelySwiftSDK.xcodeproj/project.pbxproj index af0854c49..3df0de831 100644 --- a/OptimizelySwiftSDK.xcodeproj/project.pbxproj +++ b/OptimizelySwiftSDK.xcodeproj/project.pbxproj @@ -1884,8 +1884,8 @@ 84861812286D0B8900B7F41B /* OdpSegmentManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180D286D0B8900B7F41B /* OdpSegmentManagerTests.swift */; }; 84861813286D0B8900B7F41B /* OdpManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */; }; 84861814286D0B8900B7F41B /* OdpManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */; }; - 84861815286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */; }; - 84861816286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */; }; + 84861815286D0B8900B7F41B /* VuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */; }; + 84861816286D0B8900B7F41B /* VuidManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */; }; 84861817286D0B8900B7F41B /* OdpEventManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */; }; 84861818286D0B8900B7F41B /* OdpEventManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */; }; 8486181B286D188B00B7F41B /* OdpSegmentApiManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84861819286D188B00B7F41B /* OdpSegmentApiManagerTests.swift */; }; @@ -1910,22 +1910,22 @@ 84B4D75D27E2A7550078CDA4 /* OptimizelySegmentOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */; }; 84B4D75E27E2A7550078CDA4 /* OptimizelySegmentOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */; }; 84B4D75F27E2A7550078CDA4 /* OptimizelySegmentOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */; }; - 84E2E9422852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9432852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9442852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9452852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9462852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9472852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9482852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9492852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94A2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94B2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94C2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94D2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94E2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E94F2852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9502852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; - 84E2E9512852A378001114AB /* OdpVuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* OdpVuidManager.swift */; }; + 84E2E9422852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9432852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9442852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9452852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9462852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9472852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9482852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9492852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94A2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94B2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94C2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94D2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94E2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E94F2852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9502852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; + 84E2E9512852A378001114AB /* VuidManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E9412852A378001114AB /* VuidManager.swift */; }; 84E2E96128540B5E001114AB /* OptimizelySdkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */; }; 84E2E96228540B5E001114AB /* OptimizelySdkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */; }; 84E2E96328540B5E001114AB /* OptimizelySdkSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */; }; @@ -2424,13 +2424,13 @@ 848617FA286CF33700B7F41B /* OdpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpEvent.swift; sourceTree = ""; }; 8486180D286D0B8900B7F41B /* OdpSegmentManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpSegmentManagerTests.swift; sourceTree = ""; }; 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpManagerTests.swift; sourceTree = ""; }; - 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpVuidManagerTests.swift; sourceTree = ""; }; + 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VuidManagerTests.swift; sourceTree = ""; }; 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpEventManagerTests.swift; sourceTree = ""; }; 84861819286D188B00B7F41B /* OdpSegmentApiManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpSegmentApiManagerTests.swift; sourceTree = ""; }; 8486181A286D188B00B7F41B /* OdpEventApiManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OdpEventApiManagerTests.swift; sourceTree = ""; }; 84958C5D280F22FE008655C7 /* OptimizelyUserContextTests_Performance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyUserContextTests_Performance.swift; sourceTree = ""; }; 84B4D74F27E2A7550078CDA4 /* OptimizelySegmentOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptimizelySegmentOption.swift; sourceTree = ""; }; - 84E2E9412852A378001114AB /* OdpVuidManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OdpVuidManager.swift; sourceTree = ""; }; + 84E2E9412852A378001114AB /* VuidManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VuidManager.swift; sourceTree = ""; }; 84E2E96028540B5E001114AB /* OptimizelySdkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = OptimizelySdkSettings.swift; path = ../ODP/OptimizelySdkSettings.swift; sourceTree = ""; }; 84E2E9712855875E001114AB /* OdpEventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OdpEventManager.swift; sourceTree = ""; }; 84E7ABBA27D2A1F100447CAE /* ThreadSafeLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeLogger.swift; sourceTree = ""; }; @@ -2652,7 +2652,6 @@ isa = PBXGroup; children = ( 6E6522DD278E4F3800954EA1 /* OdpManager.swift */, - 84E2E9412852A378001114AB /* OdpVuidManager.swift */, 848617C72863DC2700B7F41B /* OdpSegmentManager.swift */, 84E2E9712855875E001114AB /* OdpEventManager.swift */, 848617D82863E21200B7F41B /* OdpSegmentApiManager.swift */, @@ -2727,6 +2726,7 @@ 6EA2CC232345618E001E7531 /* OptimizelyConfig.swift */, 6ECB60C9234D5D9C00016D41 /* OptimizelyConfig+ObjC.swift */, C78CAF572445AD8D009FE876 /* OptimizelyJSON.swift */, + 84E2E9412852A378001114AB /* VuidManager.swift */, C78CAFA324486E0A009FE876 /* OptimizelyJSON+ObjC.swift */, ); path = Optimizely; @@ -2964,7 +2964,7 @@ 84861810286D0B8900B7F41B /* OdpEventManagerTests.swift */, 8486180E286D0B8900B7F41B /* OdpManagerTests.swift */, 8486180D286D0B8900B7F41B /* OdpSegmentManagerTests.swift */, - 8486180F286D0B8900B7F41B /* OdpVuidManagerTests.swift */, + 8486180F286D0B8900B7F41B /* VuidManagerTests.swift */, 84861819286D188B00B7F41B /* OdpSegmentApiManagerTests.swift */, 8486181A286D188B00B7F41B /* OdpEventApiManagerTests.swift */, 6E27EC9A266EF11000B4A6D4 /* OptimizelyDecisionTests.swift */, @@ -4197,7 +4197,7 @@ 6E14CDA92423F9C300010234 /* Utils.swift in Sources */, 6EF8DE1F24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 6E14CD882423F9A100010234 /* AttributeValue.swift in Sources */, - 84E2E9492852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9492852A378001114AB /* VuidManager.swift in Sources */, 6E14CD822423F9A100010234 /* DataStoreFile.swift in Sources */, 6E14CDA42423F9C300010234 /* Notifications.swift in Sources */, 6E20050B26B4D28500278087 /* MockLogger.swift in Sources */, @@ -4270,7 +4270,7 @@ 6E424D0126324B620081004A /* SemanticVersion.swift in Sources */, 6E424D0226324B620081004A /* Audience.swift in Sources */, 6E424D0326324B620081004A /* AttributeValue.swift in Sources */, - 84E2E9482852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9482852A378001114AB /* VuidManager.swift in Sources */, 6E424D0426324B620081004A /* ConditionLeaf.swift in Sources */, 6E424D0526324B620081004A /* ConditionHolder.swift in Sources */, 6E424D5226324C4D0081004A /* OptimizelyClient+Decide.swift in Sources */, @@ -4380,7 +4380,7 @@ 6E75190122C520D500B2B157 /* Attribute.swift in Sources */, 848617DB2863E21200B7F41B /* OdpSegmentApiManager.swift in Sources */, 6E7516B322C520D400B2B157 /* DefaultUserProfileService.swift in Sources */, - 84E2E9432852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9432852A378001114AB /* VuidManager.swift in Sources */, 6E75183522C520D400B2B157 /* EventForDispatch.swift in Sources */, 6E7517D522C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, 6E75187122C520D400B2B157 /* Variation.swift in Sources */, @@ -4504,7 +4504,7 @@ 6E7518F022C520D500B2B157 /* ConditionHolder.swift in Sources */, 6EF8DE2424BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 6E75183022C520D400B2B157 /* BatchEvent.swift in Sources */, - 84E2E94E2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94E2852A378001114AB /* VuidManager.swift in Sources */, 6E75192022C520D500B2B157 /* OPTNotificationCenter.swift in Sources */, 6E9B117922C5487A00C22D81 /* tvOSOnlyTests.swift in Sources */, 6E20051026B4D28500278087 /* MockLogger.swift in Sources */, @@ -4577,7 +4577,7 @@ 6ECB60D7234E601A00016D41 /* OptimizelyClientTests_OptimizelyConfig_Objc.m in Sources */, 6E75195822C520D500B2B157 /* OPTBucketer.swift in Sources */, 6E424C03263228FD0081004A /* AtomicDictionary.swift in Sources */, - 84E2E94A2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94A2852A378001114AB /* VuidManager.swift in Sources */, 6E994B3A25A3E6EA00999262 /* DecisionResponse.swift in Sources */, 6E75170A22C520D400B2B157 /* OptimizelyClient.swift in Sources */, 6E9B11AC22C5489300C22D81 /* OTUtils.swift in Sources */, @@ -4701,7 +4701,7 @@ 6E7518BF22C520D400B2B157 /* Variable.swift in Sources */, 6E75181722C520D400B2B157 /* DataStoreQueueStackImpl.swift in Sources */, 6E75185322C520D400B2B157 /* ProjectConfig.swift in Sources */, - 84E2E94D2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94D2852A378001114AB /* VuidManager.swift in Sources */, 6E75173D22C520D400B2B157 /* MurmurHash3.swift in Sources */, 6E7516E922C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E7518A722C520D400B2B157 /* FeatureFlag.swift in Sources */, @@ -4809,7 +4809,7 @@ 6E9B11B522C5489600C22D81 /* MockUrlSession.swift in Sources */, 6EF8DE1724BD1BB2008B9488 /* OptimizelyDecision.swift in Sources */, 8428D3D12807337400D0FB0C /* LruCacheTests.swift in Sources */, - 84861816286D0B8900B7F41B /* OdpVuidManagerTests.swift in Sources */, + 84861816286D0B8900B7F41B /* VuidManagerTests.swift in Sources */, 6E7517C522C520D400B2B157 /* DefaultDatafileHandler.swift in Sources */, 6E75190922C520D500B2B157 /* Attribute.swift in Sources */, 6E75177B22C520D400B2B157 /* SDKVersion.swift in Sources */, @@ -4855,7 +4855,7 @@ 6E9B117822C5487100C22D81 /* DataStoreTests.swift in Sources */, 6E75171B22C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E75195122C520D500B2B157 /* OPTDatafileHandler.swift in Sources */, - 84E2E94F2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94F2852A378001114AB /* VuidManager.swift in Sources */, 6E75176322C520D400B2B157 /* AtomicProperty.swift in Sources */, 6E9B117722C5487100C22D81 /* BatchEventBuilderTests_EventTags.swift in Sources */, 6E7517DD22C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, @@ -4936,7 +4936,7 @@ 84861809286CF33700B7F41B /* OdpEvent.swift in Sources */, 6E75171022C520D400B2B157 /* OptimizelyClient.swift in Sources */, 6E7516C822C520D400B2B157 /* DefaultEventDispatcher.swift in Sources */, - 84E2E9502852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9502852A378001114AB /* VuidManager.swift in Sources */, 6E75194622C520D500B2B157 /* OPTDecisionService.swift in Sources */, 6E75185622C520D400B2B157 /* ProjectConfig.swift in Sources */, 6E20051226B4D28600278087 /* MockLogger.swift in Sources */, @@ -5104,7 +5104,7 @@ 6E7517D722C520D400B2B157 /* DefaultNotificationCenter.swift in Sources */, 6E75181F22C520D400B2B157 /* BatchEventBuilder.swift in Sources */, 84F6BADD27FD011B004BE62A /* OptimizelyUserContextTests_ODP_Decide.swift in Sources */, - 84E2E9472852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9472852A378001114AB /* VuidManager.swift in Sources */, 6E623F06253F9045000617D0 /* DecisionInfo.swift in Sources */, 6E424BE2263228E90081004A /* AtomicArray.swift in Sources */, 6E9B115822C5486E00C22D81 /* EventDispatcherTests.swift in Sources */, @@ -5205,7 +5205,7 @@ 84861804286CF33700B7F41B /* OdpEvent.swift in Sources */, 6E75170B22C520D400B2B157 /* OptimizelyClient.swift in Sources */, 6E7516C322C520D400B2B157 /* DefaultEventDispatcher.swift in Sources */, - 84E2E94B2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94B2852A378001114AB /* VuidManager.swift in Sources */, 6E75194122C520D500B2B157 /* OPTDecisionService.swift in Sources */, 6E75185122C520D400B2B157 /* ProjectConfig.swift in Sources */, 6E20050D26B4D28500278087 /* MockLogger.swift in Sources */, @@ -5338,7 +5338,7 @@ 984FE51F2CC8AA88004F6F41 /* UserProfileTracker.swift in Sources */, 6E75171822C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E75174822C520D400B2B157 /* HandlerRegistryService.swift in Sources */, - 84E2E94C2852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E94C2852A378001114AB /* VuidManager.swift in Sources */, 6E7518FA22C520D500B2B157 /* UserAttribute.swift in Sources */, 6E7516E822C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E75191222C520D500B2B157 /* BackgroundingCallbacks.swift in Sources */, @@ -5440,7 +5440,7 @@ 984FE51D2CC8AA88004F6F41 /* UserProfileTracker.swift in Sources */, 6E75171D22C520D400B2B157 /* OptimizelyClient+ObjC.swift in Sources */, 6E75174D22C520D400B2B157 /* HandlerRegistryService.swift in Sources */, - 84E2E9512852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9512852A378001114AB /* VuidManager.swift in Sources */, 6E7518FF22C520D500B2B157 /* UserAttribute.swift in Sources */, 6E7516ED22C520D400B2B157 /* OPTEventDispatcher.swift in Sources */, 6E75191722C520D500B2B157 /* BackgroundingCallbacks.swift in Sources */, @@ -5540,7 +5540,7 @@ 6E75195422C520D500B2B157 /* OPTBucketer.swift in Sources */, 848617DA2863E21200B7F41B /* OdpSegmentApiManager.swift in Sources */, 6E75171E22C520D400B2B157 /* OptimizelyResult.swift in Sources */, - 84E2E9422852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9422852A378001114AB /* VuidManager.swift in Sources */, 6E75172A22C520D400B2B157 /* Constants.swift in Sources */, 6E7516A622C520D400B2B157 /* DefaultLogger.swift in Sources */, 6E75189422C520D400B2B157 /* Experiment.swift in Sources */, @@ -5664,7 +5664,7 @@ 6E7518DE22C520D400B2B157 /* ConditionLeaf.swift in Sources */, 6EF8DE1D24BD1BB2008B9488 /* OptimizelyDecideOption.swift in Sources */, 6E7518EA22C520D400B2B157 /* ConditionHolder.swift in Sources */, - 84E2E9462852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9462852A378001114AB /* VuidManager.swift in Sources */, 6E75182A22C520D400B2B157 /* BatchEvent.swift in Sources */, 6E75191A22C520D500B2B157 /* OPTNotificationCenter.swift in Sources */, 6E20050826B4D28500278087 /* MockLogger.swift in Sources */, @@ -5749,7 +5749,7 @@ 75C71A2125E454460084187E /* EventForDispatch.swift in Sources */, 75C71A2225E454460084187E /* SemanticVersion.swift in Sources */, 75C71A2325E454460084187E /* Audience.swift in Sources */, - 84E2E9452852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9452852A378001114AB /* VuidManager.swift in Sources */, 6E6522E1278E4F3800954EA1 /* OdpManager.swift in Sources */, 75C71A2425E454460084187E /* AttributeValue.swift in Sources */, 75C71A2525E454460084187E /* ConditionLeaf.swift in Sources */, @@ -5832,7 +5832,7 @@ BD6485502491474500F30986 /* OPTBucketer.swift in Sources */, 848617DC2863E21200B7F41B /* OdpSegmentApiManager.swift in Sources */, BD6485512491474500F30986 /* OptimizelyResult.swift in Sources */, - 84E2E9442852A378001114AB /* OdpVuidManager.swift in Sources */, + 84E2E9442852A378001114AB /* VuidManager.swift in Sources */, BD6485522491474500F30986 /* Constants.swift in Sources */, BD6485532491474500F30986 /* DefaultLogger.swift in Sources */, BD6485542491474500F30986 /* Experiment.swift in Sources */, diff --git a/Sources/ODP/OdpEventManager.swift b/Sources/ODP/OdpEventManager.swift index 9ddec34a2..9f22323d7 100644 --- a/Sources/ODP/OdpEventManager.swift +++ b/Sources/ODP/OdpEventManager.swift @@ -49,7 +49,7 @@ open class OdpEventManager { // MARK: - events - func registerVUID(vuid: String) { + func sendInitializedEvent(vuid: String) { sendEvent(type: Constants.ODP.eventType, action: "client_initialized", identifiers: [ @@ -58,8 +58,11 @@ open class OdpEventManager { data: [:]) } - func identifyUser(vuid: String, userId: String?) { - var identifiers = [Constants.ODP.keyForVuid: vuid] + func identifyUser(vuid: String?, userId: String?) { + var identifiers = [String: String]() + if let _vuid = vuid { + identifiers[Constants.ODP.keyForVuid] = _vuid + } if let userId = userId { identifiers[Constants.ODP.keyForUserId] = userId } diff --git a/Sources/ODP/OdpManager.swift b/Sources/ODP/OdpManager.swift index 8553fdc10..40ba6ad31 100644 --- a/Sources/ODP/OdpManager.swift +++ b/Sources/ODP/OdpManager.swift @@ -18,17 +18,13 @@ import Foundation public class OdpManager { var enabled: Bool - var vuidManager: OdpVuidManager - var odpConfig: OdpConfig! var segmentManager: OdpSegmentManager! var eventManager: OdpEventManager! let logger = OPTLoggerFactory.getLogger() - var vuid: String { - return vuidManager.vuid - } + var vuid: String? /// OdpManager init /// - Parameters: @@ -42,6 +38,7 @@ public class OdpManager { /// - eventManager: ODPEventManager public init(sdkKey: String, disable: Bool, + vuid: String? = nil, cacheSize: Int, cacheTimeoutInSecs: Int, timeoutForSegmentFetchInSecs: Int? = nil, @@ -50,8 +47,7 @@ public class OdpManager { eventManager: OdpEventManager? = nil) { self.enabled = !disable - self.vuidManager = OdpVuidManager.shared - + self.vuid = vuid guard enabled else { logger.i(.odpNotEnabled) return @@ -65,8 +61,6 @@ public class OdpManager { self.odpConfig = OdpConfig() self.segmentManager.odpConfig = odpConfig self.eventManager.odpConfig = odpConfig - - self.eventManager.registerVUID(vuid: self.vuidManager.vuid) } func fetchQualifiedSegments(userId: String, @@ -77,7 +71,7 @@ public class OdpManager { return } - let userKey = OdpVuidManager.isVuid(userId) ? Constants.ODP.keyForVuid : Constants.ODP.keyForUserId + let userKey = VuidManager.isVuid(userId) ? Constants.ODP.keyForVuid : Constants.ODP.keyForUserId let userValue = userId segmentManager.fetchQualifiedSegments(userKey: userKey, @@ -97,15 +91,13 @@ public class OdpManager { return } - var vuid = vuidManager.vuid - var fsUserId: String? = userId - if OdpVuidManager.isVuid(userId) { + if VuidManager.isVuid(userId) { // overwrite if userId is vuid (when userContext is created with vuid) - vuid = userId - fsUserId = nil + eventManager.identifyUser(vuid: userId, userId: nil) + } else { + eventManager.identifyUser(vuid: self.vuid, userId: userId) } - eventManager.identifyUser(vuid: vuid, userId: fsUserId) } /// Send an event to the ODP server. @@ -125,15 +117,13 @@ public class OdpManager { let typeUpdated = (type ?? "").isEmpty ? Constants.ODP.eventType : type! - // add vuid to all events by default - var identifiersUpdated = identifiers - if identifiers[Constants.ODP.keyForVuid] == nil { - identifiersUpdated[Constants.ODP.keyForVuid] = vuidManager.vuid + + if identifiers[Constants.ODP.keyForVuid] == nil, let _vuid = vuid { + identifiersUpdated[Constants.ODP.keyForVuid] = _vuid } // replace aliases (fs-user-id, FS_USER_ID, FS-USER-ID) with "fs_user_id". - for (idKey, idValue) in identifiersUpdated { if idKey == Constants.ODP.keyForUserId { break } diff --git a/Sources/ODP/OptimizelySdkSettings.swift b/Sources/ODP/OptimizelySdkSettings.swift index 6a1d71225..fc3d47139 100644 --- a/Sources/ODP/OptimizelySdkSettings.swift +++ b/Sources/ODP/OptimizelySdkSettings.swift @@ -27,6 +27,8 @@ public struct OptimizelySdkSettings { let timeoutForOdpEventInSecs: Int /// ODP features are disabled if this is set to true. let disableOdp: Bool + /// VUID is enabled if this is set to true. + let enableVuid: Bool /// Optimizely SDK Settings /// @@ -36,6 +38,7 @@ public struct OptimizelySdkSettings { /// - timeoutForSegmentFetchInSecs: The timeout in seconds of odp segment fetch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - timeoutForOdpEventInSecs: The timeout in seconds of odp event dispatch (optional. default = 10) - OS default timeout will be used if this is set to zero. /// - disableOdp: Set this flag to true (default = false) to disable ODP features + /// - enableVuid: Set this flag to true (default = false) to enable vuid. /// - sdkName: Set this flag to override sdkName included in events /// - sdkVersion: Set this flag to override sdkVersion included in events public init(segmentsCacheSize: Int = 100, @@ -43,6 +46,7 @@ public struct OptimizelySdkSettings { timeoutForSegmentFetchInSecs: Int = 10, timeoutForOdpEventInSecs: Int = 10, disableOdp: Bool = false, + enableVuid: Bool = false, sdkName: String? = nil, sdkVersion: String? = nil) { self.segmentsCacheSize = segmentsCacheSize @@ -50,6 +54,7 @@ public struct OptimizelySdkSettings { self.timeoutForSegmentFetchInSecs = timeoutForSegmentFetchInSecs self.timeoutForOdpEventInSecs = timeoutForOdpEventInSecs self.disableOdp = disableOdp + self.enableVuid = enableVuid if let _sdkName = sdkName, _sdkName != "" { Utils.swiftSdkClientName = _sdkName } diff --git a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift index be4732a6e..c49d8e405 100644 --- a/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift +++ b/Sources/Optimizely+Decide/OptimizelyClient+Decide.swift @@ -35,7 +35,11 @@ extension OptimizelyClient { /// /// - Parameter attributes: A map of attribute names to current user attribute values. /// - Returns: An OptimizelyUserContext associated with this OptimizelyClient - public func createUserContext(attributes: [String: Any]? = nil) -> OptimizelyUserContext { + public func createUserContext(attributes: [String: Any]? = nil) -> OptimizelyUserContext? { + guard enableVuid, let vuid = self.vuid else { + logger.e("Vuid is not enabled or invalid VUID. User context not created.") + return nil + } return OptimizelyUserContext(optimizely: self, userId: vuid, attributes: attributes) } diff --git a/Sources/Optimizely+Decide/OptimizelyUserContext.swift b/Sources/Optimizely+Decide/OptimizelyUserContext.swift index 7f7cf48e4..e21c16d69 100644 --- a/Sources/Optimizely+Decide/OptimizelyUserContext.swift +++ b/Sources/Optimizely+Decide/OptimizelyUserContext.swift @@ -84,7 +84,6 @@ public class OptimizelyUserContext { self.atomicAttributes = AtomicProperty(property: attributes, lock: lock) self.atomicForcedDecisions = AtomicProperty(property: nil, lock: lock) self.atomicQualifiedSegments = AtomicProperty(property: nil, lock: lock) - if identify { // async call so event building overhead is not blocking context creation lock.async { diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 665bc0af6..499536117 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -60,6 +60,7 @@ open class OptimizelyClient: NSObject { var decisionService: OPTDecisionService! public var notificationCenter: OPTNotificationCenter? public var odpManager: OdpManager! + private var vuidManager: VuidManager! let sdkSettings: OptimizelySdkSettings // MARK: - Public interfaces @@ -91,13 +92,15 @@ open class OptimizelyClient: NSObject { self.defaultDecideOptions = defaultDecideOptions ?? [] super.init() - + self.vuidManager = VuidManager.shared + self.vuidManager.configure(enable: self.sdkSettings.enableVuid) self.odpManager = odpManager ?? OdpManager(sdkKey: sdkKey, disable: sdkSettings.disableOdp, cacheSize: sdkSettings.segmentsCacheSize, cacheTimeoutInSecs: sdkSettings.segmentsCacheTimeoutInSecs, timeoutForSegmentFetchInSecs: sdkSettings.timeoutForSegmentFetchInSecs, timeoutForEventDispatchInSecs: sdkSettings.timeoutForOdpEventInSecs) + self.odpManager.vuid = vuidManager.vuid let userProfileService = userProfileService ?? DefaultUserProfileService() let logger = logger ?? DefaultLogger() type(of: logger).logLevel = defaultLogLevel ?? .info @@ -115,6 +118,16 @@ open class OptimizelyClient: NSObject { self.decisionService = HandlerRegistryService.shared.injectDecisionService(sdkKey: self.sdkKey) self.notificationCenter = HandlerRegistryService.shared.injectNotificationCenter(sdkKey: self.sdkKey) + if let _vuid = self.vuidManager.vuid { + try? sendOdpEvent(type: Constants.ODP.eventType, + action: "client_initialized", + identifiers: [ + Constants.ODP.keyForVuid: _vuid + ], + data: [:]) + } + + logger.d("SDK Version: \(version)") } @@ -971,8 +984,12 @@ extension OptimizelyClient { } /// the device vuid (read only) - public var vuid: String { - return odpManager.vuid + public var vuid: String? { + return self.vuidManager.vuid + } + + public var enableVuid: Bool { + return self.vuidManager.enable } func identifyUserToOdp(userId: String) { diff --git a/Sources/ODP/OdpVuidManager.swift b/Sources/Optimizely/VuidManager.swift similarity index 68% rename from Sources/ODP/OdpVuidManager.swift rename to Sources/Optimizely/VuidManager.swift index f1112148d..6ffcb45a5 100644 --- a/Sources/ODP/OdpVuidManager.swift +++ b/Sources/Optimizely/VuidManager.swift @@ -16,15 +16,22 @@ import Foundation -class OdpVuidManager { - var vuid: String = "" +public class VuidManager { + private var _vuid: String = "" + private(set) var enable: Bool = false let logger = OPTLoggerFactory.getLogger() - + // a single vuid should be shared for all SDK instances - static let shared = OdpVuidManager() + public static let shared = VuidManager() - init() { - self.vuid = load() + public func configure(enable: Bool) { + self.enable = enable + if enable { + self._vuid = load() + } else { + self.remove() + self._vuid = "" + } } static var newVuid: String { @@ -35,16 +42,24 @@ class OdpVuidManager { let vuid = (vuidFull.count <= maxLength) ? vuidFull : String(vuidFull.prefix(maxLength)) return vuid } - + static func isVuid(_ visitorId: String) -> Bool { - return visitorId.starts(with: "vuid_") + return visitorId.lowercased().starts(with: "vuid_") } } // MARK: - VUID Store -extension OdpVuidManager { +extension VuidManager { + public var vuid: String? { + if self.enable { + return _vuid + } else { + logger.w("VUID is not enabled.") + return nil + } + } private var keyForVuid: String { return "optimizely-vuid" @@ -55,11 +70,16 @@ extension OdpVuidManager { return oldVuid } - let vuid = OdpVuidManager.newVuid + let vuid = VuidManager.newVuid save(vuid: vuid) return vuid } - + + private func remove() { + UserDefaults.standard.set(nil, forKey: keyForVuid) + UserDefaults.standard.synchronize() + } + private func save(vuid: String) { UserDefaults.standard.set(vuid, forKey: keyForVuid) UserDefaults.standard.synchronize() diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift index c67ba7b77..a5628f1df 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_Decide.swift @@ -24,7 +24,9 @@ class OptimizelyClientTests_Decide: XCTestCase { super.setUp() let datafile = OTUtils.loadJSONDatafile("api_datafile")! - optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey) + let settings = OptimizelySdkSettings(enableVuid: true) + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings) + try! optimizely.start(datafile: datafile) } @@ -54,11 +56,11 @@ class OptimizelyClientTests_Decide: XCTestCase { let user = optimizely.createUserContext(attributes: attributes) - XCTAssert(user.optimizely == optimizely) - XCTAssert(user.userId == optimizely.vuid, "vuid should be used as the default userId when not given") - XCTAssert(user.attributes["country"] as! String == "us") - XCTAssert(user.attributes["age"] as! Int == 100) - XCTAssert(user.attributes["old"] as! Bool == true) + XCTAssert(user?.optimizely == optimizely) + XCTAssert(user?.userId == optimizely.vuid, "vuid should be used as the default userId when not given") + XCTAssert(user?.attributes["country"] as! String == "us") + XCTAssert(user?.attributes["age"] as! Int == 100) + XCTAssert(user?.attributes["old"] as! Bool == true) } func testCreateUserContext_multiple() { diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift index 9151bc0d6..22600e796 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_ODP.swift @@ -187,10 +187,19 @@ class OptimizelyClientTests_ODP: XCTestCase { // MARK: - vuid - func testVuid() { - XCTAssert(optimizely.vuid.starts(with: "vuid_")) + func testVuidEnabled() { + let settings = OptimizelySdkSettings(enableVuid: true) + optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, settings: settings) + XCTAssertTrue(optimizely.enableVuid) + XCTAssert(optimizely.vuid!.starts(with: "vuid_")) } + func testVuidDiabled() { + // Default client vuid diabled + XCTAssertFalse(optimizely.enableVuid) + XCTAssertNil(optimizely.vuid) + } + // MARK: - OdpConfig Update func testUpdateOpdConfigCalled_wheneverProjectConfigUpdated_initialOrPolling() { diff --git a/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift b/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift index c6d16a873..d06562b8a 100644 --- a/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpEventManagerTests.swift @@ -89,7 +89,7 @@ class OdpEventManagerTests: XCTestCase { } func testRegisterVUID_noApiKey() { - manager.registerVUID(vuid: "v1") + manager.sendInitializedEvent(vuid: "v1") XCTAssertEqual(1, manager.eventQueue.count) @@ -149,7 +149,7 @@ class OdpEventManagerTests: XCTestCase { XCTAssertTrue(manager.odpConfig.eventQueueingAllowed, "initially datafile not ready and assumed queueing is allowed") - manager.registerVUID(vuid: "v1") // each of these will try to flush + manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush manager.identifyUser(vuid: "v1", userId: "u1") manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:]) @@ -184,7 +184,7 @@ class OdpEventManagerTests: XCTestCase { XCTAssertTrue(manager.odpConfig.eventQueueingAllowed, "initially datafile not ready and assumed queueing is allowed") - manager.registerVUID(vuid: "v1") // each of these will try to flush + manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush manager.identifyUser(vuid: "v1", userId: "u1") manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:]) @@ -215,7 +215,7 @@ class OdpEventManagerTests: XCTestCase { func testFlush_maxSize() { manager.maxQueueSize = 2 - manager.registerVUID(vuid: "v1") // each of these will try to flush + manager.sendInitializedEvent(vuid: "v1") // each of these will try to flush manager.identifyUser(vuid: "v1", userId: "u1") manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:]) diff --git a/Tests/OptimizelyTests-Common/OdpManagerTests.swift b/Tests/OptimizelyTests-Common/OdpManagerTests.swift index 0d8d8db2f..f8989a01d 100644 --- a/Tests/OptimizelyTests-Common/OdpManagerTests.swift +++ b/Tests/OptimizelyTests-Common/OdpManagerTests.swift @@ -23,7 +23,6 @@ class OdpManagerTests: XCTestCase { var segmentManager: MockOdpSegmentManager! var eventManager: MockOdpEventManager! var manager: OdpManager! - override func setUp() { OTUtils.clearAllEventQueues() segmentManager = MockOdpSegmentManager(cacheSize: cacheSize, @@ -46,6 +45,7 @@ class OdpManagerTests: XCTestCase { func testConfigurations_cache() { let manager = OdpManager(sdkKey: sdkKey, disable: false, + cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) XCTAssertEqual(manager.segmentManager?.segmentsCache.maxSize, cacheSize) @@ -59,7 +59,6 @@ class OdpManagerTests: XCTestCase { disable: true, cacheSize: cacheSize, cacheTimeoutInSecs: cacheTimeout) - XCTAssertTrue(manager.vuid.starts(with: "vuid_"), "vuid should be serverved even when ODP is disabled.") let sem = DispatchSemaphore(value: 0) manager.fetchQualifiedSegments(userId: "user1", options: []) { segments, error in @@ -73,7 +72,6 @@ class OdpManagerTests: XCTestCase { XCTAssertNil(manager.odpConfig) // these calls should be dropped gracefully with nil - manager.identifyUser(userId: "user1") try? manager.sendEvent(type: "t1", action: "a1", identifiers: [:], data: [:]) @@ -99,12 +97,20 @@ class OdpManagerTests: XCTestCase { XCTAssertEqual(segmentManager.receivedOptions, []) } - // MARK: - registerVuid - func testRegisterVUIDCalledAutomatically() { - XCTAssertEqual(eventManager.receivedRegisterVuid, manager.vuid, "registerVUID is implicitly called on OdpManager init") + func testRegisterVUIDDoesNotCallAutomatically_vuidDisabled() { + let newEventManager = MockOdpEventManager(sdkKey: sdkKey) + + _ = OdpManager(sdkKey: sdkKey, + disable: false, + cacheSize: cacheSize, + cacheTimeoutInSecs: cacheTimeout, + segmentManager: segmentManager, + eventManager: newEventManager) + + XCTAssertNil(newEventManager.receivedRegisterVuid) } - + func testRegisterVUIDCalledAutomatically_odpDisabled() { let newEventManager = MockOdpEventManager(sdkKey: sdkKey) @@ -127,17 +133,19 @@ class OdpManagerTests: XCTestCase { } func testIdentifyUser_odpIntegrated() { + let vuid = "vuid_123" + manager.vuid = vuid manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) manager.identifyUser(userId: "user-1") - XCTAssert(OdpVuidManager.isVuid(eventManager.receivedIdentifyVuid)) + XCTAssert(VuidManager.isVuid(eventManager.receivedIdentifyVuid)) XCTAssertEqual(eventManager.receivedIdentifyUserId, "user-1") } func testIdentifyUser_odpIntegrated_vuidAsUserId() { manager.updateOdpConfig(apiKey: "key-1", apiHost: "host-1", segmentsToCheck: []) - let vuidAsUserId = OdpVuidManager.newVuid + let vuidAsUserId = VuidManager.newVuid manager.identifyUser(userId: vuidAsUserId) XCTAssertEqual(eventManager.receivedIdentifyVuid, vuidAsUserId) @@ -161,11 +169,14 @@ class OdpManagerTests: XCTestCase { // MARK: - sendEvent func testSendEvent_datafileNotReady() { + let vuid = "vuid_123" + manager.vuid = vuid + try? manager.sendEvent(type: "t1", action: "a1", identifiers: ["id-key1": "id-val-1"], data: ["key1" : "val1"]) XCTAssertEqual(eventManager.receivedType, "t1") XCTAssertEqual(eventManager.receivedAction, "a1") - XCTAssertEqual(eventManager.receivedIdentifiers, ["vuid": manager.vuid,"id-key1": "id-val-1"]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["vuid": "vuid_123","id-key1": "id-val-1"]) XCTAssert(eventManager.receivedData.count == 1) XCTAssert((eventManager.receivedData["key1"] as! String) == "val1") @@ -217,20 +228,23 @@ class OdpManagerTests: XCTestCase { } func testSendEvent_aliasIdentifiers() { + let vuid = "vuid_123" + manager.vuid = vuid + try? manager.sendEvent(type: nil, action: "a1", identifiers: ["fs_user_id": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["fs-user-id": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["FS_USER_ID": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["FS-USER-ID": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["fs_user_id": "v1", "vuid": vuid]) try? manager.sendEvent(type: nil, action: "a1", identifiers: ["email": "e1", "FS-USER-ID": "v1"], data: [:]) - XCTAssertEqual(eventManager.receivedIdentifiers, ["email": "e1", "fs_user_id": "v1", "vuid": manager.vuid]) + XCTAssertEqual(eventManager.receivedIdentifiers, ["email": "e1", "fs_user_id": "v1", "vuid": vuid]) } // MARK: - updateConfig @@ -352,11 +366,6 @@ class OdpManagerTests: XCTestCase { XCTAssertEqual(eventManager.flushApiKeys.count, 1, "flush called when app goes to background") } - // MARK: - vuid - - func testVuid() { - XCTAssertEqual(manager.vuid, manager.vuidManager.vuid) - } // MARK: - Helpers @@ -375,11 +384,11 @@ class OdpManagerTests: XCTestCase { var resetCalled = false - override func registerVUID(vuid: String) { + override func sendInitializedEvent(vuid: String) { self.receivedRegisterVuid = vuid } - override func identifyUser(vuid: String, userId: String?) { + override func identifyUser(vuid: String?, userId: String?) { self.receivedIdentifyVuid = vuid self.receivedIdentifyUserId = userId } diff --git a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift b/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift deleted file mode 100644 index 261c05446..000000000 --- a/Tests/OptimizelyTests-Common/OdpVuidManagerTests.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2022, Optimizely, Inc. and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest - -class OdpVuidManagerTests: XCTestCase { - var manager = OdpVuidManager() - - func testNewVuid() { - let vuid = OdpVuidManager.newVuid - - XCTAssertTrue(vuid.starts(with: "vuid_")) - XCTAssertEqual(vuid.count, 32) - } - - func testIsVuid() { - XCTAssertTrue(OdpVuidManager.isVuid("vuid_123")) - XCTAssertFalse(OdpVuidManager.isVuid("vuid-123")) - XCTAssertFalse(OdpVuidManager.isVuid("123")) - } - - func testAutoSaveAndLoad() { - UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - - manager = OdpVuidManager() - let vuid1 = manager.vuid - - manager = OdpVuidManager() - let vuid2 = manager.vuid - - XCTAssertTrue(vuid1 == vuid2) - XCTAssert(OdpVuidManager.isVuid(vuid1)) - XCTAssert(OdpVuidManager.isVuid(vuid2)) - - UserDefaults.standard.removeObject(forKey: "optimizely-vuid") - - manager = OdpVuidManager() - let vuid3 = manager.vuid - - XCTAssertTrue(vuid1 != vuid3) - } -} diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift index f0a73284b..a08ba0e5c 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_Decide_Reasons.swift @@ -24,7 +24,7 @@ class OptimizelyUserContextTests_Decide_Reasons: XCTestCase { var decisionService: DefaultDecisionService! var ups: OPTUserProfileService! var user: OptimizelyUserContext! - + override func setUp() { super.setUp() @@ -41,13 +41,13 @@ class OptimizelyUserContextTests_Decide_Reasons: XCTestCase { // MARK: - error reasons (always included) extension OptimizelyUserContextTests_Decide_Reasons { - + func testDecideReasons_sdkNotReady() { optimizely = OptimizelyClient(sdkKey: OTUtils.randomSdkKey, userProfileService: OTUtils.createClearUserProfileService()) try? optimizely.start(datafile: OTUtils.loadJSONDatafile("unsupported_datafile")!) user = optimizely.createUserContext(userId: kUserId) - + let decision = user.decide(key: "any-key") XCTAssert(decision.hasFailed) XCTAssertEqual(decision.reasons, [OptimizelyError.sdkNotReady.reason]) @@ -59,13 +59,13 @@ extension OptimizelyUserContextTests_Decide_Reasons { XCTAssert(decision.hasFailed) XCTAssertEqual(decision.reasons, [OptimizelyError.featureKeyInvalid(key).reason]) } - + func testDecideReasons_variableValueInvalid() { let featureKey = "feature_1" let rolloutId = "3319450668" let integerVariableId = "2687470095" let integerVariableKey = "i_42" - + // inject invalid variable value var rollout = optimizely.config!.getRollout(id: rolloutId)! var rolloutVariation = rollout.experiments[0].variations[0] @@ -83,12 +83,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { // MARK: - error messages (only with "includeReasons") extension OptimizelyUserContextTests_Decide_Reasons { - + func testDecideReasons_conditionNoMatchingAudience() { let featureKey = "feature_1" let audienceId = "invalid_id" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -99,7 +99,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_format" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -110,10 +110,10 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_condition" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"gt\",\"value\":\"US\",\"name\":\"age\",\"type\":\"custom_attribute\"}" user.setAttribute(key: "age", value: 25) - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -124,7 +124,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "13389130056" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"exact\",\"value\":\"US\",\"name\":\"country\",\"type\":\"custom_attribute\"}" let attributeKey = "country" let attributeValue = 25 @@ -140,7 +140,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "age_18" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" user.setAttribute(key: "age", value: pow(2,54) as Double) // TOO-BIG value @@ -154,7 +154,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_type" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"invalid\"}" user.setAttribute(key: "age", value: 25) @@ -168,7 +168,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_match" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"invalid\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" user.setAttribute(key: "age", value: 25) @@ -182,7 +182,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "nil_value" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"name\":\"age\",\"type\":\"custom_attribute\",\"match\":\"gt\"}" user.setAttribute(key: "age", value: 25) @@ -196,7 +196,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "invalid_name" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"type\":\"custom_attribute\",\"match\":\"gt\",\"value\":18}" user.setAttribute(key: "age", value: 25) @@ -210,7 +210,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let audienceId = "age_18" setAudienceForFeatureTest(featureKey: featureKey, audienceId: audienceId) - + let condition = "{\"match\":\"gt\",\"value\":18,\"name\":\"age\",\"type\":\"custom_attribute\"}" var decision = user.decide(key: featureKey) @@ -218,13 +218,13 @@ extension OptimizelyUserContextTests_Decide_Reasons { decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(OptimizelyError.missingAttributeValue(condition, "age").reason)) } - + } // MARK: - log messages (only with "includeReasons") extension OptimizelyUserContextTests_Decide_Reasons { - + func testDecideReasons_experimentNotRunning() { let featureKey = "feature_1" let experimentKey = "exp_with_audience" @@ -242,7 +242,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let experimentKey = "exp_with_audience" let variationKey2 = "b" let variationId2 = "10416523121" - + OTUtils.setVariationToUPS(ups: ups, userId: kUserId, experimentId: experimentId, variationId: variationId2) let decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -255,7 +255,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let variationKey = "b" let experimentKey = setForcedVariationForFeatureTest(featureKey: featureKey, userId: kUserId, variationKey: variationKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssertEqual(decision.variationKey, variationKey) XCTAssertEqual(decision.reasons, [LogMessage.userHasForcedVariation(kUserId, experimentKey, variationKey).reason]) @@ -277,7 +277,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let variationKey = "b" setWhiteListForFeatureTest(featureKey: featureKey, userId: kUserId, variationKey: variationKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssertEqual(decision.variationKey, variationKey) XCTAssertEqual(decision.reasons, [LogMessage.forcedVariationFound(variationKey, kUserId).reason]) @@ -287,12 +287,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { let featureKey = "feature_1" let variationKey = "invalid-key" setWhiteListForFeatureTest(featureKey: featureKey, userId: kUserId, variationKey: variationKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssertNotEqual(decision.variationKey, variationKey) XCTAssert(decision.reasons.contains(LogMessage.forcedVariationFoundButInvalid(variationKey, kUserId).reason)) } - + func testDecideReasons_userMeetsConditionsForTargetingRule() { let key = "feature_1" @@ -322,7 +322,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { decision = user.decide(key: key, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userBucketedIntoTargetingRule(kUserId, "1").reason)) } - + func testDecideReasons_userBucketedIntoEveryoneTargetingRule() { let key = "feature_1" @@ -342,7 +342,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { decision = user.decide(key: key, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotBucketedIntoTargetingRule(kUserId, "2").reason)) } - + func testDecideReasons_userBucketedIntoVariationInExperiment() { let featureKey = "feature_2" // embedding experiment: "exp_no_audience" let experimentKey = "exp_no_audience" @@ -359,13 +359,13 @@ extension OptimizelyUserContextTests_Decide_Reasons { func testDecideReasons_userNotBucketedIntoVariation() { let featureKey = "feature_2" // embedding experiment: "exp_no_audience" let experimentId = "10420810910" // "exp_no_audience" - + var experiment = optimizely.config!.getExperiment(id: experimentId)! var trafficAllocation = experiment.trafficAllocation[0] trafficAllocation.endOfRange = 0 experiment.trafficAllocation = [trafficAllocation] optimizely.config!.experimentIdMap = [experimentId: experiment] - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -384,7 +384,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { variation.id = variationIdInvalid experiment.variations = [variation] optimizely.config!.experimentIdMap = [experimentId: experiment] - + var decision = user.decide(key: featureKey) XCTAssert(decision.reasons.isEmpty) decision = user.decide(key: featureKey, options: [.includeReasons]) @@ -396,7 +396,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let experimentKey = "group_exp_1" let groupId = "13142870430" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.ignoreUserProfileService, .includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userBucketedIntoExperimentInGroup(kUserId, experimentKey, @@ -408,7 +408,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { let experimentKey = "group_exp_2" let groupId = "13142870430" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotBucketedIntoExperimentInGroup(kUserId, experimentKey, @@ -420,12 +420,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { var group = optimizely.config!.getGroup(id: groupId)! group.trafficAllocation = [] optimizely.config!.project.groups = [group] - + // set up temp feature-experiments AFTER config.project updated (otherwise overwritten) let featureKey = "feature_3" let experimentKey = "group_exp_1" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotBucketedIntoAnyExperimentInGroup(kUserId, groupId).reason)) @@ -439,12 +439,12 @@ extension OptimizelyUserContextTests_Decide_Reasons { trafficAllocation.entityId = experimentIdInvalid group.trafficAllocation = [trafficAllocation] optimizely.config!.project.groups = [group] - + // set up temp feature-experiments AFTER config.project updated (otherwise overwritten) let featureKey = "feature_3" let experimentKey = "group_exp_1" setExperimentForFeatureTest(featureKey: featureKey, experimentKey: experimentKey) - + let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userBucketedIntoInvalidExperiment(experimentIdInvalid).reason)) } @@ -456,13 +456,13 @@ extension OptimizelyUserContextTests_Decide_Reasons { let decision = user.decide(key: featureKey, options: [.includeReasons]) XCTAssert(decision.reasons.contains(LogMessage.userNotInExperiment(kUserId, experimentKey).reason)) } - + } // Utils extension OptimizelyUserContextTests_Decide_Reasons { - + func setAudienceForFeatureTest(featureKey: String, audienceId: String) { let experimentId = "10390977673" // "exp_with_audience" var experiment = optimizely.config!.getExperiment(id: experimentId)! @@ -483,7 +483,7 @@ extension OptimizelyUserContextTests_Decide_Reasons { _ = optimizely.setForcedVariation(experimentKey: experiment.key, userId: userId, variationKey: variationKey) return experiment.key } - + func removeVariationsForFeatureTest(featureKey: String, userId: String, variationKey: String) { let experimentId = "10390977673" // "exp_with_audience" var experiment = optimizely.config!.getExperiment(id: experimentId)! @@ -491,20 +491,19 @@ extension OptimizelyUserContextTests_Decide_Reasons { optimizely.config!.experimentIdMap = [experimentId: experiment] optimizely.config!.experimentKeyMap = [experiment.key: experiment] } - + func setWhiteListForFeatureTest(featureKey: String, userId: String, variationKey: String) { let experimentId = "10390977673" // "exp_with_audience" var experiment = optimizely.config!.getExperiment(id: experimentId)! experiment.forcedVariations = [userId: variationKey] optimizely.config!.experimentIdMap = [experimentId: experiment] } - + func setExperimentForFeatureTest(featureKey: String, experimentKey: String) { let experimentId = optimizely.config!.getExperimentId(key: experimentKey)! var feature = optimizely.config!.getFeatureFlag(key: featureKey)! feature.experimentIds = [experimentId] optimizely.config!.featureFlagKeyMap = [featureKey: feature] } - + } - diff --git a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift index 79e7a3ba8..2033a7405 100644 --- a/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift +++ b/Tests/OptimizelyTests-Common/OptimizelyUserContextTests_ODP_2.swift @@ -26,20 +26,31 @@ class OptimizelyUserContextTests_ODP_2: XCTestCase { // odp disabled to avoid initial noise - let optimizely = OptimizelyClient(sdkKey: sdkKey, - settings: OptimizelySdkSettings(disableOdp: true)) - +// let optimizely = OptimizelyClient(sdkKey: sdkKey, +// settings: OptimizelySdkSettings(disableOdp: true, enableVuid: true)) +// // override with a custom enabled odpManager. // - client_inializatied event will be sent automatically // - will wait in the queue until project config is ready let odpEventApiManager = MockOdpEventApiManager() - optimizely.odpManager = OdpManager(sdkKey: sdkKey, - disable: false, - cacheSize: 10, - cacheTimeoutInSecs: 10, - eventManager: OdpEventManager(sdkKey: sdkKey, - apiManager: odpEventApiManager)) + + let optimizely = OptimizelyClient(sdkKey: sdkKey, + odpManager: + OdpManager(sdkKey: sdkKey, + disable: false, + vuid: "vuid_123", + cacheSize: 10, + cacheTimeoutInSecs: 10, + eventManager: + OdpEventManager( + sdkKey: sdkKey, + apiManager: odpEventApiManager + ) + ), + settings: + OptimizelySdkSettings(disableOdp: true, enableVuid: true) + ) // identified event will sent but wait in the queue until project config is ready _ = optimizely.createUserContext(userId: "tester") diff --git a/Tests/OptimizelyTests-Common/VuidManagerTests.swift b/Tests/OptimizelyTests-Common/VuidManagerTests.swift new file mode 100644 index 000000000..35bb653b7 --- /dev/null +++ b/Tests/OptimizelyTests-Common/VuidManagerTests.swift @@ -0,0 +1,78 @@ +// +// Copyright 2022, Optimizely, Inc. and contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +class VuidManagerTests: XCTestCase { + var manager = VuidManager.shared + + + func testNewVuid() { + manager.configure(enable: true) + + let vuid = VuidManager.newVuid + + XCTAssertTrue(vuid.starts(with: "vuid_")) + XCTAssertEqual(vuid.count, 32) + } + + func testIsVuid() { + manager.configure(enable: true) + XCTAssertTrue(VuidManager.isVuid("vuid_123")) + XCTAssertFalse(VuidManager.isVuid("vuid-123")) + XCTAssertFalse(VuidManager.isVuid("123")) + } + + func testIsVuidIgnoreCase() { + manager.configure(enable: true) + XCTAssertTrue(VuidManager.isVuid("VUID_123")) + XCTAssertFalse(VuidManager.isVuid("VUID-123")) + XCTAssertFalse(VuidManager.isVuid("123")) + } + + func testAutoSaveAndLoad() { + UserDefaults.standard.removeObject(forKey: "optimizely-vuid") + + manager.configure(enable: true) + let vuid1 = manager.vuid + + let vuid2 = manager.vuid + + XCTAssertTrue(vuid1 == vuid2) + XCTAssert(VuidManager.isVuid(vuid1!)) + XCTAssert(VuidManager.isVuid(vuid2!)) + + UserDefaults.standard.removeObject(forKey: "optimizely-vuid") + + manager.configure(enable: true) + let vuid3 = manager.vuid + + XCTAssertTrue(vuid1 != vuid3) + } + + func testRemoveOldVuid() { + manager.configure(enable: true) + let cahcedVuid1 = UserDefaults.standard.string(forKey: "optimizely-vuid") + XCTAssertNotNil(cahcedVuid1) + XCTAssertTrue(cahcedVuid1 == manager.vuid) + + manager.configure(enable: false) + let cahcedVuid2 = UserDefaults.standard.string(forKey: "optimizely-vuid") + XCTAssertNil(cahcedVuid2) + + } + +}