Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions FlipcashTests/Database/Database+MintUpsertTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 17 additions & 13 deletions FlipcashTests/ProfileCreationStateTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@ 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
/// can never become ready and the next attempt has to store fresh bytes.
@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()

Expand All @@ -48,18 +48,18 @@ 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
/// never be finalized against the previous photo's blob.
@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()

Expand All @@ -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)
}
Expand All @@ -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 -
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions FlipcashTests/Regressions/Regression_69ea28b0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
3 changes: 3 additions & 0 deletions FlipcashTests/URLSanitizationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down