diff --git a/Sources/RemoteMessaging/Matchers/UserAttributeMatcher.swift b/Sources/RemoteMessaging/Matchers/UserAttributeMatcher.swift index 582665f4b..97e669c89 100644 --- a/Sources/RemoteMessaging/Matchers/UserAttributeMatcher.swift +++ b/Sources/RemoteMessaging/Matchers/UserAttributeMatcher.swift @@ -160,19 +160,19 @@ public struct UserAttributeMatcher: AttributeMatcher { case let matchingAttribute as PrivacyProPurchasePlatformMatchingAttribute: return StringArrayMatchingAttribute(matchingAttribute.value).matches(value: privacyProPurchasePlatform ?? "") case let matchingAttribute as PrivacyProSubscriptionStatusMatchingAttribute: - guard let value = matchingAttribute.value else { - return .fail + let mappedStatuses = matchingAttribute.value.compactMap { status in + return PrivacyProSubscriptionStatus(rawValue: status) } - guard let status = PrivacyProSubscriptionStatus(rawValue: value) else { - return .fail + for status in mappedStatuses { + switch status { + case .active: if isPrivacyProSubscriptionActive { return .match } + case .expiring: if isPrivacyProSubscriptionExpiring { return .match } + case .expired: if isPrivacyProSubscriptionExpired { return .match } + } } - switch status { - case .active: return isPrivacyProSubscriptionActive ? .match : .fail - case .expiring: return isPrivacyProSubscriptionExpiring ? .match : .fail - case .expired: return isPrivacyProSubscriptionExpired ? .match : .fail - } + return .fail case let matchingAttribute as InteractedWithMessageMatchingAttribute: if dismissedMessageIds.contains(where: { messageId in StringArrayMatchingAttribute(matchingAttribute.value).matches(value: messageId) == .match diff --git a/Sources/RemoteMessaging/Model/MatchingAttributes.swift b/Sources/RemoteMessaging/Model/MatchingAttributes.swift index 62aef41aa..388d15995 100644 --- a/Sources/RemoteMessaging/Model/MatchingAttributes.swift +++ b/Sources/RemoteMessaging/Model/MatchingAttributes.swift @@ -799,13 +799,14 @@ struct PrivacyProPurchasePlatformMatchingAttribute: MatchingAttribute, Equatable } struct PrivacyProSubscriptionStatusMatchingAttribute: MatchingAttribute, Equatable { - var value: String? + var value: [String] = [] var fallback: Bool? init(jsonMatchingAttribute: AnyDecodable) { - guard let jsonMatchingAttribute = jsonMatchingAttribute.value as? [String: Any] else { return } - - if let value = jsonMatchingAttribute[RuleAttributes.value] as? String { + guard let jsonMatchingAttribute = jsonMatchingAttribute.value as? [String: Any] else { + return + } + if let value = jsonMatchingAttribute[RuleAttributes.value] as? [String] { self.value = value } if let fallback = jsonMatchingAttribute[RuleAttributes.fallback] as? Bool { @@ -813,7 +814,7 @@ struct PrivacyProSubscriptionStatusMatchingAttribute: MatchingAttribute, Equatab } } - init(value: String?, fallback: Bool?) { + init(value: [String], fallback: Bool?) { self.value = value self.fallback = fallback } diff --git a/Tests/BrowserServicesKitTests/RemoteMessaging/Mappers/JsonToRemoteConfigModelMapperTests.swift b/Tests/BrowserServicesKitTests/RemoteMessaging/Mappers/JsonToRemoteConfigModelMapperTests.swift index 0c2255a7e..82d88de45 100644 --- a/Tests/BrowserServicesKitTests/RemoteMessaging/Mappers/JsonToRemoteConfigModelMapperTests.swift +++ b/Tests/BrowserServicesKitTests/RemoteMessaging/Mappers/JsonToRemoteConfigModelMapperTests.swift @@ -168,7 +168,7 @@ class JsonToRemoteConfigModelMapperTests: XCTestCase { attribs = rule8?.attributes.filter { $0 is PrivacyProSubscriptionStatusMatchingAttribute } XCTAssertEqual(attribs?.first as? PrivacyProSubscriptionStatusMatchingAttribute, PrivacyProSubscriptionStatusMatchingAttribute( - value: "active", fallback: nil + value: ["active", "expiring"], fallback: nil )) let rule9 = config.rules.filter { $0.id == 9 }.first diff --git a/Tests/BrowserServicesKitTests/RemoteMessaging/Matchers/UserAttributeMatcherTests.swift b/Tests/BrowserServicesKitTests/RemoteMessaging/Matchers/UserAttributeMatcherTests.swift index 690f3773e..98fca95ac 100644 --- a/Tests/BrowserServicesKitTests/RemoteMessaging/Matchers/UserAttributeMatcherTests.swift +++ b/Tests/BrowserServicesKitTests/RemoteMessaging/Matchers/UserAttributeMatcherTests.swift @@ -250,35 +250,47 @@ class UserAttributeMatcherTests: XCTestCase { func testWhenPrivacyProSubscriptionStatusMatchesThenReturnMatch() throws { XCTAssertEqual(userAttributeMatcher.evaluate( - matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: "active", fallback: nil) + matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: ["active"], fallback: nil) + ), .match) + } + + func testWhenPrivacyProSubscriptionStatusHasMultipleAttributesAndOneMatchesThenReturnMatch() throws { + XCTAssertEqual(userAttributeMatcher.evaluate( + matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: ["active", "expiring", "expired"], fallback: nil) ), .match) } func testWhenPrivacyProSubscriptionStatusDoesNotMatchThenReturnFail() throws { XCTAssertEqual(userAttributeMatcher.evaluate( - matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: "expiring", fallback: nil) + matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: ["expiring"], fallback: nil) ), .fail) } func testWhenPrivacyProSubscriptionStatusHasUnsupportedStatusThenReturnFail() throws { XCTAssertEqual(userAttributeMatcher.evaluate( - matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: "unsupported_status", fallback: nil) + matchingAttribute: PrivacyProSubscriptionStatusMatchingAttribute(value: ["unsupported_status"], fallback: nil) ), .fail) } func testWhenOneDismissedMessageIdMatchesThenReturnMatch() throws { setUpUserAttributeMatcher(dismissedMessageIds: ["1"]) - XCTAssertEqual(userAttributeMatcher.evaluate(matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["1", "2", "3"], fallback: nil)), .match) + XCTAssertEqual(userAttributeMatcher.evaluate( + matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["1", "2", "3"], fallback: nil) + ), .match) } func testWhenAllDismissedMessageIdsMatchThenReturnMatch() throws { setUpUserAttributeMatcher(dismissedMessageIds: ["1", "2", "3"]) - XCTAssertEqual(userAttributeMatcher.evaluate(matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["1", "2", "3"], fallback: nil)), .match) + XCTAssertEqual(userAttributeMatcher.evaluate( + matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["1", "2", "3"], fallback: nil) + ), .match) } func testWhenNoDismissedMessageIdsMatchThenReturnFail() throws { setUpUserAttributeMatcher(dismissedMessageIds: ["1", "2", "3"]) - XCTAssertEqual(userAttributeMatcher.evaluate(matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["4", "5"], fallback: nil)), .fail) + XCTAssertEqual(userAttributeMatcher.evaluate( + matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["4", "5"], fallback: nil) + ), .fail) } func testWhenHaveDismissedMessageIdsAndMatchAttributeIsEmptyThenReturnFail() throws { @@ -288,7 +300,9 @@ class UserAttributeMatcherTests: XCTestCase { func testWhenHaveNoDismissedMessageIdsAndMatchAttributeIsNotEmptyThenReturnFail() throws { setUpUserAttributeMatcher(dismissedMessageIds: []) - XCTAssertEqual(userAttributeMatcher.evaluate(matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["1", "2"], fallback: nil)), .fail) + XCTAssertEqual(userAttributeMatcher.evaluate( + matchingAttribute: InteractedWithMessageMatchingAttribute(value: ["1", "2"], fallback: nil) + ), .fail) } private func setUpUserAttributeMatcher(dismissedMessageIds: [String] = []) { diff --git a/Tests/BrowserServicesKitTests/Resources/remote-messaging-config.json b/Tests/BrowserServicesKitTests/Resources/remote-messaging-config.json index fb58b7259..8359a757d 100644 --- a/Tests/BrowserServicesKitTests/Resources/remote-messaging-config.json +++ b/Tests/BrowserServicesKitTests/Resources/remote-messaging-config.json @@ -270,7 +270,7 @@ "value": ["apple", "stripe"] }, "pproSubscriptionStatus": { - "value": "active" + "value": ["active", "expiring"] }, "pproDaysSinceSubscribed": { "min": 5,