Implemented phase 6 for the POC prototype in the Website service.
This commit is contained in:
1 parent
256d22df40
commit
f2f445827b
40 files changed
+1906
-37
No files matched your search
+99
-2
@@ -140,7 +140,7 @@ struct AccountAndCollectingControllerTests {
|
||||
_ = try await post(client, "/wants/\(id)/remove", "", as: mara)
|
||||
|
||||
try await client.execute(uri: "/wants", method: .get, headers: [.cookie: mara]) { response in
|
||||
#expect(String(buffer: response.body).contains("Nothing on your list yet"))
|
||||
#expect(String(buffer: response.body).contains("No wants yet"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,7 @@ private extension AccountAndCollectingControllerTests {
|
||||
let sessions = SessionStore(trade: .init(fluent: fluent), collecting: collecting)
|
||||
let router = Router(context: WebsiteRequestContext.self)
|
||||
|
||||
router.addRoutes(CatalogueController<WebsiteRequestContext>(repository: catalogue, sessions: sessions).routes)
|
||||
router.addRoutes(CatalogueController<WebsiteRequestContext>(repository: catalogue, sessions: sessions, collecting: collecting).routes)
|
||||
router.addRoutes(SessionController<WebsiteRequestContext>(sessions: sessions, accounts: accounts, catalogue: catalogue).routes)
|
||||
router.addRoutes(AccountController<WebsiteRequestContext>(accounts: accounts, catalogue: catalogue, sessions: sessions).routes)
|
||||
router.addRoutes(CollectingController<WebsiteRequestContext>(collecting: collecting, catalogue: catalogue, sessions: sessions).routes)
|
||||
@@ -282,3 +282,100 @@ private extension AccountAndCollectingControllerTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Collection, events, and recovery
|
||||
|
||||
extension AccountAndCollectingControllerTests {
|
||||
|
||||
@Test
|
||||
func `keeps a collection through its form and the switches on a series`() async throws {
|
||||
try await withApp { client in
|
||||
let mara = try await signIn(client)
|
||||
|
||||
_ = try await post(client, "/collection", "series=echo-country&issues=1-3%2C+9", as: mara)
|
||||
_ = try await post(client, "/series/echo-country/own", "issue=5", as: mara)
|
||||
_ = try await post(client, "/series/echo-country/own", "issue=1", as: mara)
|
||||
|
||||
try await client.execute(uri: "/collection", method: .get, headers: [.cookie: mara]) { response in
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
// #2, #3 and #5: #9 is outside the six-issue run, and #1 was switched back off.
|
||||
#expect(body.contains(">3/6<"))
|
||||
#expect(body.contains("3 missing · 1 listed now"))
|
||||
}
|
||||
|
||||
try await client.execute(uri: "/series/echo-country", method: .get, headers: [.cookie: mara]) { response in
|
||||
#expect(String(buffer: response.body).contains("3 of 6 owned"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `serves the events, and narrows a search to the copies to pick up at one`() async throws {
|
||||
try await withApp { client in
|
||||
try await client.execute(uri: "/events", method: .get) { response in
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
#expect(response.status == .ok)
|
||||
#expect(body.contains("Dutch Comic Con"))
|
||||
#expect(body.contains("Comic Con Brussels"))
|
||||
}
|
||||
|
||||
// Silverfish alone trades at Stripdagen.
|
||||
try await client.execute(uri: "/search?pickup=stripdagen-haarlem&ships-from=anywhere", method: .get) { response in
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
#expect(body.contains("4 listings"))
|
||||
#expect(body.contains("from 1 shop"))
|
||||
}
|
||||
|
||||
try await client.execute(uri: "/search?pickup=no-such-event", method: .get) { response in
|
||||
#expect(String(buffer: response.body).contains("No listings match"))
|
||||
}
|
||||
|
||||
try await client.execute(uri: "/listing/nightward-1-cgc-9-8", method: .get) { response in
|
||||
#expect(String(buffer: response.body).contains("or pick up at Dutch Comic Con"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func `hands a recovery link to an account's email alone, good until it runs out`() async throws {
|
||||
try await withApp { client in
|
||||
let link = try await client.execute(
|
||||
uri: "/recover",
|
||||
method: .post,
|
||||
headers: Self.form,
|
||||
body: ByteBuffer(string: "email=Mara.V%40example.com")
|
||||
) { response in
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
#expect(response.status == .ok)
|
||||
#expect(response.headers[.cacheControl] == "no-store")
|
||||
|
||||
return String(try #require(body.firstMatch(of: /\/recover\/[0-9a-f]{64}/)).output)
|
||||
}
|
||||
|
||||
try await client.execute(uri: link, method: .get) { response in
|
||||
#expect(response.status == .ok)
|
||||
#expect(String(buffer: response.body).contains("Set up a new passkey"))
|
||||
}
|
||||
|
||||
try await client.execute(uri: "/recover/0000", method: .get) { response in
|
||||
#expect(response.status == .gone)
|
||||
}
|
||||
|
||||
try await client.execute(uri: "/recover", method: .post, headers: Self.form, body: ByteBuffer(string: "email=nobody%40example.com")) { response in
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
#expect(body.contains("No account has that email"))
|
||||
#expect(body.firstMatch(of: /\/recover\/[0-9a-f]{64}/) == nil)
|
||||
}
|
||||
|
||||
try await client.execute(uri: "/recover", method: .post, headers: Self.form, body: ByteBuffer(string: "email=nope")) { response in
|
||||
#expect(response.status == .unprocessableContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -141,7 +141,11 @@ struct TradeControllerTests {
|
||||
|
||||
// The copy is off sale, for everyone.
|
||||
try await client.execute(uri: listing, method: .get) { response in
|
||||
#expect(String(buffer: response.body).contains("This copy is sold."))
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
#expect(body.contains("This copy sold on "))
|
||||
// Three other copies of Nightward #1 remain listed, none of them in this shop.
|
||||
#expect(body.contains("has 0 more copies of Nightward #1, and 3 are listed across CCN."))
|
||||
}
|
||||
|
||||
// A step out of turn changes nothing.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
import Persistence
|
||||
import Testing
|
||||
|
||||
@testable import WebsiteLibrary
|
||||
|
||||
@Suite(
|
||||
"RecoveryLinks type",
|
||||
.tags(.type)
|
||||
)
|
||||
struct RecoveryLinksTests {
|
||||
|
||||
// MARK: Methods tests
|
||||
|
||||
@Test
|
||||
func `hands out a link that can be looked at, and used once`() async {
|
||||
let links = RecoveryLinks()
|
||||
let token = await links.make(for: .mara)
|
||||
let peeked = await links.user(for: token)
|
||||
let stillGood = await links.user(for: token)
|
||||
let redeemed = await links.redeem(token)
|
||||
let again = await links.redeem(token)
|
||||
|
||||
#expect(token.count == 64)
|
||||
#expect(peeked == .mara)
|
||||
#expect(stillGood == .mara)
|
||||
#expect(redeemed == .mara)
|
||||
#expect(again == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `lets a link run out after an hour, and ends an account's older link`() async {
|
||||
let clock = Clock()
|
||||
let links = RecoveryLinks(now: clock.now)
|
||||
let older = await links.make(for: .mara)
|
||||
let newer = await links.make(for: .mara)
|
||||
let other = await links.make(for: .joost)
|
||||
let replaced = await links.user(for: older)
|
||||
|
||||
clock.advance(by: 3_599)
|
||||
|
||||
let inTime = await links.user(for: newer)
|
||||
|
||||
clock.advance(by: 1)
|
||||
|
||||
let tooLate = await links.redeem(newer)
|
||||
let othersToo = await links.user(for: other)
|
||||
|
||||
#expect(replaced == nil)
|
||||
#expect(inTime == .mara)
|
||||
#expect(tooLate == nil)
|
||||
#expect(othersToo == nil)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user