Added the catalogue models and seed to support phase 5 of the POC prototype in the Persistence package.
This commit is contained in:
1 parent
9403931109
commit
89a7a7b8e5
19 files changed
+990
-15
No files matched your search
@@ -91,3 +91,83 @@ extension AccountRepositoryTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@testable import Persistence
|
||||
|
||||
@Suite(
|
||||
"CollectingRepository repository",
|
||||
.tags(.repository)
|
||||
)
|
||||
struct CollectingRepositoryTests {
|
||||
|
||||
// MARK: Wants tests
|
||||
|
||||
@Test
|
||||
func `matches a want within its limits, shipping from the EU alone`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let collecting = CollectingRepository(fluent: fluent)
|
||||
let mara = try await AccountRepository(fluent: fluent).account(email: "mara.v@example.com")
|
||||
|
||||
try await collecting.addWant(title: " Nightward #1 ", minGrade: 9.0, maxPriceCents: 20_000, slabbedOnly: false, for: mara)
|
||||
try await collecting.addWant(title: " ", minGrade: nil, maxPriceCents: nil, slabbedOnly: false, for: mara)
|
||||
|
||||
let wants = try await collecting.wants(of: mara)
|
||||
let want = try #require(wants.first)
|
||||
|
||||
#expect(wants.count == 1)
|
||||
#expect(want.title == "Nightward #1")
|
||||
// The raw NM at €120 from Antwerp; the €150 CGC 9.4 ships from the UK, and the rest cost more or grade lower.
|
||||
#expect(want.matches.map(\.slug) == ["nightward-1-raw-nm"])
|
||||
#expect(want.newMatchCount == 1)
|
||||
#expect(want.alerts)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `counts a match as new until the wants are looked at, and again when a copy is listed`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let clock = Clock()
|
||||
let collecting = CollectingRepository(fluent: fluent, now: clock.now)
|
||||
let accounts = AccountRepository(fluent: fluent)
|
||||
let mara = try await accounts.account(email: "mara.v@example.com")
|
||||
let joost = try await accounts.account(email: "joost@kestrelrow.example")
|
||||
|
||||
try await collecting.addWant(title: "Nightward #3", minGrade: nil, maxPriceCents: nil, slabbedOnly: false, for: mara)
|
||||
|
||||
let nothing = try await collecting.newMatchCount(for: mara)
|
||||
|
||||
_ = try await CatalogueRepository(fluent: fluent).publish(ListingDraftTests.draft(), by: joost, at: clock.now())
|
||||
|
||||
clock.advance(by: 60)
|
||||
|
||||
let listed = try await collecting.newMatchCount(for: mara)
|
||||
|
||||
try await collecting.markWantsSeen(by: mara)
|
||||
|
||||
let seen = try await collecting.newMatchCount(for: mara)
|
||||
let stillMatching = try await collecting.wants(of: mara).first?.matches.count
|
||||
|
||||
#expect(nothing == 0)
|
||||
#expect(listed == 1)
|
||||
#expect(seen == 0)
|
||||
#expect(stillMatching == 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `keeps a want's alerts and removal to its owner`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let collecting = CollectingRepository(fluent: fluent)
|
||||
let accounts = AccountRepository(fluent: fluent)
|
||||
let mara = try await accounts.account(email: "mara.v@example.com")
|
||||
let sam = try await accounts.account(email: "sam@example.com")
|
||||
|
||||
try await collecting.addWant(title: "Nightward #1", minGrade: nil, maxPriceCents: nil, slabbedOnly: true, for: mara)
|
||||
|
||||
let id = try #require(try await collecting.wants(of: mara).first?.id)
|
||||
|
||||
try await collecting.toggleAlerts(ofWant: id, of: sam)
|
||||
try await collecting.removeWant(id, of: sam)
|
||||
|
||||
let untouched = try await collecting.wants(of: mara)
|
||||
|
||||
try await collecting.toggleAlerts(ofWant: id, of: mara)
|
||||
|
||||
let silenced = try await collecting.wants(of: mara)
|
||||
let quiet = try await collecting.newMatchCount(for: mara)
|
||||
|
||||
try await collecting.removeWant(id, of: mara)
|
||||
|
||||
let removed = try await collecting.wants(of: mara)
|
||||
|
||||
#expect(untouched.first?.alerts == true)
|
||||
#expect(untouched.first?.matches.allSatisfy(\.isSlabbed) == true)
|
||||
#expect(silenced.first?.alerts == false)
|
||||
#expect(quiet == 0)
|
||||
#expect(removed.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Series tests
|
||||
|
||||
@Test
|
||||
func `knows the seeded runs, and makes up a series it only knows from its listings`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let collecting = CollectingRepository(fluent: fluent)
|
||||
let all = try await collecting.allSeries()
|
||||
let nightward = try await collecting.series(slug: "nightward")
|
||||
let listedOnly = try await collecting.series(slug: "hollow-comet")
|
||||
let unknown = try await collecting.series(slug: "no-such-series")
|
||||
|
||||
#expect(all.map(\.slug) == ["echo-country", "ferrous-saint", "nightward", "paper-tigers", "vermilion-lake"])
|
||||
#expect(nightward?.issueCount == 24)
|
||||
#expect(listedOnly?.name == "Hollow Comet")
|
||||
#expect(listedOnly?.issueCount == 0)
|
||||
#expect(unknown == nil)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `derives a listing's series and issue number from what it states`() {
|
||||
let single = Seed.listings[0]
|
||||
let manga = Seed.listings[5]
|
||||
|
||||
#expect(single.seriesName == "Nightward")
|
||||
#expect(single.seriesSlug == "nightward")
|
||||
#expect(single.issueNumber == 1)
|
||||
#expect(manga.seriesSlug == "hollow-comet")
|
||||
#expect(manga.issueNumber == nil)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user