From 3e9ed86a294f57269eaf081a5fd9f0eb8e6a9583 Mon Sep 17 00:00:00 2001 From: Pete Smith <5278441+aataraxiaa@users.noreply.github.com> Date: Wed, 4 Sep 2024 19:07:54 +0200 Subject: [PATCH] Freemium PIR: Disable and Delete PIR If the Freemium Feature Is Disabled (#3200) Task/Issue URL: https://app.asana.com/0/0/1208081037247881/f **Description**: This PR implements disabling of PIR and deletion of related data when: 1. The user had onboarded to Freemium AND 2. The feature flag is disabled --- .../Freemium/PIR/FreemiumPIRFeature.swift | 49 ++++++- .../View/NavigationBarViewController.swift | 2 +- .../Tab/View/BrowserTabViewController.swift | 5 +- ...ataBrokerProtectionAgentManagerTests.swift | 54 +++---- ...ataBrokerProtectionAgentStopperTests.swift | 74 +++++----- .../DataBrokerProtectionTests/Mocks.swift | 2 +- ...okerProtectionFeatureGatekeeperTests.swift | 40 ++--- .../PIR/FreemiumPIRFeatureTests.swift | 138 ++++++++++++++++-- UnitTests/Menus/MoreOptionsMenuTests.swift | 10 +- .../RemoteMessagingClientTests.swift | 2 +- 10 files changed, 257 insertions(+), 119 deletions(-) diff --git a/DuckDuckGo/Freemium/PIR/FreemiumPIRFeature.swift b/DuckDuckGo/Freemium/PIR/FreemiumPIRFeature.swift index a10fe0bb8b..6124cf4e9e 100644 --- a/DuckDuckGo/Freemium/PIR/FreemiumPIRFeature.swift +++ b/DuckDuckGo/Freemium/PIR/FreemiumPIRFeature.swift @@ -32,6 +32,8 @@ final class DefaultFreemiumPIRFeature: FreemiumPIRFeature { private let featureFlagger: FeatureFlagger private let subscriptionManager: SubscriptionManager private let accountManager: AccountManager + private var freemiumPIRUserStateManager: FreemiumPIRUserStateManager + private let featureDisabler: DataBrokerProtectionFeatureDisabling var isAvailable: Bool { /* Freemium PIR availability criteria: @@ -41,18 +43,59 @@ final class DefaultFreemiumPIRFeature: FreemiumPIRFeature { 4. (Temp) In experiment cohort */ featureFlagger.isFeatureOn(.freemiumPIR) // #1 - && subscriptionManager.isPrivacyProPurchaseAvailable // #2 - && !accountManager.isUserAuthenticated // #3 + && isPotentialPrivacyProSubscriber // #2 & #3 // TODO: - Also check experiment cohort here } init(featureFlagger: FeatureFlagger = NSApp.delegateTyped.featureFlagger, subscriptionManager: SubscriptionManager, - accountManager: AccountManager) { + accountManager: AccountManager, + freemiumPIRUserStateManager: FreemiumPIRUserStateManager, + featureDisabler: DataBrokerProtectionFeatureDisabling = DataBrokerProtectionFeatureDisabler()) { self.featureFlagger = featureFlagger self.subscriptionManager = subscriptionManager self.accountManager = accountManager + self.freemiumPIRUserStateManager = freemiumPIRUserStateManager + self.featureDisabler = featureDisabler + + offBoardIfNecessary() + } +} + +private extension DefaultFreemiumPIRFeature { + + /// Returns true if a user is a "potential" Privacy Pro subscriber. This means: + /// + /// 1. Is eligible to purchase + /// 2. Is not a current subscriber + var isPotentialPrivacyProSubscriber: Bool { + subscriptionManager.isPrivacyProPurchaseAvailable + && !accountManager.isUserAuthenticated + } + + /// Returns true IFF: + /// + /// 1. The user did onboard to Freemium PIR + /// 2. The feature flag is disabled + /// 3. The user `isPotentialPrivacyProSubscriber` (see definition) + var shouldDisableAndDelete: Bool { + guard freemiumPIRUserStateManager.didOnboard else { return false } + + return !featureFlagger.isFeatureOn(.freemiumPIR) + && isPotentialPrivacyProSubscriber + } + + /// This method offboards a Freemium user if the feature flag was disabled + /// + /// Offboarding involves: + /// - Resettting `FreemiumPIRUserStateManager`state + /// - Disabling and deleting PIR data + func offBoardIfNecessary() { + if shouldDisableAndDelete { + freemiumPIRUserStateManager.didOnboard = false + featureDisabler.disableAndDelete() + } } } diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift index 8e04e6d0d9..529b304db9 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift @@ -287,7 +287,7 @@ final class NavigationBarViewController: NSViewController { @IBAction func optionsButtonAction(_ sender: NSButton) { let internalUserDecider = NSApp.delegateTyped.internalUserDecider let freemiumPIRUserStateManager = DefaultFreemiumPIRUserStateManager(userDefaults: .dbp) - let freemiumPIRFeature = DefaultFreemiumPIRFeature(subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager) + let freemiumPIRFeature = DefaultFreemiumPIRFeature(subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager, freemiumPIRUserStateManager: freemiumPIRUserStateManager) let menu = MoreOptionsMenu(tabCollectionViewModel: tabCollectionViewModel, passwordManagerCoordinator: PasswordManagerCoordinator.shared, vpnFeatureGatekeeper: DefaultVPNFeatureGatekeeper(subscriptionManager: subscriptionManager), diff --git a/DuckDuckGo/Tab/View/BrowserTabViewController.swift b/DuckDuckGo/Tab/View/BrowserTabViewController.swift index b3dcd6082a..2091ffb8ba 100644 --- a/DuckDuckGo/Tab/View/BrowserTabViewController.swift +++ b/DuckDuckGo/Tab/View/BrowserTabViewController.swift @@ -790,8 +790,8 @@ final class BrowserTabViewController: NSViewController { private func homePageViewControllerCreatingIfNeeded() -> HomePageViewController { return homePageViewController ?? { let subscriptionManager = Application.appDelegate.subscriptionManager - let freemiumPIRFeature = DefaultFreemiumPIRFeature(subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager) let freemiumPIRUserStateManager = DefaultFreemiumPIRUserStateManager(userDefaults: .dbp) + let freemiumPIRFeature = DefaultFreemiumPIRFeature(subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager, freemiumPIRUserStateManager: freemiumPIRUserStateManager) let homePageViewController = HomePageViewController(tabCollectionViewModel: tabCollectionViewModel, bookmarkManager: bookmarkManager, freemiumPIRFeature: freemiumPIRFeature, freemiumPIRUserStateManager: freemiumPIRUserStateManager) @@ -806,7 +806,8 @@ final class BrowserTabViewController: NSViewController { private func dataBrokerProtectionHomeViewControllerCreatingIfNeeded() -> DBPHomeViewController { return dataBrokerProtectionHomeViewController ?? { let subscriptionManager = Application.appDelegate.subscriptionManager - let freemiumPIRFeature = DefaultFreemiumPIRFeature(subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager) + let freemiumPIRUserStateManager = DefaultFreemiumPIRUserStateManager(userDefaults: .dbp) + let freemiumPIRFeature = DefaultFreemiumPIRFeature(subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager, freemiumPIRUserStateManager: freemiumPIRUserStateManager) let dataBrokerProtectionHomeViewController = DBPHomeViewController(dataBrokerProtectionManager: DataBrokerProtectionManager.shared, freemiumPIRFeature: freemiumPIRFeature) self.dataBrokerProtectionHomeViewController = dataBrokerProtectionHomeViewController return dataBrokerProtectionHomeViewController diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift index e488134321..0b462cf8d1 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift @@ -33,7 +33,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { private var mockProfile: DataBrokerProtectionProfile! private var mockAgentStopper: MockAgentStopper! private var mockAuthenticationManager: MockAuthenticationManager! - private var mockFreemiumPIRUserState: MockFreemiumPIRUserState! + private var mockFreemiumPIRUserStateManager: MockFreemiumPIRUserStateManager! override func setUpWithError() throws { @@ -70,7 +70,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { phones: [], birthYear: 1992) - mockFreemiumPIRUserState = MockFreemiumPIRUserState() + mockFreemiumPIRUserStateManager = MockFreemiumPIRUserStateManager() } func testWhenAgentStart_andProfileExists_andUserIsNotFreemium_thenActivityIsScheduled_andScheduledAllOperationsRun() async throws { @@ -85,11 +85,11 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = mockProfile mockAuthenticationManager.isUserAuthenticatedValue = true - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let schedulerStartedExpectation = XCTestExpectation(description: "Scheduler started") var schedulerStarted = false @@ -126,10 +126,10 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = mockProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let schedulerStartedExpectation = XCTestExpectation(description: "Scheduler started") var schedulerStarted = false @@ -161,7 +161,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { entitlementMonitor: DataBrokerProtectionEntitlementMonitor(), authenticationManager: MockAuthenticationManager(), pixelHandler: mockPixelHandler, - stopAction: mockStopAction, freemiumPIRUserStateManager: MockFreemiumPIRUserState()) + stopAction: mockStopAction, freemiumPIRUserStateManager: MockFreemiumPIRUserStateManager()) sut = DataBrokerProtectionAgentManager( userNotificationService: mockNotificationService, activityScheduler: mockActivityScheduler, @@ -172,10 +172,10 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: agentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = nil - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopAgentExpectation = XCTestExpectation(description: "Stop agent expectation") @@ -207,7 +207,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = nil @@ -246,11 +246,11 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = mockProfile mockAuthenticationManager.isUserAuthenticatedValue = true - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false var startScheduledScansCalled = false mockQueueManager.startScheduledAllOperationsIfPermittedCalledCompletion = { _ in @@ -276,10 +276,10 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = mockProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true var startScheduledScansCalled = false mockQueueManager.startScheduledScanOperationsIfPermittedCalledCompletion = { _ in @@ -305,10 +305,10 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = mockProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false var startImmediateScansCalled = false mockQueueManager.startImmediateScanOperationsIfPermittedCalledCompletion = { _ in @@ -334,10 +334,10 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockDataManager.profileToReturn = mockProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true var startImmediateScansCalled = false mockQueueManager.startImmediateScanOperationsIfPermittedCalledCompletion = { _ in @@ -363,7 +363,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockNotificationService.reset() @@ -386,7 +386,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockNotificationService.reset() @@ -409,7 +409,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockNotificationService.reset() mockQueueManager.startImmediateScanOperationsIfPermittedCompletionError = DataBrokerProtectionAgentErrorCollection(oneTimeError: NSError(domain: "test", code: 10)) @@ -433,7 +433,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = true @@ -457,7 +457,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = false @@ -481,10 +481,10 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) mockAuthenticationManager.isUserAuthenticatedValue = true - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false var startScheduledScansCalled = false mockQueueManager.startScheduledAllOperationsIfPermittedCalledCompletion = { _ in @@ -510,9 +510,9 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, authenticationManager: mockAuthenticationManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true var startScheduledScansCalled = false mockQueueManager.startScheduledScanOperationsIfPermittedCalledCompletion = { _ in diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift index ff91b82ff5..f9f3f9b27f 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift @@ -29,7 +29,7 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { private var mockEntitlementMonitor: DataBrokerProtectionEntitlementMonitor! private var mockDataManager: MockDataBrokerProtectionDataManager! private var mockStopAction: MockDataProtectionStopAction! - private var mockFreemiumPIRUserState: MockFreemiumPIRUserState! + private var mockFreemiumPIRUserStateManager: MockFreemiumPIRUserStateManager! private var fakeProfile: DataBrokerProtectionProfile { let name = DataBrokerProtectionProfile.Name(firstName: "John", lastName: "Doe") @@ -46,8 +46,8 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockDataManager = MockDataBrokerProtectionDataManager(pixelHandler: mockPixelHandler, fakeBrokerFlag: DataBrokerDebugFlagFakeBroker()) mockStopAction = MockDataProtectionStopAction() - mockFreemiumPIRUserState = MockFreemiumPIRUserState() - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager = MockFreemiumPIRUserStateManager() + mockFreemiumPIRUserStateManager.didOnboard = false } override func tearDown() { @@ -63,14 +63,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = nil - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -80,14 +80,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = nil - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -97,14 +97,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = nil - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -114,14 +114,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = nil - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -131,14 +131,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = false mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -148,14 +148,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = false mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertFalse(mockStopAction.wasStopCalled) @@ -165,14 +165,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = false mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -182,14 +182,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = false mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -199,14 +199,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertTrue(mockStopAction.wasStopCalled) @@ -216,14 +216,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertFalse(mockStopAction.wasStopCalled) @@ -239,7 +239,7 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertFalse(mockStopAction.wasStopCalled) @@ -249,14 +249,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertFalse(mockStopAction.wasStopCalled) @@ -266,14 +266,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() XCTAssertFalse(mockStopAction.wasStopCalled) @@ -283,14 +283,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalidAndUserIsNotFreemium(interval: 0.1) @@ -307,14 +307,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = true mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalidAndUserIsNotFreemium(interval: 0.1) @@ -331,14 +331,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = true mockAuthenticationManager.hasValidEntitlementValue = false mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalidAndUserIsNotFreemium(interval: 0.1) @@ -355,14 +355,14 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { mockAuthenticationManager.isUserAuthenticatedValue = false mockAuthenticationManager.hasValidEntitlementValue = false mockDataManager.profileToReturn = fakeProfile - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, entitlementMonitor: mockEntitlementMonitor, authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalidAndUserIsNotFreemium(interval: 0.1) @@ -385,7 +385,7 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { authenticationManager: mockAuthenticationManager, pixelHandler: mockPixelHandler, stopAction: mockStopAction, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalidAndUserIsNotFreemium(interval: 0.1) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift index f74d93f5c9..1fbc11b710 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift @@ -1939,6 +1939,6 @@ struct MockMigrationsProvider: DataBrokerProtectionDatabaseMigrationsProvider { } } -final class MockFreemiumPIRUserState: FreemiumPIRUserStateManager { +final class MockFreemiumPIRUserStateManager: FreemiumPIRUserStateManager { var didOnboard = false } diff --git a/UnitTests/DBP/Tests/DataBrokerProtectionFeatureGatekeeperTests.swift b/UnitTests/DBP/Tests/DataBrokerProtectionFeatureGatekeeperTests.swift index 39549e751c..885816f9e5 100644 --- a/UnitTests/DBP/Tests/DataBrokerProtectionFeatureGatekeeperTests.swift +++ b/UnitTests/DBP/Tests/DataBrokerProtectionFeatureGatekeeperTests.swift @@ -28,7 +28,7 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { private var mockFeatureDisabler: MockFeatureDisabler! private var mockFeatureAvailability: MockFeatureAvailability! private var mockAccountManager: MockAccountManager! - private var mockFreemiumPIRUserState: MockFreemiumPIRUserState! + private var mockFreemiumPIRUserStateManager: MockFreemiumPIRUserStateManager! private func userDefaults() -> UserDefaults { UserDefaults(suiteName: "testing_\(UUID().uuidString)")! @@ -38,8 +38,8 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { mockFeatureDisabler = MockFeatureDisabler() mockFeatureAvailability = MockFeatureAvailability() mockAccountManager = MockAccountManager() - mockFreemiumPIRUserState = MockFreemiumPIRUserState() - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager = MockFreemiumPIRUserStateManager() + mockFreemiumPIRUserStateManager.didOnboard = false } func testWhenNoAccessTokenIsFound_butEntitlementIs_andIsNotActiveFreemiumUser_thenFeatureIsDisabled() async { @@ -50,7 +50,7 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { userDefaults: userDefaults(), subscriptionAvailability: mockFeatureAvailability, accountManager: mockAccountManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) // When let result = await sut.arePrerequisitesSatisfied() @@ -63,12 +63,12 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { // Given mockAccountManager.accessToken = "token" mockAccountManager.hasEntitlementResult = .failure(MockError.someError) - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false sut = DefaultDataBrokerProtectionFeatureGatekeeper(featureDisabler: mockFeatureDisabler, userDefaults: userDefaults(), subscriptionAvailability: mockFeatureAvailability, accountManager: mockAccountManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) // When let result = await sut.arePrerequisitesSatisfied() @@ -81,12 +81,12 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { // Given mockAccountManager.accessToken = "token" mockAccountManager.hasEntitlementResult = .failure(MockError.someError) - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true sut = DefaultDataBrokerProtectionFeatureGatekeeper(featureDisabler: mockFeatureDisabler, userDefaults: userDefaults(), subscriptionAvailability: mockFeatureAvailability, accountManager: mockAccountManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) // When let result = await sut.arePrerequisitesSatisfied() @@ -99,12 +99,12 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { // Given mockAccountManager.accessToken = nil mockAccountManager.hasEntitlementResult = .failure(MockError.someError) - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false sut = DefaultDataBrokerProtectionFeatureGatekeeper(featureDisabler: mockFeatureDisabler, userDefaults: userDefaults(), subscriptionAvailability: mockFeatureAvailability, accountManager: mockAccountManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) // When let result = await sut.arePrerequisitesSatisfied() @@ -117,12 +117,12 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { // Given mockAccountManager.accessToken = "token" mockAccountManager.hasEntitlementResult = .success(true) - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false sut = DefaultDataBrokerProtectionFeatureGatekeeper(featureDisabler: mockFeatureDisabler, userDefaults: userDefaults(), subscriptionAvailability: mockFeatureAvailability, accountManager: mockAccountManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) // When let result = await sut.arePrerequisitesSatisfied() @@ -135,12 +135,12 @@ final class DataBrokerProtectionFeatureGatekeeperTests: XCTestCase { // Given mockAccountManager.accessToken = nil mockAccountManager.hasEntitlementResult = .failure(MockError.someError) - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true sut = DefaultDataBrokerProtectionFeatureGatekeeper(featureDisabler: mockFeatureDisabler, userDefaults: userDefaults(), subscriptionAvailability: mockFeatureAvailability, accountManager: mockAccountManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState) + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager) // When let result = await sut.arePrerequisitesSatisfied() @@ -154,18 +154,6 @@ private enum MockError: Error { case someError } -private class MockFeatureDisabler: DataBrokerProtectionFeatureDisabling { - var disableAndDeleteWasCalled = false - - func disableAndDelete() { - disableAndDeleteWasCalled = true - } - - func reset() { - disableAndDeleteWasCalled = false - } -} - private class MockFeatureAvailability: SubscriptionFeatureAvailability { var mockFeatureAvailable: Bool = false var mockSubscriptionPurchaseAllowed: Bool = false diff --git a/UnitTests/Freemium/PIR/FreemiumPIRFeatureTests.swift b/UnitTests/Freemium/PIR/FreemiumPIRFeatureTests.swift index 9a632017c5..edd1f80e4e 100644 --- a/UnitTests/Freemium/PIR/FreemiumPIRFeatureTests.swift +++ b/UnitTests/Freemium/PIR/FreemiumPIRFeatureTests.swift @@ -21,14 +21,7 @@ import XCTest import Subscription import BrowserServicesKit import SubscriptionTestingUtilities - -final class MockFreemiumPIRFeatureFlagger: FeatureFlagger { - var isEnabled = false - - func isFeatureOn(forProvider: F) -> Bool where F: BrowserServicesKit.FeatureFlagSourceProviding { - return isEnabled - } -} +import Freemium final class FreemiumPIRFeatureTests: XCTestCase { @@ -36,6 +29,8 @@ final class FreemiumPIRFeatureTests: XCTestCase { private var mockFeatureFlagger: MockFreemiumPIRFeatureFlagger! private var mockAccountManager: MockAccountManager! private var mockSubscriptionManager: SubscriptionManagerMock! + private var mockFreemiumPIRUserStateManagerManager: MockFreemiumPIRUserStateManager! + private var mockFeatureDisabler: MockFeatureDisabler! override func setUpWithError() throws { @@ -58,6 +53,10 @@ final class FreemiumPIRFeatureTests: XCTestCase { storePurchaseManager: mockStorePurchaseManager, currentEnvironment: currentEnvironment, canPurchase: false) + + mockFreemiumPIRUserStateManagerManager = MockFreemiumPIRUserStateManager() + mockFeatureDisabler = MockFeatureDisabler() + } func testWhenFeatureFlagDisabled_thenFreemiumPIRIsNotAvailable() throws { @@ -67,7 +66,9 @@ final class FreemiumPIRFeatureTests: XCTestCase { mockAccountManager.accessToken = nil sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, subscriptionManager: mockSubscriptionManager, - accountManager: mockAccountManager) + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) // When let result = sut.isAvailable @@ -82,7 +83,9 @@ final class FreemiumPIRFeatureTests: XCTestCase { mockAccountManager.accessToken = nil sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, subscriptionManager: mockSubscriptionManager, - accountManager: mockAccountManager) + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) // When let result = sut.isAvailable @@ -97,7 +100,9 @@ final class FreemiumPIRFeatureTests: XCTestCase { mockAccountManager.accessToken = "some_token" sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, subscriptionManager: mockSubscriptionManager, - accountManager: mockAccountManager) + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) // When let result = sut.isAvailable @@ -112,7 +117,9 @@ final class FreemiumPIRFeatureTests: XCTestCase { mockAccountManager.accessToken = "some_token" sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, subscriptionManager: mockSubscriptionManager, - accountManager: mockAccountManager) + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) // When let result = sut.isAvailable @@ -120,18 +127,117 @@ final class FreemiumPIRFeatureTests: XCTestCase { XCTAssertFalse(result) } - func testWhenFeatureFlagEnabledAndPrivacyProAvailableAndNotSubscribed_thenFreemiumPIRIsAvailable() throws { + func testWhenUserDidNotOnboard_thenOffboardingIsNotExecuted() { + // Given + mockFreemiumPIRUserStateManagerManager.didOnboard = false + mockFeatureFlagger.isEnabled = false + mockSubscriptionManager.canPurchase = true + mockAccountManager.accessToken = nil + + // When + sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, + subscriptionManager: mockSubscriptionManager, + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) + + // Then + XCTAssertFalse(mockFeatureDisabler.disableAndDeleteWasCalled) + } + + func testWhenUserDidOnboard_andFeatureIsDisabled_andUserCanPurchase_andUserIsNotSubscribed_thenOffboardingIsExecuted() { + // Given + mockFreemiumPIRUserStateManagerManager.didOnboard = true + mockFeatureFlagger.isEnabled = false + mockSubscriptionManager.canPurchase = true + mockAccountManager.accessToken = nil + + // When + sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, + subscriptionManager: mockSubscriptionManager, + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) + + // Then + XCTAssertFalse(mockFreemiumPIRUserStateManagerManager.didOnboard) + XCTAssertTrue(mockFeatureDisabler.disableAndDeleteWasCalled) + } + + func testWhenUserDidOnboard_andFeatureIsDisabled_andUserCanPurchase_andUserIsSubscribed_thenOffboardingIsNotExecuted() { + // Given + mockFreemiumPIRUserStateManagerManager.didOnboard = true + mockFeatureFlagger.isEnabled = false + mockSubscriptionManager.canPurchase = true + mockAccountManager.accessToken = "some_token" + + // When + sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, + subscriptionManager: mockSubscriptionManager, + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) + + // Then + XCTAssertTrue(mockFreemiumPIRUserStateManagerManager.didOnboard) + XCTAssertFalse(mockFeatureDisabler.disableAndDeleteWasCalled) + } + + func testWhenUserDidOnboard_andFeatureIsEnabled_andUserCanPurchase_andUserIsNotSubscribed_thenOffboardingIsNotExecuted() { // Given + mockFreemiumPIRUserStateManagerManager.didOnboard = true mockFeatureFlagger.isEnabled = true mockSubscriptionManager.canPurchase = true mockAccountManager.accessToken = nil + + // When sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, subscriptionManager: mockSubscriptionManager, - accountManager: mockAccountManager) + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) + + // Then + XCTAssertTrue(mockFreemiumPIRUserStateManagerManager.didOnboard) + XCTAssertFalse(mockFeatureDisabler.disableAndDeleteWasCalled) + } + + func testWhenUserDidOnboard_andFeatureIsDisabled_andUserCannotPurchase_thenOffboardingIsNotExecuted() { + // Given + mockFreemiumPIRUserStateManagerManager.didOnboard = true + mockFeatureFlagger.isEnabled = false + mockSubscriptionManager.canPurchase = false + mockAccountManager.accessToken = nil + // When - let result = sut.isAvailable + sut = DefaultFreemiumPIRFeature(featureFlagger: mockFeatureFlagger, + subscriptionManager: mockSubscriptionManager, + accountManager: mockAccountManager, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManagerManager, + featureDisabler: mockFeatureDisabler) // Then - XCTAssertTrue(result) + XCTAssertTrue(mockFreemiumPIRUserStateManagerManager.didOnboard) + XCTAssertFalse(mockFeatureDisabler.disableAndDeleteWasCalled) + } +} + +final class MockFreemiumPIRFeatureFlagger: FeatureFlagger { + var isEnabled = false + + func isFeatureOn(forProvider: F) -> Bool where F: BrowserServicesKit.FeatureFlagSourceProviding { + return isEnabled + } +} + +final class MockFeatureDisabler: DataBrokerProtectionFeatureDisabling { + var disableAndDeleteWasCalled = false + + func disableAndDelete() { + disableAndDeleteWasCalled = true + } + + func reset() { + disableAndDeleteWasCalled = false } } diff --git a/UnitTests/Menus/MoreOptionsMenuTests.swift b/UnitTests/Menus/MoreOptionsMenuTests.swift index 19ce494d28..105f5deeeb 100644 --- a/UnitTests/Menus/MoreOptionsMenuTests.swift +++ b/UnitTests/Menus/MoreOptionsMenuTests.swift @@ -41,7 +41,7 @@ final class MoreOptionsMenuTests: XCTestCase { private var mockFreemiumPIRFeatureFlagger = MockFreemiumPIRFeatureFlagger() private var mockFreemiumPIRPresenter = MockFreemiumPIRPresenter() private var freemiumPIRFeature: DefaultFreemiumPIRFeature! - private var mockFreemiumPIRUserState = MockFreemiumPIRUserState() + private var mockFreemiumPIRUserStateManager = MockFreemiumPIRUserStateManager() var moreOptionsMenu: MoreOptionsMenu! @@ -68,7 +68,7 @@ final class MoreOptionsMenuTests: XCTestCase { purchasePlatform: .appStore), canPurchase: false) - freemiumPIRFeature = DefaultFreemiumPIRFeature(featureFlagger: mockFreemiumPIRFeatureFlagger, subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager) + freemiumPIRFeature = DefaultFreemiumPIRFeature(featureFlagger: mockFreemiumPIRFeatureFlagger, subscriptionManager: subscriptionManager, accountManager: subscriptionManager.accountManager, freemiumPIRUserStateManager: MockFreemiumPIRUserStateManager(), featureDisabler: MockFeatureDisabler()) } @@ -93,7 +93,7 @@ final class MoreOptionsMenuTests: XCTestCase { sharingMenu: NSMenu(), internalUserDecider: internalUserDecider, subscriptionManager: subscriptionManager, - freemiumPIRUserStateManager: mockFreemiumPIRUserState, + freemiumPIRUserStateManager: mockFreemiumPIRUserStateManager, freemiumPIRFeature: freemiumPIRFeature, freemiumPIRPresenter: mockFreemiumPIRPresenter) @@ -285,7 +285,7 @@ final class MoreOptionsMenuTests: XCTestCase { subscriptionManager.canPurchase = true subscriptionManager.currentEnvironment = SubscriptionEnvironment(serviceEnvironment: .production, purchasePlatform: .stripe) mockFreemiumPIRFeatureFlagger.isEnabled = true - mockFreemiumPIRUserState.didOnboard = false + mockFreemiumPIRUserStateManager.didOnboard = false setupMoreOptionsMenu() let freemiumItemIndex = try XCTUnwrap(moreOptionsMenu.indexOfItem(withTitle: UserText.freemiumPIROptionsMenuItem)) @@ -304,7 +304,7 @@ final class MoreOptionsMenuTests: XCTestCase { subscriptionManager.canPurchase = true subscriptionManager.currentEnvironment = SubscriptionEnvironment(serviceEnvironment: .production, purchasePlatform: .stripe) mockFreemiumPIRFeatureFlagger.isEnabled = true - mockFreemiumPIRUserState.didOnboard = true + mockFreemiumPIRUserStateManager.didOnboard = true setupMoreOptionsMenu() let freemiumItemIndex = try XCTUnwrap(moreOptionsMenu.indexOfItem(withTitle: UserText.freemiumPIROptionsMenuItem)) diff --git a/UnitTests/RemoteMessaging/RemoteMessagingClientTests.swift b/UnitTests/RemoteMessaging/RemoteMessagingClientTests.swift index cf2dcd7e28..0eb0f2fe73 100644 --- a/UnitTests/RemoteMessaging/RemoteMessagingClientTests.swift +++ b/UnitTests/RemoteMessaging/RemoteMessagingClientTests.swift @@ -30,7 +30,7 @@ struct MockRemoteMessagingStoreProvider: RemoteMessagingStoreProviding { } } -struct MockFreemiumPIRUserState: FreemiumPIRUserStateManager { +final class MockFreemiumPIRUserStateManager: FreemiumPIRUserStateManager { var didOnboard = false }