174 lines
6.8 KiB
Swift
174 lines
6.8 KiB
Swift
import Testing
|
|
|
|
@testable import Persistence
|
|
|
|
@Suite(
|
|
"AccountRepository repository",
|
|
.tags(.repository)
|
|
)
|
|
struct AccountRepositoryTests {
|
|
|
|
// MARK: Methods tests
|
|
|
|
@Test
|
|
func `finds the seeded demo buyer`() async throws {
|
|
try await withDatabase { fluent in
|
|
let user = try await AccountRepository(fluent: fluent).account(email: AccountRepository.demoEmail)
|
|
|
|
#expect(user.email == "mara.v@example.com")
|
|
#expect(user.displayName == "Mara Visser")
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `joins an unknown email once, named after its local part`() async throws {
|
|
try await withDatabase { fluent in
|
|
let repository = AccountRepository(fluent: fluent)
|
|
let joined = try await repository.account(email: "sam@example.com")
|
|
let returning = try await repository.account(email: "sam@example.com")
|
|
|
|
#expect(joined.displayName == "sam")
|
|
#expect(returning == joined)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Passkeys
|
|
|
|
extension AccountRepositoryTests {
|
|
|
|
@Test
|
|
func `joins an account together with its first passkey`() async throws {
|
|
try await withDatabase { fluent in
|
|
let accounts = AccountRepository(fluent: fluent)
|
|
let free = try await accounts.isUnregistered(credentialID: "cred-1")
|
|
let sam = try #require(try await accounts.join(email: "sam@example.com", credentialID: "cred-1", publicKey: [1, 2, 3], signCount: 0))
|
|
let passkey = try await accounts.passkey(credentialID: "cred-1")
|
|
let byEmail = try await accounts.passkeys(email: "sam@example.com")
|
|
let taken = try await accounts.isUnregistered(credentialID: "cred-1")
|
|
|
|
#expect(free)
|
|
#expect(!taken)
|
|
#expect(sam.displayName == "sam")
|
|
#expect(passkey == Passkey(credentialID: "cred-1", publicKey: [1, 2, 3], signCount: 0, user: sam))
|
|
#expect(byEmail == [passkey])
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `never adds a passkey to an account that already exists`() async throws {
|
|
try await withDatabase { fluent in
|
|
let accounts = AccountRepository(fluent: fluent)
|
|
let hijack = try await accounts.join(email: AccountRepository.demoEmail, credentialID: "cred-x", publicKey: [9], signCount: 0)
|
|
let passkeys = try await accounts.passkeys(email: AccountRepository.demoEmail)
|
|
let stillFree = try await accounts.isUnregistered(credentialID: "cred-x")
|
|
|
|
#expect(hijack == nil)
|
|
#expect(passkeys.isEmpty)
|
|
#expect(stillFree)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `records a passkey's signature counter, and finds accounts without creating them`() async throws {
|
|
try await withDatabase { fluent in
|
|
let accounts = AccountRepository(fluent: fluent)
|
|
|
|
_ = try await accounts.join(email: "sam@example.com", credentialID: "cred-1", publicKey: [1], signCount: 0)
|
|
try await accounts.recordUse(ofPasskey: "cred-1", signCount: 7)
|
|
|
|
let passkey = try await accounts.passkey(credentialID: "cred-1")
|
|
let known = try await accounts.existingAccount(email: "sam@example.com")
|
|
let unknown = try await accounts.existingAccount(email: "nobody@example.com")
|
|
let stillUnknown = try await accounts.existingAccount(email: "nobody@example.com")
|
|
|
|
#expect(passkey?.signCount == 7)
|
|
#expect(known?.email == "sam@example.com")
|
|
#expect(unknown == nil)
|
|
#expect(stillUnknown == nil)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Accounts and shops
|
|
|
|
extension AccountRepositoryTests {
|
|
|
|
@Test
|
|
func `says whether a sign-in created the account`() async throws {
|
|
try await withDatabase { fluent in
|
|
let accounts = AccountRepository(fluent: fluent)
|
|
let first = try await accounts.signIn(email: "sam@example.com")
|
|
let again = try await accounts.signIn(email: "sam@example.com")
|
|
|
|
#expect(first.isNew)
|
|
#expect(!again.isNew)
|
|
#expect(first.user == again.user)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `renames an account, and manages the passkeys of its own`() async throws {
|
|
try await withDatabase { fluent in
|
|
let accounts = AccountRepository(fluent: fluent)
|
|
let mara = try await accounts.account(email: AccountRepository.demoEmail)
|
|
let sam = try await accounts.account(email: "sam@example.com")
|
|
let renamed = try await accounts.rename(mara, to: "Mara V.")
|
|
let stored = try await accounts.existingAccount(email: AccountRepository.demoEmail)
|
|
|
|
try await accounts.addPasskey(to: mara, credentialID: "cred-m", publicKey: [1], signCount: 0)
|
|
try await accounts.revokePasskey("cred-m", of: sam)
|
|
|
|
let kept = try await accounts.passkeys(of: mara)
|
|
|
|
try await accounts.revokePasskey("cred-m", of: mara)
|
|
|
|
let revoked = try await accounts.passkeys(of: mara)
|
|
|
|
#expect(renamed.displayName == "Mara V.")
|
|
#expect(stored?.displayName == "Mara V.")
|
|
#expect(kept.map(\.passkey.credentialID) == ["cred-m"])
|
|
#expect(revoked.isEmpty)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `opens one shop per account, under a handle of its own`() async throws {
|
|
try await withDatabase { fluent in
|
|
let accounts = AccountRepository(fluent: fluent)
|
|
let catalogue = CatalogueRepository(fluent: fluent)
|
|
let mara = try await accounts.account(email: AccountRepository.demoEmail)
|
|
let sam = try await accounts.account(email: "sam@example.com")
|
|
let draft = ShopDraft(
|
|
name: "Mara's Longbox", handle: "maras-longbox", city: "Haarlem", country: "NL", about: "Spare copies.",
|
|
kind: .privateSeller, kvkNumber: "12345678", vatID: nil, legalName: nil, returnsNote: "14-day returns", handlingDays: 2
|
|
)
|
|
let shop = try await catalogue.openShop(draft, for: mara)
|
|
let owned = try await TradeRepository(fluent: fluent).shop(ownedBy: mara)
|
|
|
|
#expect(shop.slug == "maras-longbox")
|
|
#expect(shop.kind == .privateSeller)
|
|
// A private seller shows no registration, whatever was typed.
|
|
#expect(shop.kvkNumber == nil)
|
|
#expect(!shop.isVerified)
|
|
#expect(shop.ratedSales == 0)
|
|
#expect(owned == shop)
|
|
|
|
await #expect(throws: ShopError.alreadyOwner) {
|
|
try await catalogue.openShop(draft, for: mara)
|
|
}
|
|
await #expect(throws: ShopError.handleTaken) {
|
|
try await catalogue.openShop(draft, for: sam)
|
|
}
|
|
|
|
// And the new shop sells: its owner lists a copy, which a buyer finds.
|
|
let listing = try await catalogue.publish(ListingDraftTests.draft(), by: mara)
|
|
|
|
#expect(listing?.shop.slug == "maras-longbox")
|
|
}
|
|
}
|
|
|
|
}
|