From 038097063b98dfb34949c056c2fcacae7794158c Mon Sep 17 00:00:00 2001 From: Fabrizio Demaria Date: Wed, 6 Nov 2024 16:07:56 +0100 Subject: [PATCH] fix: apply is sent with null values --- Sources/Confidence/FlagEvaluation.swift | 18 +++++++--- Tests/ConfidenceTests/ConfidenceTest.swift | 40 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Sources/Confidence/FlagEvaluation.swift b/Sources/Confidence/FlagEvaluation.swift index 393fbc2a..c04d6c61 100644 --- a/Sources/Confidence/FlagEvaluation.swift +++ b/Sources/Confidence/FlagEvaluation.swift @@ -44,11 +44,7 @@ extension FlagResolution { ) } - if resolvedFlag.resolveReason != .targetingKeyError { - Task { - await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken) - } - } else { + if resolvedFlag.resolveReason == .targetingKeyError { return Evaluation( value: defaultValue, variant: nil, @@ -59,6 +55,9 @@ extension FlagResolution { } guard let value = resolvedFlag.value else { + Task { + await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken) + } return Evaluation( value: defaultValue, variant: resolvedFlag.variant, @@ -77,6 +76,9 @@ extension FlagResolution { resolveReason = .stale } if let typedValue = typedValue { + Task { + await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken) + } return Evaluation( value: typedValue, variant: resolvedFlag.variant, @@ -87,6 +89,9 @@ extension FlagResolution { } else { // `null` type from backend instructs to use client-side default value if parsedValue == .init(null: ()) { + Task { + await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken) + } return Evaluation( value: defaultValue, variant: resolvedFlag.variant, @@ -105,6 +110,9 @@ extension FlagResolution { } } } else { + Task { + await flagApplier?.apply(flagName: parsedKey.flag, resolveToken: self.resolveToken) + } return Evaluation( value: defaultValue, variant: resolvedFlag.variant, diff --git a/Tests/ConfidenceTests/ConfidenceTest.swift b/Tests/ConfidenceTests/ConfidenceTest.swift index 4d8c3f96..e99988c4 100644 --- a/Tests/ConfidenceTests/ConfidenceTest.swift +++ b/Tests/ConfidenceTests/ConfidenceTest.swift @@ -285,6 +285,46 @@ class ConfidenceTest: XCTestCase { XCTAssertEqual(flagApplier.applyCallCount, 1) } + func testResolveAndApplyIntegerFlagNullValue() async throws { + class FakeClient: ConfidenceResolveClient { + var resolveStats: Int = 0 + var resolvedValues: [ResolvedValue] = [] + func resolve(ctx: ConfidenceStruct) async throws -> ResolvesResult { + self.resolveStats += 1 + return .init(resolvedValues: resolvedValues, resolveToken: "token") + } + } + + let client = FakeClient() + client.resolvedValues = [ + ResolvedValue( + value: .init(structure: ["size": .init(null: ())]), + flag: "flag", + resolveReason: .match) + ] + + let confidence = Confidence.Builder(clientSecret: "test") + .withContext(initialContext: ["targeting_key": .init(string: "user2")]) + .withFlagResolverClient(flagResolver: client) + .withFlagApplier(flagApplier: flagApplier) + .build() + + try await confidence.fetchAndActivate() + let evaluation = confidence.getEvaluation( + key: "flag.size", + defaultValue: 4) + + XCTAssertEqual(client.resolveStats, 1) + XCTAssertEqual(evaluation.value, 4) + XCTAssertNil(evaluation.errorCode) + XCTAssertNil(evaluation.errorMessage) + XCTAssertEqual(evaluation.reason, .match) + XCTAssertNil(evaluation.variant) + XCTAssertEqual(client.resolveStats, 1) + await fulfillment(of: [flagApplier.applyExpectation], timeout: 1) + XCTAssertEqual(flagApplier.applyCallCount, 1) + } + func testResolveAndApplyIntegerFlagTwice() async throws { class FakeClient: ConfidenceResolveClient { var resolveStats: Int = 0