Dated the demo cons in the Persistence package from the weekend after seeding, and re-dated the ones already seeded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e0524da525
commit
3a3d2619ad
6 files changed
+76
-31
No files matched your search
@@ -29,7 +29,7 @@ struct CreateCommunity: AsyncMigration {
|
|||||||
.unique(on: "user_id", "series_slug", "issue")
|
.unique(on: "user_id", "series_slug", "issue")
|
||||||
.create()
|
.create()
|
||||||
|
|
||||||
for (event, shops) in Seed.events {
|
for (event, shops) in Seed.events(after: .init()) {
|
||||||
let record = EventRecord(event)
|
let record = EventRecord(event)
|
||||||
|
|
||||||
try await record.save(on: database)
|
try await record.save(on: database)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import FluentKit
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// Re-dates the demo cons from the first weekend after it runs, for databases seeded when ``CreateCommunity`` used fixed dates.
|
||||||
|
struct RedateEvents: AsyncMigration {
|
||||||
|
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
func prepare(on database: Database) async throws {
|
||||||
|
for (event, _) in Seed.events(after: .init()) {
|
||||||
|
try await EventRecord.query(on: database)
|
||||||
|
.filter(\.$slug == event.slug)
|
||||||
|
.set(\.$startsOn, to: event.startsOn)
|
||||||
|
.set(\.$endsOn, to: event.endsOn)
|
||||||
|
.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func revert(on database: Database) async throws {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -158,29 +158,36 @@ enum Seed {
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
/// The demo cons, soonest first, each with the slugs of the shops trading there.
|
/// The demo cons, soonest first, each with the slugs of the shops trading there, dated from the first weekend after `date`.
|
||||||
static let events: [(event: Event, shops: [String])] = [
|
static func events(
|
||||||
(.init(slug: "dutch-comic-con", name: "Dutch Comic Con", venue: "Jaarbeurs, Utrecht", startsOn: day(2026, 9, 26), endsOn: day(2026, 9, 27)),
|
after date: Date
|
||||||
["kestrel-row-comics", "silverfish-comics", "third-panel-supply"]),
|
) -> [(event: Event, shops: [String])] {
|
||||||
(.init(slug: "stripdagen-haarlem", name: "Stripdagen Haarlem", venue: "Grote Markt, Haarlem", startsOn: day(2026, 10, 3), endsOn: day(2026, 10, 4)),
|
[
|
||||||
["silverfish-comics"]),
|
("dutch-comic-con", "Dutch Comic Con", "Jaarbeurs, Utrecht", 0, ["kestrel-row-comics", "silverfish-comics", "third-panel-supply"]),
|
||||||
(.init(slug: "comic-con-amsterdam", name: "Comic Con Amsterdam", venue: "RAI, Amsterdam", startsOn: day(2026, 11, 21), endsOn: day(2026, 11, 22)),
|
("stripdagen-haarlem", "Stripdagen Haarlem", "Grote Markt, Haarlem", 1, ["silverfish-comics"]),
|
||||||
["kestrel-row-comics", "inkwell-and-staple"]),
|
("comic-con-amsterdam", "Comic Con Amsterdam", "RAI, Amsterdam", 8, ["kestrel-row-comics", "inkwell-and-staple"]),
|
||||||
(.init(slug: "comic-con-brussels", name: "Comic Con Brussels", venue: "Tour & Taxis, Brussels", startsOn: day(2026, 12, 5), endsOn: day(2026, 12, 6)),
|
("comic-con-brussels", "Comic Con Brussels", "Tour & Taxis, Brussels", 10, ["third-panel-supply"]),
|
||||||
["third-panel-supply"]),
|
|
||||||
]
|
]
|
||||||
|
.map { slug, name, venue, week, shops in
|
||||||
|
let (startsOn, endsOn) = weekend(week, after: date)
|
||||||
|
|
||||||
/// Noon on a day in Amsterdam, where the demo cons are dated.
|
return (.init(slug: slug, name: name, venue: venue, startsOn: startsOn, endsOn: endsOn), shops)
|
||||||
static func day(
|
}
|
||||||
_ year: Int,
|
}
|
||||||
_ month: Int,
|
|
||||||
_ day: Int
|
/// Noon on the Saturday and Sunday `week` weeks on from the first weekend after `date`, in Amsterdam, where the demo cons are held.
|
||||||
) -> Date {
|
static func weekend(
|
||||||
|
_ week: Int,
|
||||||
|
after date: Date
|
||||||
|
) -> (startsOn: Date, endsOn: Date) {
|
||||||
var calendar = Calendar(identifier: .gregorian)
|
var calendar = Calendar(identifier: .gregorian)
|
||||||
|
|
||||||
calendar.timeZone = TimeZone(identifier: "Europe/Amsterdam") ?? .gmt
|
calendar.timeZone = TimeZone(identifier: "Europe/Amsterdam") ?? .gmt
|
||||||
|
|
||||||
return calendar.date(from: .init(year: year, month: month, day: day, hour: 12)) ?? .distantPast
|
let saturday = calendar.nextDate(after: date, matching: .init(hour: 12, weekday: 7), matchingPolicy: .nextTime) ?? date
|
||||||
|
let startsOn = calendar.date(byAdding: .weekOfYear, value: week, to: saturday) ?? saturday
|
||||||
|
|
||||||
|
return (startsOn, calendar.date(byAdding: .day, value: 1, to: startsOn) ?? startsOn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The runs the demo listings belong to.
|
/// The runs the demo listings belong to.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public struct PrepareDB: Sendable {
|
|||||||
CreatePasskey(),
|
CreatePasskey(),
|
||||||
CreateCollecting(),
|
CreateCollecting(),
|
||||||
CreateCommunity(),
|
CreateCommunity(),
|
||||||
|
RedateEvents(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-8
@@ -46,13 +46,13 @@ struct CommunityRepositoryTests {
|
|||||||
let clock = Clock()
|
let clock = Clock()
|
||||||
let collecting = CollectingRepository(fluent: fluent, now: clock.now)
|
let collecting = CollectingRepository(fluent: fluent, now: clock.now)
|
||||||
|
|
||||||
// 22 September 2026: every demo con is still to come.
|
// The present, which every demo con is seeded after.
|
||||||
clock.advance(by: Seed.day(2026, 9, 22).timeIntervalSince(clock.now()))
|
clock.advance(by: Date().timeIntervalSince(clock.now()))
|
||||||
|
|
||||||
let all = try await collecting.upcomingEvents()
|
let all = try await collecting.upcomingEvents()
|
||||||
|
|
||||||
// Sunday 27 September, the con's last day: still on. Two weeks later it is over, and so is Stripdagen.
|
// The first con's last day: still on. Two weeks later it is over, and so is Stripdagen.
|
||||||
clock.advance(by: 5 * 86_400)
|
clock.advance(by: try #require(all.first).endsOn.timeIntervalSince(clock.now()))
|
||||||
|
|
||||||
let lastDay = try await collecting.upcomingEvents()
|
let lastDay = try await collecting.upcomingEvents()
|
||||||
|
|
||||||
@@ -67,17 +67,35 @@ struct CommunityRepositoryTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func `dates the demo cons on weekends, from the first one after seeding`() {
|
||||||
|
var calendar = Calendar(identifier: .gregorian)
|
||||||
|
|
||||||
|
calendar.timeZone = TimeZone(identifier: "Europe/Amsterdam") ?? .gmt
|
||||||
|
|
||||||
|
// Monday 21 September 2026: the dates the prototype first shipped with.
|
||||||
|
let days = Seed.events(after: Date(timeIntervalSince1970: 1_790_000_000)).map { entry in
|
||||||
|
[entry.event.startsOn, entry.event.endsOn]
|
||||||
|
.map { calendar.dateComponents([.month, .day, .hour], from: $0) }
|
||||||
|
.map { "\($0.month ?? 0)/\($0.day ?? 0) \($0.hour ?? 0):00" }
|
||||||
|
}
|
||||||
|
|
||||||
|
#expect(days == [
|
||||||
|
["9/26 12:00", "9/27 12:00"],
|
||||||
|
["10/3 12:00", "10/4 12:00"],
|
||||||
|
["11/21 12:00", "11/22 12:00"],
|
||||||
|
["12/5 12:00", "12/6 12:00"],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func `lets a seller pick the events their shop trades at`() async throws {
|
func `lets a seller pick the events their shop trades at`() async throws {
|
||||||
try await withDatabase { fluent in
|
try await withDatabase { fluent in
|
||||||
let clock = Clock()
|
let collecting = CollectingRepository(fluent: fluent)
|
||||||
let collecting = CollectingRepository(fluent: fluent, now: clock.now)
|
|
||||||
let accounts = AccountRepository(fluent: fluent)
|
let accounts = AccountRepository(fluent: fluent)
|
||||||
let joost = try await accounts.account(email: "joost@kestrelrow.example")
|
let joost = try await accounts.account(email: "joost@kestrelrow.example")
|
||||||
let mara = try await accounts.account(email: "mara.v@example.com")
|
let mara = try await accounts.account(email: "mara.v@example.com")
|
||||||
|
|
||||||
clock.advance(by: Seed.day(2026, 9, 22).timeIntervalSince(clock.now()))
|
|
||||||
|
|
||||||
try await collecting.setEvents(["stripdagen-haarlem", "comic-con-amsterdam", "no-such-event"], forShopOwnedBy: joost)
|
try await collecting.setEvents(["stripdagen-haarlem", "comic-con-amsterdam", "no-such-event"], forShopOwnedBy: joost)
|
||||||
// An account without a shop changes nothing.
|
// An account without a shop changes nothing.
|
||||||
try await collecting.setEvents(["dutch-comic-con"], forShopOwnedBy: mara)
|
try await collecting.setEvents(["dutch-comic-con"], forShopOwnedBy: mara)
|
||||||
|
|||||||
+3
-5
@@ -246,9 +246,8 @@ private extension AccountAndCollectingControllerTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the body against the session, catalogue, account, and collecting routes, over a migrated, seeded in-memory database, judging events by `now`.
|
/// Runs the body against the session, catalogue, account, and collecting routes, over a migrated, seeded in-memory database.
|
||||||
func withApp(
|
func withApp(
|
||||||
now: @escaping @Sendable () -> Date = { .init() },
|
|
||||||
_ body: @escaping @Sendable (any TestClientProtocol) async throws -> Void
|
_ body: @escaping @Sendable (any TestClientProtocol) async throws -> Void
|
||||||
) async throws {
|
) async throws {
|
||||||
let fluent = try Service(
|
let fluent = try Service(
|
||||||
@@ -266,7 +265,7 @@ private extension AccountAndCollectingControllerTests {
|
|||||||
|
|
||||||
let accounts = AccountRepository(fluent: fluent)
|
let accounts = AccountRepository(fluent: fluent)
|
||||||
let catalogue = CatalogueRepository(fluent: fluent)
|
let catalogue = CatalogueRepository(fluent: fluent)
|
||||||
let collecting = CollectingRepository(fluent: fluent, now: now)
|
let collecting = CollectingRepository(fluent: fluent)
|
||||||
let sessions = SessionStore(trade: .init(fluent: fluent), collecting: collecting)
|
let sessions = SessionStore(trade: .init(fluent: fluent), collecting: collecting)
|
||||||
let router = Router(context: WebsiteRequestContext.self)
|
let router = Router(context: WebsiteRequestContext.self)
|
||||||
|
|
||||||
@@ -309,8 +308,7 @@ extension AccountAndCollectingControllerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func `serves the events, and narrows a search to the copies to pick up at one`() async throws {
|
func `serves the events, and narrows a search to the copies to pick up at one`() async throws {
|
||||||
// 22 September 2026: every demo con is still to come.
|
try await withApp { client in
|
||||||
try await withApp(now: { Date(timeIntervalSince1970: 1_790_071_200) }) { client in
|
|
||||||
try await client.execute(uri: "/events", method: .get) { response in
|
try await client.execute(uri: "/events", method: .get) { response in
|
||||||
let body = String(buffer: response.body)
|
let body = String(buffer: response.body)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user