diff --git a/Vault/Tests/VaultFeedTests/Presentation/SettingsDangerViewModelTests.swift b/Vault/Tests/VaultFeedTests/Presentation/SettingsDangerViewModelTests.swift new file mode 100644 index 000000000..699edc790 --- /dev/null +++ b/Vault/Tests/VaultFeedTests/Presentation/SettingsDangerViewModelTests.swift @@ -0,0 +1,76 @@ +import Foundation +import Testing +@testable import VaultFeed + +@MainActor +struct SettingsDangerViewModelTests { + @Test + func deleteEntireVault_success_callsDeleterAndClearsAutofillStore() async throws { + let deleter = VaultStoreDeleterMock() + let autofillStore = VaultOTPAutofillStoreMock() + let sut = makeSUT(deleter: deleter, autofillStore: autofillStore) + + let deletion = Task { try await sut.deleteEntireVault() } + // The deliberate 2-second completion delay keeps the operation + // in flight long enough to observe the loading state. + while sut.isDeleting == false { + await Task.yield() + } + try await deletion.value + + #expect(deleter.deleteVaultCallCount == 1) + #expect(autofillStore.removeAllCallCount == 1) + #expect(sut.isDeleting == false) + } + + @Test + func deleteEntireVault_deleterFailure_throwsPresentationErrorAndResetsState() async { + let deleter = VaultStoreDeleterMock() + deleter.deleteVaultHandler = { throw TestError() } + let autofillStore = VaultOTPAutofillStoreMock() + let sut = makeSUT(deleter: deleter, autofillStore: autofillStore) + + await #expect(throws: PresentationError.self) { + try await sut.deleteEntireVault() + } + + #expect(sut.isDeleting == false) + #expect(autofillStore.removeAllCallCount == 0) + } + + @Test + func deleteEntireVault_requiresAuthenticationBeforeDeleting() async { + let deleter = VaultStoreDeleterMock() + let sut = makeSUT( + deleter: deleter, + policy: DeviceAuthenticationPolicyAlwaysDeny(), + ) + + await #expect(throws: PresentationError.self) { + try await sut.deleteEntireVault() + } + + // MANIFESTO C4: device auth gates the unattended-device threat + // for this destructive action; the deleter must never run when + // it fails. + #expect(deleter.deleteVaultCallCount == 0) + } +} + +// MARK: - Helpers + +extension SettingsDangerViewModelTests { + private func makeSUT( + deleter: VaultStoreDeleterMock = VaultStoreDeleterMock(), + autofillStore: VaultOTPAutofillStoreMock = VaultOTPAutofillStoreMock(), + policy: some DeviceAuthenticationPolicy = DeviceAuthenticationPolicyAlwaysAllow(), + ) -> SettingsDangerViewModel { + SettingsDangerViewModel( + dataModel: anyVaultDataModel( + vaultDeleter: deleter, + vaultOtpAutofillStore: autofillStore, + ), + authenticationService: DeviceAuthenticationService(policy: policy), + ) + } +} diff --git a/Vault/Tests/VaultiOSTests/SettingsDangerViewSnapshotTests.swift b/Vault/Tests/VaultiOSTests/SettingsDangerViewSnapshotTests.swift new file mode 100644 index 000000000..85f681d51 --- /dev/null +++ b/Vault/Tests/VaultiOSTests/SettingsDangerViewSnapshotTests.swift @@ -0,0 +1,41 @@ +import Foundation +import SwiftUI +import TestHelpers +import Testing +@testable import VaultFeed +@testable import VaultiOS + +@MainActor +final class SettingsDangerViewSnapshotTests { + @Test + func layout() { + let colorSchemes: [ColorScheme] = [.light, .dark] + let dynamicTypeSizes: [DynamicTypeSize] = [.xSmall, .medium, .xxLarge] + for colorScheme in colorSchemes { + for dynamicTypeSize in dynamicTypeSizes { + let snapshottingView = SettingsDangerView(viewModel: makeViewModel()) + .dynamicTypeSize(dynamicTypeSize) + .preferredColorScheme(colorScheme) + .framedForTest() + let named = "\(colorScheme)_\(dynamicTypeSize)" + + assertSnapshot( + of: snapshottingView, + as: .image, + named: named, + ) + } + } + } +} + +// MARK: - Helpers + +extension SettingsDangerViewSnapshotTests { + private func makeViewModel() -> SettingsDangerViewModel { + SettingsDangerViewModel( + dataModel: anyVaultDataModel(), + authenticationService: DeviceAuthenticationService(policy: DeviceAuthenticationPolicyAlwaysAllow()), + ) + } +} diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_medium.png b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_medium.png new file mode 100644 index 000000000..90368767e Binary files /dev/null and b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_medium.png differ diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_xSmall.png b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_xSmall.png new file mode 100644 index 000000000..f3e4e944d Binary files /dev/null and b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_xSmall.png differ diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_xxLarge.png b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_xxLarge.png new file mode 100644 index 000000000..abd255ffb Binary files /dev/null and b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.dark_xxLarge.png differ diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_medium.png b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_medium.png new file mode 100644 index 000000000..90368767e Binary files /dev/null and b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_medium.png differ diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_xSmall.png b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_xSmall.png new file mode 100644 index 000000000..f3e4e944d Binary files /dev/null and b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_xSmall.png differ diff --git a/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_xxLarge.png b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_xxLarge.png new file mode 100644 index 000000000..abd255ffb Binary files /dev/null and b/Vault/Tests/VaultiOSTests/__Snapshots__/SettingsDangerViewSnapshotTests/layout.light_xxLarge.png differ