From 80d5c90901b2ca536fdb45d8f0c08016731009f2 Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 08:48:47 +0100 Subject: [PATCH 1/7] strongly-typed StrippenRefresherFailMissingCredentialsError as an enum instead of String --- .../Dashboard/HolderDashboardViewModel.swift | 14 ++++++++++---- .../HolderDashboardViewModel+makeVCCards.swift | 11 ++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift index 52edf7165..d4fe74337 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift @@ -93,6 +93,12 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { /// that allows us to use a `didSet{}` to /// get a callback if any of them are mutated. struct State: Equatable { + enum StrippenRefresherFailMissingCredentialsError: Error { + case noInternet + case otherFailureFirstOccurence, otherFailureSubsequentOccurence + } + + var qrCards: [QRCard] var expiredGreenCards: [ExpiredQR] var blockedEventItems: [RemovedEventItem] @@ -103,7 +109,7 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { // When there's an error with the refreshing process, // we show an error message on each QR card that lacks credentials. // This does not discriminate between domestic/EU. - var errorForQRCardsMissingCredentials: String? + var errorForQRCardsMissingCredentials: StrippenRefresherFailMissingCredentialsError? var deviceHasClockDeviation: Bool = false @@ -461,7 +467,7 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { case (.noInternet, .expired, true): logDebug("StrippenRefresh: Need refreshing now, but no internet. Showing in UI.") - state.errorForQRCardsMissingCredentials = L.holderDashboardStrippenExpiredErrorfooterNointernet() + state.errorForQRCardsMissingCredentials = .noInternet case (.noInternet, .expiring, true): // Do nothing @@ -478,8 +484,8 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { checkForMismatchedIdentityError(error: error) state.errorForQRCardsMissingCredentials = refresherState.errorOccurenceCount > 1 - ? L.holderDashboardStrippenExpiredErrorfooterServerHelpdesk(Current.contactInformationProvider.phoneNumberLink) - : L.holderDashboardStrippenExpiredErrorfooterServerTryagain(AppAction.tryAgain) + ? .otherFailureSubsequentOccurence + : .otherFailureFirstOccurence case let (.failed(error), .expiring, _): // In this case we just swallow the server errors. diff --git a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift index 53d553d89..228855cec 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift @@ -572,7 +572,16 @@ extension HolderDashboardViewModel.QRCard { /// Returns `HolderDashboardViewController.Card.Error`, if appropriate, which configures the display of an error on the QRCardView. private func qrCardError(state: HolderDashboardViewModel.State, actionHandler: HolderDashboardCardUserActionHandling) -> HolderDashboardViewController.Card.Error? { guard let error = state.errorForQRCardsMissingCredentials, shouldShowErrorBeneathCard else { return nil } - return HolderDashboardViewController.Card.Error(message: error, didTapURL: { [weak actionHandler] url in + + let errorMessage: String = { + switch error { + case .noInternet: return L.holderDashboardStrippenExpiredErrorfooterNointernet() + case .otherFailureFirstOccurence: return L.holderDashboardStrippenExpiredErrorfooterServerTryagain(AppAction.tryAgain) + case .otherFailureSubsequentOccurence: return L.holderDashboardStrippenExpiredErrorfooterServerHelpdesk(Current.contactInformationProvider.phoneNumberLink) + } + }() + + return HolderDashboardViewController.Card.Error(message: errorMessage, didTapURL: { [weak actionHandler] url in if url.absoluteString == AppAction.tryAgain { actionHandler?.didTapRetryLoadQRCards() } else { From 828351356e965fb06dfce7b5b0d6a25665b80123 Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:21:11 +0100 Subject: [PATCH 2/7] Refresh screen if remote config is updated --- .../Holder/Dashboard/HolderDashboardViewModel.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift index d4fe74337..932099ca1 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift @@ -217,7 +217,7 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { // Observation tokens: private var remoteConfigUpdateObserverToken: Observatory.ObserverToken? private var clockDeviationObserverToken: Observatory.ObserverToken? - private var remoteConfigUpdatesConfigurationWarningToken: Observatory.ObserverToken? + private var remoteConfigUpdatesToken: Observatory.ObserverToken? private var disclosurePolicyUpdateObserverToken: Observatory.ObserverToken? private var configurationAlmostOutOfDateObserverToken: Observatory.ObserverToken? @@ -323,7 +323,7 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { clockDeviationObserverToken.map(Current.clockDeviationManager.observatory.remove) disclosurePolicyUpdateObserverToken.map(Current.disclosurePolicyManager.observatory.remove) remoteConfigUpdateObserverToken.map(Current.remoteConfigManager.observatoryForUpdates.remove) - remoteConfigUpdatesConfigurationWarningToken.map(Current.remoteConfigManager.observatoryForReloads.remove) + remoteConfigUpdatesToken.map(Current.remoteConfigManager.observatoryForReloads.remove) } // MARK: - Setup @@ -378,9 +378,10 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { func setupConfigNotificationManager() { - remoteConfigUpdatesConfigurationWarningToken = Current.remoteConfigManager.observatoryForReloads.append { [weak self] result in - guard let self, case .success = result else { return } - self.setupRecommendedVersion() + remoteConfigUpdatesToken = Current.remoteConfigManager.observatoryForUpdates.append { [weak self] result in + guard let self else { return } + self.setupRecommendedVersion() // Config changed, check recommended version. + self.qrcardDatasource.reload() // Config changed, reload the screen. } } From c60bd4afb7508ea5d2d9283737cfffa3938a8c89 Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:24:01 +0100 Subject: [PATCH 3/7] =?UTF-8?q?Group=20DCCs=20and=20CTBs=20when=20sorting,?= =?UTF-8?q?=20so=20that=20it=E2=80=99s=20not=20random=20(and=20so=20array?= =?UTF-8?q?=20equality=20check=20is=20deterministic)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Logic/HolderDashboardViewModel+Model.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift index 484906abc..2d34a1fb8 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift @@ -130,7 +130,15 @@ extension HolderDashboardViewModel { let firstOrigin = firstGreenCard.origins.first else { return .greatestFiniteMagnitude } - return firstOrigin.customSortIndex + // DCCs and CTBs should be grouped when the GreenCards are sorted: + let regionModifier: Double = { + switch self.region { + case .europeanUnion: return 1 + case .netherlands: return 0 + } + }() + + return firstOrigin.customSortIndex + regionModifier } var origins: [GreenCard.Origin] { From 1c125b44b6415993b697dae4229fec3fc1d969bf Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:24:50 +0100 Subject: [PATCH 4/7] RemoteConfigManager provides latest config hash in callback --- .../RemoteConfiguration/DisclosurePolicyManager.swift | 2 +- .../Managers/RemoteConfiguration/RemoteConfigManager.swift | 7 ++++--- .../RemoteConfiguration/VerificationPolicyEnabler.swift | 2 +- .../CTR/Interface/AppCoordinator/LaunchStateManager.swift | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Packages/Managers/Sources/Managers/RemoteConfiguration/DisclosurePolicyManager.swift b/Packages/Managers/Sources/Managers/RemoteConfiguration/DisclosurePolicyManager.swift index 874ceed11..c785ea6fa 100644 --- a/Packages/Managers/Sources/Managers/RemoteConfiguration/DisclosurePolicyManager.swift +++ b/Packages/Managers/Sources/Managers/RemoteConfiguration/DisclosurePolicyManager.swift @@ -45,7 +45,7 @@ public class DisclosurePolicyManager: DisclosurePolicyManaging { private func configureRemoteConfigManager() { - remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _ in + remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _, _ in self?.detectPolicyChange() } } diff --git a/Packages/Managers/Sources/Managers/RemoteConfiguration/RemoteConfigManager.swift b/Packages/Managers/Sources/Managers/RemoteConfiguration/RemoteConfigManager.swift index 320d983d8..7501de448 100644 --- a/Packages/Managers/Sources/Managers/RemoteConfiguration/RemoteConfigManager.swift +++ b/Packages/Managers/Sources/Managers/RemoteConfiguration/RemoteConfigManager.swift @@ -31,7 +31,8 @@ public protocol RemoteConfigManaging: AnyObject { /// The remote configuration manager public class RemoteConfigManager: RemoteConfigManaging { - public typealias ConfigNotification = (RemoteConfiguration, Data, URLResponse) + /// String is the config hash: + public typealias ConfigNotification = (RemoteConfiguration, Data, URLResponse, String) // MARK: - Vars @@ -200,7 +201,7 @@ public class RemoteConfigManager: RemoteConfigManaging { storedConfiguration = remoteConfiguration // Some observers want to know whenever the config is reloaded (regardless if data changed since last time): - self.notifyReloadObservers(Result.success((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse))) + self.notifyReloadObservers(Result.success((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse, hash: newHash ?? ""))) // Is the newly fetched config hash the same as the existing one? // Use the hash, as not all of the config values are mapping in the remoteconfig object. @@ -208,7 +209,7 @@ public class RemoteConfigManager: RemoteConfigManaging { completion(.success((false, remoteConfiguration))) } else { // Inform the observers that only wish to know when config has changed: - notifyUpdateObservers((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse)) + notifyUpdateObservers((remoteConfiguration: remoteConfiguration, data: data, response: urlResponse, hash: newHash ?? "")) completion(.success((true, remoteConfiguration))) } } diff --git a/Packages/Managers/Sources/Managers/RemoteConfiguration/VerificationPolicyEnabler.swift b/Packages/Managers/Sources/Managers/RemoteConfiguration/VerificationPolicyEnabler.swift index e12560911..dc580c98e 100644 --- a/Packages/Managers/Sources/Managers/RemoteConfiguration/VerificationPolicyEnabler.swift +++ b/Packages/Managers/Sources/Managers/RemoteConfiguration/VerificationPolicyEnabler.swift @@ -55,7 +55,7 @@ public final class VerificationPolicyEnabler: VerificationPolicyEnableable { private func configureRemoteConfigManager() { - remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] remoteConfiguration, _, _ in + remoteConfigManagerObserverToken = remoteConfigManager.observatoryForUpdates.append { [weak self] remoteConfiguration, _, _, _ in guard let policies = remoteConfiguration.verificationPolicies else { // No feature flag available, enable default policy self?.enable(verificationPolicies: []) diff --git a/Sources/CTR/Interface/AppCoordinator/LaunchStateManager.swift b/Sources/CTR/Interface/AppCoordinator/LaunchStateManager.swift index cd43eba89..7f7e5c577 100644 --- a/Sources/CTR/Interface/AppCoordinator/LaunchStateManager.swift +++ b/Sources/CTR/Interface/AppCoordinator/LaunchStateManager.swift @@ -136,7 +136,7 @@ final class LaunchStateManager: LaunchStateManaging { // Attach behaviours that we want the RemoteConfigManager to perform // each time it refreshes the config in future: - remoteConfigManagerUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { _, rawData, _ in + remoteConfigManagerUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { _, rawData, _, _ in // Update the remote config for the crypto library Current.cryptoLibUtility.store(rawData, for: .remoteConfiguration) @@ -169,7 +169,7 @@ final class LaunchStateManager: LaunchStateManaging { // We are within the TTL. Nothing to do. break } - case .success(let (_, _, urlResponse)): + case .success(let (_, _, urlResponse, _)): // Mark remote config loaded Current.cryptoLibUtility.checkFile(.remoteConfiguration) From 0188eac8d0ed880d24fea52308a69e0ab25f5f94 Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:25:44 +0100 Subject: [PATCH 5/7] =?UTF-8?q?Save=20remoteConfigHash=20as=20state,=20so?= =?UTF-8?q?=20that=20view=20redraws=20automatically=20if=20it=E2=80=99s=20?= =?UTF-8?q?updated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RemoteConfigManagerTests.swift | 8 ++++---- .../Dashboard/HolderDashboardViewModel.swift | 17 ++++------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/Packages/Managers/Tests/ManagersTests/RemoteConfigManagerTests.swift b/Packages/Managers/Tests/ManagersTests/RemoteConfigManagerTests.swift index ce440c8c9..385480b09 100644 --- a/Packages/Managers/Tests/ManagersTests/RemoteConfigManagerTests.swift +++ b/Packages/Managers/Tests/ManagersTests/RemoteConfigManagerTests.swift @@ -250,7 +250,7 @@ class RemoteConfigManagerTests: XCTestCase { _ = sut.observatoryForReloads.append { result in reloadObserverReceivedConfiguration = result.successValue?.0 } - _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in + _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in updateObserverReceivedConfiguration = remoteConfiguration } @@ -297,7 +297,7 @@ class RemoteConfigManagerTests: XCTestCase { _ = sut.observatoryForReloads.append { result in reloadObserverReceivedConfiguration = result.successValue?.0 } - _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in + _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in updateObserverReceivedConfiguration = remoteConfiguration } @@ -389,7 +389,7 @@ class RemoteConfigManagerTests: XCTestCase { _ = sut.observatoryForReloads.append { result in reloadObserverReceivedConfiguration = result.successValue?.0 } - _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in + _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in updateObserverReceivedConfiguration = remoteConfiguration } @@ -427,7 +427,7 @@ class RemoteConfigManagerTests: XCTestCase { _ = sut.observatoryForReloads.append { result in reloadObserverReceivedConfiguration = result.successValue?.0 } - _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _ in + _ = sut.observatoryForUpdates.append { remoteConfiguration, _, _, _ in updateObserverReceivedConfiguration = remoteConfiguration } diff --git a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift index 932099ca1..d0878f1b5 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift @@ -104,6 +104,7 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { var blockedEventItems: [RemovedEventItem] var mismatchedIdentityItems: [RemovedEventItem] var isRefreshingStrippen: Bool + var lastKnownConfigHash: String? // Related to strippen refreshing. // When there's an error with the refreshing process, @@ -217,7 +218,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { // Observation tokens: private var remoteConfigUpdateObserverToken: Observatory.ObserverToken? private var clockDeviationObserverToken: Observatory.ObserverToken? - private var remoteConfigUpdatesToken: Observatory.ObserverToken? private var disclosurePolicyUpdateObserverToken: Observatory.ObserverToken? private var configurationAlmostOutOfDateObserverToken: Observatory.ObserverToken? @@ -277,7 +277,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { setupFuzzyMatchingRemovedEventsDatasource() setupStrippenRefresher() setupNotificationListeners() - setupConfigNotificationManager() setupRecommendedVersion() recalculateActiveDisclosurePolicyMode() recalculateDisclosureBannerState() @@ -295,8 +294,10 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { } // If the config ever changes, reload dependencies: - remoteConfigUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _ in + remoteConfigUpdateObserverToken = Current.remoteConfigManager.observatoryForUpdates.append { [weak self] _, _, _, hash in self?.strippenRefresher.load() + self?.setupRecommendedVersion() // Config changed, check recommended version. + self?.state.lastKnownConfigHash = hash } disclosurePolicyUpdateObserverToken = Current.disclosurePolicyManager.observatory.append { [weak self] in @@ -323,7 +324,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { clockDeviationObserverToken.map(Current.clockDeviationManager.observatory.remove) disclosurePolicyUpdateObserverToken.map(Current.disclosurePolicyManager.observatory.remove) remoteConfigUpdateObserverToken.map(Current.remoteConfigManager.observatoryForUpdates.remove) - remoteConfigUpdatesToken.map(Current.remoteConfigManager.observatoryForReloads.remove) } // MARK: - Setup @@ -376,15 +376,6 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { strippenRefresher.load() } - func setupConfigNotificationManager() { - - remoteConfigUpdatesToken = Current.remoteConfigManager.observatoryForUpdates.append { [weak self] result in - guard let self else { return } - self.setupRecommendedVersion() // Config changed, check recommended version. - self.qrcardDatasource.reload() // Config changed, reload the screen. - } - } - // MARK: - View Lifecycle callbacks: func viewWillAppear() { From c74f292cbd8384ee7d70a87d8c07fd427e92692b Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 10:46:17 +0100 Subject: [PATCH 6/7] lint --- .../Interface/Holder/Dashboard/HolderDashboardViewModel.swift | 3 +-- .../Dashboard/Logic/HolderDashboardViewModel+Model.swift | 4 ++-- .../Logic/HolderDashboardViewModel+makeVCCards.swift | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift index d0878f1b5..6dff115be 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/HolderDashboardViewModel.swift @@ -93,12 +93,11 @@ final class HolderDashboardViewModel: HolderDashboardViewModelType { /// that allows us to use a `didSet{}` to /// get a callback if any of them are mutated. struct State: Equatable { - enum StrippenRefresherFailMissingCredentialsError: Error { + enum StrippenRefresherFailMissingCredentialsError: Error { // swiftlint:disable:this type_name case noInternet case otherFailureFirstOccurence, otherFailureSubsequentOccurence } - var qrCards: [QRCard] var expiredGreenCards: [ExpiredQR] var blockedEventItems: [RemovedEventItem] diff --git a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift index 2d34a1fb8..91241c617 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+Model.swift @@ -133,8 +133,8 @@ extension HolderDashboardViewModel { // DCCs and CTBs should be grouped when the GreenCards are sorted: let regionModifier: Double = { switch self.region { - case .europeanUnion: return 1 - case .netherlands: return 0 + case .europeanUnion: return 1 + case .netherlands: return 0 } }() diff --git a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift index 228855cec..0da046b7b 100644 --- a/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift +++ b/Sources/CTR/Interface/Holder/Dashboard/Logic/HolderDashboardViewModel+makeVCCards.swift @@ -4,6 +4,7 @@ * * SPDX-License-Identifier: EUPL-1.2 */ +// swiftlint:disable file_length import Foundation import Shared From 9c6d8da95cb24a7b971fdaf0d5d1f7e4994f40e0 Mon Sep 17 00:00:00 2001 From: Ian Dundas <1131967+iandundas@users.noreply.github.com> Date: Fri, 3 Mar 2023 11:30:46 +0100 Subject: [PATCH 7/7] Fix test --- .../HolderDashboardViewModelTests+RemoteConfigUpdates.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTRTests/Interface/Holder/Dashboard/HolderDashboardViewModelTests+RemoteConfigUpdates.swift b/CTRTests/Interface/Holder/Dashboard/HolderDashboardViewModelTests+RemoteConfigUpdates.swift index 043e017c9..865369fe7 100644 --- a/CTRTests/Interface/Holder/Dashboard/HolderDashboardViewModelTests+RemoteConfigUpdates.swift +++ b/CTRTests/Interface/Holder/Dashboard/HolderDashboardViewModelTests+RemoteConfigUpdates.swift @@ -28,7 +28,7 @@ extension HolderDashboardViewModelTests { sut = vendSut(dashboardRegionToggleValue: .domestic, activeDisclosurePolicies: [.policy3G]) // Act - sendUpdate?((RemoteConfiguration.default, Data(), URLResponse())) + sendUpdate?((RemoteConfiguration.default, Data(), URLResponse(), "hash")) // Assert