diff --git a/FlipcashTests/Database/Database+MintUpsertTests.swift b/FlipcashTests/Database/Database+MintUpsertTests.swift index 20a55d80..19885e1b 100644 --- a/FlipcashTests/Database/Database+MintUpsertTests.swift +++ b/FlipcashTests/Database/Database+MintUpsertTests.swift @@ -10,7 +10,10 @@ import Testing import FlipcashCore @testable import Flipcash +// `@MainActor` because `StoredMintMetadata.metadata` is main-actor-isolated +// under the module's default isolation and the round-trips read it. @Suite("Mint upsert round-trips") +@MainActor struct DatabaseMintUpsertTests { // MARK: - Helpers diff --git a/FlipcashTests/ProfileCreationStateTests.swift b/FlipcashTests/ProfileCreationStateTests.swift index 36ba39d1..8c19bb9c 100644 --- a/FlipcashTests/ProfileCreationStateTests.swift +++ b/FlipcashTests/ProfileCreationStateTests.swift @@ -17,21 +17,21 @@ struct ProfileCreationStateTests { @Test("A timed-out attempt resumes the same blob instead of re-storing") func timeoutResumesTheSameBlob() async throws { let uploader = StubUploader() - await uploader.setFinalizationResult(.failure(ErrorBlob.timedOut)) + uploader.setFinalizationResult(.failure(ErrorBlob.timedOut)) let state = try await makeState() await #expect(throws: ErrorBlob.self) { try await state.uploadPhoto(with: uploader) } - #expect(await uploader.storeCount == 1) + #expect(uploader.storeCount == 1) #expect(state.reservedBlobID != nil) - await uploader.setFinalizationResult(.success(())) + uploader.setFinalizationResult(.success(())) try await state.uploadPhoto(with: uploader) - #expect(await uploader.storeCount == 1) - #expect(await uploader.setPictureCount == 1) + #expect(uploader.storeCount == 1) + #expect(uploader.setPictureCount == 1) } /// Rejection is terminal: the bytes behind a blob are immutable, so the id @@ -39,7 +39,7 @@ struct ProfileCreationStateTests { @Test("A rejection clears the blob so the next attempt re-stores") func rejectionForcesAFreshUpload() async throws { let uploader = StubUploader() - await uploader.setFinalizationResult(.failure(ErrorBlob.rejected(.moderation))) + uploader.setFinalizationResult(.failure(ErrorBlob.rejected(.moderation))) let state = try await makeState() @@ -48,10 +48,10 @@ struct ProfileCreationStateTests { } #expect(state.reservedBlobID == nil) - await uploader.setFinalizationResult(.success(())) + uploader.setFinalizationResult(.success(())) try await state.uploadPhoto(with: uploader) - #expect(await uploader.storeCount == 2) + #expect(uploader.storeCount == 2) } /// A reservation is signed against one byte count, so a different photo can @@ -59,7 +59,7 @@ struct ProfileCreationStateTests { @Test("Choosing a different photo drops the reserved blob") func selectingANewPhotoDropsTheReservation() async throws { let uploader = StubUploader() - await uploader.setFinalizationResult(.failure(ErrorBlob.timedOut)) + uploader.setFinalizationResult(.failure(ErrorBlob.timedOut)) let state = try await makeState() @@ -82,8 +82,8 @@ struct ProfileCreationStateTests { try await state.uploadPhoto(with: uploader) - #expect(await uploader.setPictureCount == 1) - #expect(await uploader.refreshCount == 1) + #expect(uploader.setPictureCount == 1) + #expect(uploader.refreshCount == 1) #expect(state.selectedImage == nil) #expect(state.isUploading == false) } @@ -96,7 +96,7 @@ struct ProfileCreationStateTests { await #expect(throws: ErrorBlob.self) { try await state.uploadPhoto(with: uploader) } - #expect(await uploader.storeCount == 0) + #expect(uploader.storeCount == 0) } // MARK: - Helpers - @@ -132,7 +132,11 @@ struct ProfileCreationStateTests { // MARK: - Doubles - /// Counts each leg of the upload and lets a test choose how finalization ends. -private actor StubUploader: ProfilePictureUploading { +/// `@MainActor` (not an `actor`): `ProfilePictureUploading` is main-actor +/// isolated under the module's default isolation, and an `actor` cannot conform +/// to a global-actor-isolated protocol. The suite is already `@MainActor`. +@MainActor +private final class StubUploader: ProfilePictureUploading { private(set) var storeCount = 0 private(set) var setPictureCount = 0 diff --git a/FlipcashTests/Regressions/Regression_69ea28b0.swift b/FlipcashTests/Regressions/Regression_69ea28b0.swift index cae62737..ed8db639 100644 --- a/FlipcashTests/Regressions/Regression_69ea28b0.swift +++ b/FlipcashTests/Regressions/Regression_69ea28b0.swift @@ -19,7 +19,11 @@ import Testing import FlipcashCore @testable import Flipcash +// `@MainActor` because `StoredMintMetadata.init(_:)` and `.metadata` are +// main-actor-isolated (they bridge to `MintMetadata`); calling them from the +// nonisolated test context is a hard error under Swift 6 / Xcode 27. @Suite("Regression: 69ea28b0 – MintMetadata decoded once per loaded transition", .bug("69ea28b00174c05561fd0000")) +@MainActor struct Regression_69ea28b0 { @Test("LoadingState.loaded carries both stored row and pre-decoded MintMetadata") diff --git a/FlipcashTests/URLSanitizationTests.swift b/FlipcashTests/URLSanitizationTests.swift index 89ea06d3..c4ba34f2 100644 --- a/FlipcashTests/URLSanitizationTests.swift +++ b/FlipcashTests/URLSanitizationTests.swift @@ -7,7 +7,10 @@ import Foundation import Testing @testable import Flipcash +// `@MainActor` because `sanitizedForAnalytics` is main-actor-isolated under the +// module's default isolation, and the suite reads it from every `#expect`. @Suite("URL Sanitization for Analytics") +@MainActor struct URLSanitizationTests { // MARK: - Fragments are stripped