diff --git a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift index d9802f87..5b86b9ab 100644 --- a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift +++ b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift @@ -129,7 +129,11 @@ public class ConfidenceFeatureProvider: FeatureProvider { oldContext: OpenFeature.EvaluationContext?, newContext: OpenFeature.EvaluationContext ) { - guard oldContext?.hash() != newContext.hash() else { + var oldConfidenceContext: ConfidenceStruct = [:] + if let context = oldContext { + oldConfidenceContext = ConfidenceTypeMapper.from(ctx: context) + } + guard oldConfidenceContext.hash() != ConfidenceTypeMapper.from(ctx: newContext).hash() else { return } diff --git a/Sources/ConfidenceProvider/Utils/EvaluationContextHash.swift b/Sources/ConfidenceProvider/Utils/EvaluationContextHash.swift deleted file mode 100644 index 52579a79..00000000 --- a/Sources/ConfidenceProvider/Utils/EvaluationContextHash.swift +++ /dev/null @@ -1,70 +0,0 @@ -import CryptoKit -import Foundation -import OpenFeature - -extension EvaluationContext { - func hash() -> String { - hashEvaluationContext(context: self) - } -} - -func hashEvaluationContext(context: EvaluationContext) -> String { - var hasher = SHA256() - - hasher.update(data: context.getTargetingKey().data) - context.asMap().sorted { $0.key < $1.key }.forEach { key, value in - hasher.update(data: key.data) - hashValue(value: value, hasher: &hasher) - } - - let digest = hasher.finalize() - - return digest.map { String(format: "%02hhx", $0) }.joined() -} - -func hashValue(value: Value, hasher: inout some HashFunction) { - switch value { - case .boolean(let bool): - hasher.update(data: bool.data) - case .string(let string): - hasher.update(data: string.data) - case .integer(let int64): - hasher.update(data: int64.data) - case .double(let double): - hasher.update(data: double.data) - case .date(let date): - hasher.update(data: date.data) - case .list(let list): - list.forEach { listValue in - hashValue(value: listValue, hasher: &hasher) - } - case .structure(let structure): - structure.sorted { $0.key < $1.key }.forEach { key, structureValue in - hasher.update(data: key.data) - hashValue(value: structureValue, hasher: &hasher) - } - case .null: - hasher.update(data: UInt8(0).data) - } -} - -extension StringProtocol { - var data: Data { .init(utf8) } -} - -extension Numeric { - var data: Data { - var source = self - return .init(bytes: &source, count: MemoryLayout.size) - } -} - -extension Bool { - var data: Data { UInt8(self ? 1 : 0).data } -} - -extension Date { - var data: Data { - self.timeIntervalSince1970.data - } -} diff --git a/Tests/ConfidenceProviderTests/EvaluationContextHashTest.swift b/Tests/ConfidenceProviderTests/EvaluationContextHashTest.swift deleted file mode 100644 index c60be68a..00000000 --- a/Tests/ConfidenceProviderTests/EvaluationContextHashTest.swift +++ /dev/null @@ -1,31 +0,0 @@ -import OpenFeature -import XCTest - -@testable import ConfidenceProvider - -final class MutableContextTests: XCTestCase { - func testHashRespectsTargetingKey() throws { - let ctx1 = MutableContext(targetingKey: "user1", structure: MutableStructure()) - let ctx2 = MutableContext(targetingKey: "user2", structure: MutableStructure()) - - XCTAssertNotEqual(ctx1.hash(), ctx2.hash()) - } - - func testHashRespectsStructure() throws { - let ctx1 = MutableContext( - targetingKey: "", structure: MutableStructure(attributes: ["field": .list([.integer(3)])])) - let ctx2 = MutableContext( - targetingKey: "", structure: MutableStructure(attributes: ["field": .list([.integer(4)])])) - - XCTAssertNotEqual(ctx1.hash(), ctx2.hash()) - } - - func testHashIsEqualForEqualContext() throws { - let ctx1 = MutableContext( - targetingKey: "user1", structure: MutableStructure(attributes: ["field": .list([.integer(3)])])) - let ctx2 = MutableContext( - targetingKey: "user1", structure: MutableStructure(attributes: ["field": .list([.integer(3)])])) - - XCTAssertEqual(ctx1.hash(), ctx2.hash()) - } -} diff --git a/Tests/ConfidenceProviderTests/Helpers/StorageMock.swift b/Tests/ConfidenceProviderTests/Helpers/StorageMock.swift index bc98ec1f..a6276bc1 100644 --- a/Tests/ConfidenceProviderTests/Helpers/StorageMock.swift +++ b/Tests/ConfidenceProviderTests/Helpers/StorageMock.swift @@ -28,7 +28,7 @@ class StorageMock: Storage { if data.isEmpty { return defaultValue } - return try JSONDecoder().decode(T.self, from: data.data) + return try JSONDecoder().decode(T.self, from: try XCTUnwrap(data.data(using: .utf8))) } } diff --git a/Tests/ConfidenceTests/ConfidenceValueHashTest.swift b/Tests/ConfidenceTests/ConfidenceValueHashTest.swift new file mode 100644 index 00000000..76184373 --- /dev/null +++ b/Tests/ConfidenceTests/ConfidenceValueHashTest.swift @@ -0,0 +1,44 @@ +import XCTest + +@testable import Confidence + +final class MutableContextTests: XCTestCase { + func testHashRespectsTargetingKey() throws { + let ctx1: ConfidenceStruct = + ["targetingKey": ConfidenceValue(string: "user1"), "structure": ConfidenceValue(structure: [:])] + let ctx2: ConfidenceStruct = + ["targetingKey": ConfidenceValue(string: "user2"), "structure": ConfidenceValue(structure: [:])] + + XCTAssertNotEqual(ctx1.hash(), ctx2.hash()) + } + + func testHashRespectsStructure() throws { + let ctx1: ConfidenceStruct = + [ + "targetingKey": ConfidenceValue(string: "user1"), + "structure": ConfidenceValue(structure: ["integer": ConfidenceValue(integer: 3)]) + ] + let ctx2: ConfidenceStruct = + [ + "targetingKey": ConfidenceValue(string: "user2"), + "structure": ConfidenceValue(structure: ["integer": ConfidenceValue(integer: 4)]) + ] + + XCTAssertNotEqual(ctx1.hash(), ctx2.hash()) + } + + func testHashIsEqualForEqualContext() throws { + let ctx1: ConfidenceStruct = + [ + "targetingKey": ConfidenceValue(string: "user1"), + "structure": ConfidenceValue(structure: ["integer": ConfidenceValue(integer: 3)]) + ] + let ctx2: ConfidenceStruct = + [ + "targetingKey": ConfidenceValue(string: "user2"), + "structure": ConfidenceValue(structure: ["integer": ConfidenceValue(integer: 3)]) + ] + + XCTAssertNotEqual(ctx1.hash(), ctx2.hash()) + } +}