Added the catalogue models and seed to support phase 4 of the POC prototype in the Persistence package.
This commit is contained in:
1 parent
0dc3fe6bea
commit
95e0a455e8
14 files changed
+743
-9
No files matched your search
@@ -0,0 +1,137 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@testable import Persistence
|
||||
|
||||
@Suite(
|
||||
"ListingDraft model",
|
||||
.tags(.model)
|
||||
)
|
||||
struct ListingDraftTests {
|
||||
|
||||
// MARK: Computed tests
|
||||
|
||||
@Test
|
||||
func `titles a copy by its format`() {
|
||||
#expect(Self.draft().title == "Nightward #3")
|
||||
#expect(Self.draft(format: "manga").title == "Nightward, Vol. 3")
|
||||
#expect(Self.draft(issue: "", format: "hardcovers").title == "Nightward")
|
||||
}
|
||||
|
||||
@Test
|
||||
func `words the printing short and in full`() {
|
||||
#expect(Self.draft().printingLabel == "1st print")
|
||||
#expect(Self.draft().printingDetail == "First printing, direct edition")
|
||||
#expect(Self.draft(format: "manga", printing: 2, edition: "").printingLabel == "2nd printing")
|
||||
#expect(Self.draft(printing: 0, edition: "").printingDetail == "Later printing")
|
||||
}
|
||||
|
||||
@Test
|
||||
func `letters the placeholder cover over a few lines, toned by the series`() {
|
||||
let cover = Self.draft(series: "The Gilded Hour of Saints", format: "trade-paperbacks").cover
|
||||
|
||||
#expect(cover.title == "THE GILDED\nHOUR OF\nSAINTS")
|
||||
#expect(cover.number == "TPB")
|
||||
#expect(cover.imprint == "MERIDIAN COMICS")
|
||||
#expect(ListingDraft.tones.contains(cover.tone))
|
||||
#expect(Self.draft(issue: "1").cover.tone == Self.draft(issue: "2").cover.tone)
|
||||
#expect(Self.draft(format: "manga").cover.number == "VOL 3")
|
||||
}
|
||||
|
||||
// MARK: Methods tests
|
||||
|
||||
@Test
|
||||
func `publishes as a listing carrying what was declared`() {
|
||||
let listing = Self.draft().listing(slug: "nightward-3-raw-nm", shop: Seed.shops[0], at: .init(timeIntervalSince1970: 0))
|
||||
|
||||
#expect(listing.seriesTitle == "Nightward (2019 series)")
|
||||
#expect(listing.issueLabel == "#3")
|
||||
#expect(listing.isFirstPrint)
|
||||
#expect(listing.gradeLabel == "Raw · NM")
|
||||
#expect(listing.defects == ["Spine stress / ticks"])
|
||||
#expect(listing.photos == ["/uploads/a.jpg"])
|
||||
#expect(listing.declaration == "Declared defects: Spine stress / ticks. Two ticks near the top staple.")
|
||||
#expect(listing.isAvailable)
|
||||
}
|
||||
|
||||
@Test(arguments: zip(
|
||||
["Nightward #1 CGC 9.8", "Hollow Comet, Vol. 4 — Sōkai", " Raw · NM+ ", "???"],
|
||||
["nightward-1-cgc-9-8", "hollow-comet-vol-4-sokai", "raw-nm", ""]
|
||||
))
|
||||
func `slugs a title down to letters, digits, and hyphens`(
|
||||
_ title: String,
|
||||
expects slug: String
|
||||
) {
|
||||
#expect(ListingDraft.slug(title) == slug)
|
||||
}
|
||||
|
||||
// MARK: Repository tests
|
||||
|
||||
@Test
|
||||
func `publishes into the seller's shop, under a slug of its own, and into the search`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let catalogue = CatalogueRepository(fluent: fluent)
|
||||
let joost = try await AccountRepository(fluent: fluent).account(email: "joost@kestrelrow.example")
|
||||
let first = try #require(try await catalogue.publish(Self.draft(), by: joost))
|
||||
let second = try #require(try await catalogue.publish(Self.draft(), by: joost))
|
||||
let found = try await catalogue.listing(slug: first.slug)
|
||||
let newest = try await catalogue.listings().first
|
||||
|
||||
#expect(first.slug == "nightward-3-raw-nm")
|
||||
#expect(second.slug == "nightward-3-raw-nm-2")
|
||||
#expect(first.shop.slug == "kestrel-row-comics")
|
||||
#expect(found?.defects == ["Spine stress / ticks"])
|
||||
#expect(found?.photos == ["/uploads/a.jpg"])
|
||||
#expect(newest?.slug == second.slug)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `publishes nothing for an account without a shop`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let mara = try await AccountRepository(fluent: fluent).account(email: "mara.v@example.com")
|
||||
let listing = try await CatalogueRepository(fluent: fluent).publish(Self.draft(), by: mara)
|
||||
|
||||
#expect(listing == nil)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `freezes the declared defects into a sale`() async throws {
|
||||
try await withDatabase { fluent in
|
||||
let accounts = AccountRepository(fluent: fluent)
|
||||
let joost = try await accounts.account(email: "joost@kestrelrow.example")
|
||||
let mara = try await accounts.account(email: "mara.v@example.com")
|
||||
let listing = try #require(try await CatalogueRepository(fluent: fluent).publish(Self.draft(), by: joost))
|
||||
let trade = TradeRepository(fluent: fluent)
|
||||
let number = try await trade.buy(listing: listing.slug, buyer: mara)
|
||||
let sale = try await trade.sale(number: number, for: mara)
|
||||
|
||||
#expect(sale?.declared.notes == "Declared defects: Spine stress / ticks. Two ticks near the top staple.")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
extension ListingDraftTests {
|
||||
|
||||
/// A draft of a raw Nightward #3 with one declared defect and one photo, unless told otherwise.
|
||||
static func draft(
|
||||
series: String = "Nightward",
|
||||
issue: String = "3",
|
||||
format: String = "single-issues",
|
||||
printing: Int = 1,
|
||||
edition: String = "Direct"
|
||||
) -> ListingDraft {
|
||||
.init(
|
||||
series: series, issue: issue, publisher: "Meridian Comics", year: 2019, format: format,
|
||||
printing: printing, edition: edition, grader: nil, grade: 9.4, condition: "NM", certNumber: nil,
|
||||
pageQuality: nil, keyNotes: nil, defects: ["Spine stress / ticks"],
|
||||
sellerNotes: "Two ticks near the top staple.", priceCents: 3_000, shippingCents: 500,
|
||||
acceptsOffers: true, photos: ["/uploads/a.jpg"]
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,3 +33,61 @@ struct AccountRepositoryTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user