import Foundation import Testing @testable import Persistence @Suite( "CollectingRepository repository, collection and events", .tags(.repository) ) struct CommunityRepositoryTests { // MARK: Collection tests @Test func `keeps a collection per account, within a series' run`() 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") // Echo Country runs six issues: #9 is outside it, and #2 is given twice. try await collecting.own(issues: [1, 2, 2, 9, 0], ofSeries: "echo-country", by: mara) try await collecting.own(issues: [2, 3], ofSeries: "echo-country", by: mara) try await collecting.own(issues: [1], ofSeries: "no-such-series", by: mara) try await collecting.disown(issue: 1, ofSeries: "echo-country", by: sam) let owned = try await collecting.ownedIssues(of: mara) let others = try await collecting.ownedIssues(of: sam) try await collecting.disown(issue: 1, ofSeries: "echo-country", by: mara) let after = try await collecting.ownedIssues(of: mara) #expect(owned == ["echo-country": [1, 2, 3]]) #expect(others.isEmpty) #expect(after == ["echo-country": [2, 3]]) } } // MARK: Events tests @Test func `lists the events still to come, soonest first, with the shops trading there`() async throws { try await withDatabase { fluent in let clock = Clock() let collecting = CollectingRepository(fluent: fluent, now: clock.now) // 22 September 2026: every demo con is still to come. clock.advance(by: Seed.day(2026, 9, 22).timeIntervalSince(clock.now())) 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. clock.advance(by: 5 * 86_400) let lastDay = try await collecting.upcomingEvents() clock.advance(by: 14 * 86_400) let later = try await collecting.upcomingEvents() #expect(all.map(\.slug) == ["dutch-comic-con", "stripdagen-haarlem", "comic-con-amsterdam", "comic-con-brussels"]) #expect(Set(all[0].shops.map(\.slug)) == ["kestrel-row-comics", "silverfish-comics", "third-panel-supply"]) #expect(lastDay.first?.slug == "dutch-comic-con") #expect(later.first?.slug == "comic-con-amsterdam") } } @Test func `lets a seller pick the events their shop trades at`() async throws { try await withDatabase { fluent in let clock = Clock() let collecting = CollectingRepository(fluent: fluent, now: clock.now) 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") 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) // An account without a shop changes nothing. try await collecting.setEvents(["dutch-comic-con"], forShopOwnedBy: mara) let trading = try await collecting.upcomingEvents() .filter { $0.shops.contains { $0.slug == "kestrel-row-comics" } } .map(\.slug) #expect(trading == ["stripdagen-haarlem", "comic-con-amsterdam"]) } } @Test func `narrows a search to the shops trading at an event`() { let filter = ListingFilter(shops: ["silverfish-comics"]) #expect(Seed.listings.filter(filter.matches).map(\.shop.slug).allSatisfy { $0 == "silverfish-comics" }) #expect(Seed.listings.filter(filter.matches).count == 4) #expect(Seed.listings.filter(ListingFilter(shops: []).matches).isEmpty) } // MARK: Recovery tests @Test func `replaces every passkey of an account with the one it recovers with`() async throws { try await withDatabase { fluent in let accounts = AccountRepository(fluent: fluent) let sam = try #require(try await accounts.join(email: "sam@example.com", credentialID: "old-1", publicKey: [1], signCount: 3)) try await accounts.addPasskey(to: sam, credentialID: "old-2", publicKey: [2], signCount: 0) try await accounts.replacePasskeys(of: sam, credentialID: "new", publicKey: [9], signCount: 0) let passkeys = try await accounts.passkeys(of: sam) let lost = try await accounts.passkey(credentialID: "old-1") #expect(passkeys.map(\.passkey.credentialID) == ["new"]) #expect(lost == nil) } } }