From 441ff11c95f0f7e59031ad88e7b4c0dc05a45c89 Mon Sep 17 00:00:00 2001 From: Michael Cerisano Date: Sat, 17 Oct 2026 09:49:56 -0400 Subject: [PATCH] Clarify finish-screen verification and failure wording --- BitMatchTests/ResultPresentationTests.swift | 10 ++++++- .../TransferOutcomePresentationTests.swift | 28 +++++++++++++++++++ .../CompletionVerdictPresentation.swift | 6 ++-- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/BitMatchTests/ResultPresentationTests.swift b/BitMatchTests/ResultPresentationTests.swift index 8dc9fb93..7aed119a 100644 --- a/BitMatchTests/ResultPresentationTests.swift +++ b/BitMatchTests/ResultPresentationTests.swift @@ -21,6 +21,10 @@ final class ResultPresentationTests: XCTestCase { XCTAssertEqual(presentation.detail, "Copied to 1 destination and verified.") XCTAssertFalse(presentation.detail.contains("2 destinations")) + XCTAssertEqual( + presentation.sourceGuidance, + "BitMatch read every file on every backup and checked it against the bytes it read from the card." + ) } func testLiveRowsNameDestinationOnlyWhenBackupsAreMixed() { @@ -169,7 +173,11 @@ final class ResultPresentationTests: XCTestCase { ) XCTAssertEqual(presentation.title, "Transfer failed") - XCTAssertEqual(presentation.sourceGuidance, "Do not erase the card. Nothing here has been confirmed safe.") + XCTAssertEqual( + presentation.detail, + "The transfer did not finish safely. Review any recorded results below." + ) + XCTAssertEqual(presentation.sourceGuidance, "Do not erase the card. The transfer did not finish safely.") } func testVisibleRowsCapsMoreThanOneThousandIssuesWithoutTrapping() { diff --git a/BitMatchTests/TransferOutcomePresentationTests.swift b/BitMatchTests/TransferOutcomePresentationTests.swift index 8dad363a..e86238c5 100644 --- a/BitMatchTests/TransferOutcomePresentationTests.swift +++ b/BitMatchTests/TransferOutcomePresentationTests.swift @@ -409,6 +409,7 @@ struct TransferOutcomePresentationTests { #expect(outcome.verdict.detail == "1 file · 100 bytes verified on A and B · SHA-256 · 1m 15s") #expect(outcome.destinations.map(\.title) == ["A › Backup", "B › Backup"]) #expect(outcome.finishTitle == "The card is safe to erase") + #expect(outcome.visibleVerdictText.contains("checked it against the bytes it read from the card")) } @Test func longBackupNamesAreMiddleTruncatedInTheFinishSubtitle() { @@ -444,6 +445,33 @@ struct TransferOutcomePresentationTests { #expect(outcome.visibleVerdictText.localizedCaseInsensitiveContains("do not erase the card")) } + @Test func failedFinishKeepsPartialVerifiedResultsVisibleAndCardUnsafe() { + let outcome = make( + state: .failed, + rows: [ + row("A001.mov", .verified, backup: backupA), + row("A002.mov", .failed, backup: backupB) + ], + hasErrors: true, + errorCount: 1 + ) + + #expect(outcome.counts.verified == 1) + #expect(outcome.finishTitle == "Transfer failed — The card") + #expect(outcome.verdict.detail.contains("Review any recorded results below")) + #expect(outcome.visibleVerdictText.contains("Do not erase the card")) + #expect(!outcome.visibleVerdictText.contains("No files were confirmed")) + } + + @Test func failedFinishWithNoRowsDoesNotInventFileResults() { + let outcome = make(state: .failed, rows: [], hasErrors: true, errorCount: 1) + + #expect(outcome.counts.total == 0) + #expect(outcome.verdict.detail.contains("Review any recorded results below")) + #expect(!outcome.visibleVerdictText.contains("files verified")) + #expect(outcome.visibleVerdictText.contains("Do not erase the card")) + } + @Test func quickAndNeedsAttentionFinishBannersNameTheCardExactlyOnce() { let name = "A_CAMERA_CARD_WITH_A_VERY_LONG_DISTINGUISHING_NAME_002" let quick = TransferOutcomePresentation.make( diff --git a/Shared/Core/Models/CompletionVerdictPresentation.swift b/Shared/Core/Models/CompletionVerdictPresentation.swift index cafd8c9c..18920f7d 100644 --- a/Shared/Core/Models/CompletionVerdictPresentation.swift +++ b/Shared/Core/Models/CompletionVerdictPresentation.swift @@ -33,7 +33,7 @@ struct CompletionVerdictPresentation: Equatable, Sendable { title: safetyState.headline(cardName: cardName), detail: "Copied to \(backups) and verified.", symbol: safetyState.symbol, - sourceGuidance: "Every file on every destination was read back and matched the card." + sourceGuidance: "BitMatch read every file on every backup and checked it against the bytes it read from the card." ) case .copiedNotVerified: return Self( @@ -53,9 +53,9 @@ struct CompletionVerdictPresentation: Equatable, Sendable { case .failed: return Self( title: safetyState.headline(cardName: cardName), - detail: "No files were confirmed copied and verified.", + detail: "The transfer did not finish safely. Review any recorded results below.", symbol: safetyState.symbol, - sourceGuidance: "Do not erase \(card). Nothing here has been confirmed safe." + sourceGuidance: "Do not erase \(card). The transfer did not finish safely." ) case .waiting, .preparing, .copying, .verifying, .finishing, .interrupted: preconditionFailure("A completion verdict must produce a finish state")