Added the catalogue models and seed to support phase 1 of the POC prototype in the Persistence package.
This commit is contained in:
1 parent
717df5208c
commit
6d7ab4bcba
19 files changed
+1381
-151
No files matched your search
@@ -82,13 +82,13 @@ struct ServiceTests {
|
||||
try await fluent.shutdown()
|
||||
}
|
||||
|
||||
@Test("in-memory: migrate, insert, read back")
|
||||
@Test("in-memory: migrate, seed, read back")
|
||||
func inMemoryRoundTrip() async throws {
|
||||
try await roundTrip(driver: .inMemory)
|
||||
}
|
||||
|
||||
@Test(
|
||||
"postgres: migrate, insert, read back",
|
||||
"postgres: migrate, seed, read back",
|
||||
.enabled(if: postgresDriver != nil)
|
||||
)
|
||||
func postgresRoundTrip() async throws {
|
||||
@@ -104,7 +104,7 @@ struct ServiceTests {
|
||||
|
||||
private extension ServiceTests {
|
||||
|
||||
/// Migrates, inserts, and reads back a record against the given driver, shutting the pool down after.
|
||||
/// Migrates, seeds, and reads the catalogue back against the given driver, shutting the pool down after.
|
||||
///
|
||||
/// The `Fluent` service normally owns pool shutdown via its `run()` in the service group; outside that,
|
||||
/// the test must shut it down explicitly — even on failure — or the pool asserts on `deinit`.
|
||||
@@ -129,10 +129,9 @@ private extension ServiceTests {
|
||||
|
||||
try await fluent.migrate()
|
||||
|
||||
let repository = ExampleRepository(fluent: fluent)
|
||||
let created = try await repository.create(name: "site")
|
||||
let repository = CatalogueRepository(fluent: fluent)
|
||||
|
||||
#expect(try await repository.all().contains(created))
|
||||
#expect(try await !repository.listings().isEmpty)
|
||||
|
||||
if revertAfter {
|
||||
try await fluent.revert()
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import Testing
|
||||
|
||||
@testable import Persistence
|
||||
|
||||
@Suite(
|
||||
"ListingFilter model",
|
||||
.tags(.model)
|
||||
)
|
||||
struct ListingFilterTests {
|
||||
|
||||
// MARK: Methods tests
|
||||
|
||||
@Test
|
||||
func `matches every listing by default`() {
|
||||
#expect(slugs(.init()).count == Seed.listings.count)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `matches every word of the query across title, publisher, and shop`() {
|
||||
#expect(slugs(.init(query: "NIGHTWARD kestrel")) == [
|
||||
"nightward-1-cgc-9-8", "nightward-the-first-year-hc", "nightward-2-cgc-9-6",
|
||||
])
|
||||
#expect(slugs(.init(query: "no such book")).isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `matches the given formats`() {
|
||||
#expect(slugs(.init(formats: ["manga"])) == ["hollow-comet-vol-4", "solar-divide-vol-1", "hollow-comet-vol-1"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func `matches slabbed or raw copies`() {
|
||||
#expect(slugs(.init(isSlabbed: true)).count == 5)
|
||||
#expect(slugs(.init(formats: ["single-issues"], isSlabbed: false)).count == 8)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `leaves ungraded listings out of a grade range`() {
|
||||
#expect(slugs(.init(gradeMin: 9.6)) == ["nightward-1-cgc-9-8", "paper-tigers-1-variant-b", "nightward-2-cgc-9-6"])
|
||||
#expect(slugs(.init(gradeMax: 7)) == ["vermilion-lake-7-cgc-6-5"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func `matches a price range in cents`() {
|
||||
#expect(slugs(.init(priceMin: 10_000, priceMax: 20_000)) == [
|
||||
"nightward-1-cgc-9-4", "nightward-1-raw-nm", "ferrous-saint-1-cbcs-9-0",
|
||||
])
|
||||
}
|
||||
|
||||
@Test
|
||||
func `matches where a listing ships from`() {
|
||||
#expect(slugs(.init(shipsFrom: .nl)).count == 9)
|
||||
#expect(slugs(.init(shipsFrom: .eu)).count == Seed.listings.count - 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `matches the remaining flags`() {
|
||||
#expect(slugs(.init(verifiedShops: true)).count == Seed.listings.count - 3)
|
||||
#expect(slugs(.init(keysOnly: true)).count == 8)
|
||||
#expect(slugs(.init(firstPrintOnly: true)).count == Seed.listings.count - 4)
|
||||
#expect(slugs(.init(acceptsOffers: true)).count == 7)
|
||||
#expect(slugs(.init(publishers: ["Cutwater Press"])) == ["paper-tigers-1-variant-b", "paper-tigers-2"])
|
||||
#expect(slugs(.init(shop: "silverfish-comics")).count == 4)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `orders ungraded listings last by grade`() {
|
||||
let grades = Seed.listings
|
||||
.sorted(by: ListingFilter(sort: .gradeDescending).ordersBefore)
|
||||
.map(\.grade)
|
||||
|
||||
#expect(grades.first == 9.8)
|
||||
#expect(grades.suffix(6).allSatisfy { $0 == nil })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension ListingFilterTests {
|
||||
|
||||
/// The slugs of the demo listings the filter matches, in seed order.
|
||||
func slugs(
|
||||
_ filter: ListingFilter
|
||||
) -> [String] {
|
||||
Seed.listings
|
||||
.filter(filter.matches)
|
||||
.map(\.slug)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import HummingbirdFluent
|
||||
import Logging
|
||||
import Testing
|
||||
|
||||
@testable import Persistence
|
||||
|
||||
@Suite(
|
||||
"CatalogueRepository repository",
|
||||
.tags(.repository)
|
||||
)
|
||||
struct CatalogueRepositoryTests {
|
||||
|
||||
// MARK: Methods tests
|
||||
|
||||
@Test
|
||||
func `seeds every demo shop and listing`() async throws {
|
||||
try await withCatalogue { repository in
|
||||
let listings = try await repository.listings()
|
||||
|
||||
#expect(listings.count == Seed.listings.count)
|
||||
|
||||
for shop in Seed.shops {
|
||||
let found = try await repository.shop(slug: shop.slug)
|
||||
|
||||
#expect(found == shop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `lists the newest listing first by default`() async throws {
|
||||
try await withCatalogue { repository in
|
||||
let listings = try await repository.listings()
|
||||
|
||||
#expect(listings.map(\.slug) == Seed.listings.map(\.slug))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `finds a listing with its shop by slug`() async throws {
|
||||
try await withCatalogue { repository in
|
||||
let found = try await repository.listing(slug: "nightward-1-cgc-9-8")
|
||||
let missing = try await repository.listing(slug: "missing")
|
||||
let listing = try #require(found)
|
||||
|
||||
#expect(listing.title == "Nightward #1")
|
||||
#expect(listing.shop.name == "Kestrel Row Comics")
|
||||
#expect(missing == nil)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `lists each publisher once, alphabetically`() async throws {
|
||||
try await withCatalogue { repository in
|
||||
let publishers = try await repository.publishers()
|
||||
|
||||
#expect(publishers == [
|
||||
"Blackline", "Cutwater Press", "Harbour & Vale", "Meridian Comics", "Sōkai Books",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `narrows and orders the listings by the filter`() async throws {
|
||||
try await withCatalogue { repository in
|
||||
let listings = try await repository.listings(matching: .init(
|
||||
query: "nightward 1",
|
||||
sort: .priceAscending
|
||||
))
|
||||
|
||||
#expect(listings.map(\.priceCents) == [3_500, 12_000, 15_000, 24_000])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private extension CatalogueRepositoryTests {
|
||||
|
||||
/// Runs the body against a migrated, seeded in-memory catalogue, shutting the pool down after.
|
||||
func withCatalogue(
|
||||
_ body: (CatalogueRepository) async throws -> Void
|
||||
) async throws {
|
||||
let fluent = try Service(
|
||||
driver: .inMemory,
|
||||
logger: Logger(label: "test")
|
||||
)()
|
||||
|
||||
do {
|
||||
await PrepareDB()(for: fluent)
|
||||
|
||||
try await fluent.migrate()
|
||||
try await body(.init(fluent: fluent))
|
||||
} catch {
|
||||
try? await fluent.shutdown()
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
try await fluent.shutdown()
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user