diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index 38524deb..665bc0af 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -782,7 +782,7 @@ open class OptimizelyClient: NSObject { public func getOptimizelyConfig() throws -> OptimizelyConfig { guard let config = self.config else { throw OptimizelyError.sdkNotReady } - return OptimizelyConfigImp(projectConfig: config) + return OptimizelyConfigImp(projectConfig: config, logger: logger) } } diff --git a/Sources/Optimizely/OptimizelyConfig.swift b/Sources/Optimizely/OptimizelyConfig.swift index af590d31..e04d3002 100644 --- a/Sources/Optimizely/OptimizelyConfig.swift +++ b/Sources/Optimizely/OptimizelyConfig.swift @@ -95,8 +95,8 @@ struct OptimizelyConfigImp: OptimizelyConfig { var attributes: [OptimizelyAttribute] = [] var audiences: [OptimizelyAudience] = [] var events: [OptimizelyEvent] = [] - - init(projectConfig: ProjectConfig) { + + init(projectConfig: ProjectConfig, logger: OPTLogger = DefaultLogger()) { guard let project = projectConfig.project else { return } self.environmentKey = project.environmentKey ?? "" @@ -139,7 +139,7 @@ struct OptimizelyConfigImp: OptimizelyConfig { return updatedRollout } - self.experimentsMap = makeExperimentsMap(project: project, experiments: updatedExperiments) + self.experimentsMap = makeExperimentsMap(project: project, experiments: updatedExperiments, logger: logger) self.featuresMap = makeFeaturesMap(project: project, experiments: updatedExperiments, rollouts: updatedRollouts) } } @@ -148,9 +148,12 @@ struct OptimizelyConfigImp: OptimizelyConfig { extension OptimizelyConfigImp { - func makeExperimentsMap(project: Project, experiments: [Experiment]) -> [String: Experiment] { + func makeExperimentsMap(project: Project, experiments: [Experiment], logger: OPTLogger) -> [String: Experiment] { var map = [String: Experiment]() experiments.forEach { + if map.keys.contains($0.key) { + logger.w("Duplicate experiment keys found in datafile: \($0.key)") + } map[$0.key] = $0 } return map diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift index 53bfde5a..cf2b63c2 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift @@ -257,6 +257,64 @@ class OptimizelyClientTests_OptimizelyConfig: XCTestCase { let result = try? self.optimizely.getOptimizelyConfig() XCTAssertNil(result) } + + func testOptimizelyConfigWithDuplicateKeys() { + let exp0: [String : Any] = [ + "id": "10001", + "key": "duplicate_key", + "status": "Running", + "layerId": "22222", + "variations": [], + "trafficAllocation": [], + "audienceIds": ["33333"], + "audienceConditions": [], + "forcedVariations": ["12345": "1234567890"] + ] + + let exp1: [String : Any] = [ + "id": "10005", + "key": "duplicate_key", + "status": "Running", + "layerId": "22222", + "variations": [], + "trafficAllocation": [], + "audienceIds": ["33333"], + "audienceConditions": [], + "forcedVariations": ["12345": "1234567890"] + ] + + var projectData: [String: Any] = [ + "version": "4", + "projectId": "11111", + "experiments": [], + "audiences": [], + "groups": [], + "attributes": [], + "accountId": "1234567890", + "events": [], + "revision": "5", + "anonymizeIP": true, + "rollouts": [], + "typedAudiences": [], + "integrations": [], + "featureFlags": [], + "botFiltering": false, + "sendFlagDecisions": true + ] + + projectData["experiments"] = [exp0, exp1] + let model: Project = try! OTUtils.model(from: projectData) + let projectConfig = ProjectConfig() + projectConfig.project = model + + let logger = TestLogger() + let optiConfigImpl = OptimizelyConfigImp(projectConfig: projectConfig, logger: logger) + let optimizelyExpMap: [String: OptimizelyExperiment] = optiConfigImpl.experimentsMap + XCTAssertEqual(logger.getMessages(.warning), ["Duplicate experiment keys found in datafile: duplicate_key"]) + + XCTAssertEqual(optimizelyExpMap.count, 1) + XCTAssertEqual(optimizelyExpMap["duplicate_key"]?.id, "10005") + } } @@ -365,3 +423,41 @@ extension OptimizelyEvent { } } +// MARK: - Mock Loggers + +fileprivate class TestLogger: OPTLogger { + private static var _logLevel: OptimizelyLogLevel? + public static var logLevel: OptimizelyLogLevel { + get { + return _logLevel ?? .info + } + set (newLevel) { + _logLevel = newLevel + } + } + + required public init() { + clearMessages() + } + + func log(level: OptimizelyLogLevel, message: String) { + logMessages[level.rawValue].append(message) + } + + // Utils + + var logMessages = [[String]]() + + var logCount: Int { + return logMessages.reduce(0) { $0 + $1.count } + } + + func getMessages(_ level: OptimizelyLogLevel) -> [String] { + return logMessages[level.rawValue] + } + + func clearMessages() { + logMessages = [[String]](repeating: [], count: OptimizelyLogLevel.debug.rawValue + 1) + } + +}