import Foundation import Hummingbird import HummingbirdTesting import Logging import NIOCore import Persistence import Testing @testable import WebsiteLibrary @Suite( "AccountController and CollectingController controllers", .tags(.controller) ) struct AccountAndCollectingControllerTests { // MARK: Story tests @Test func `takes a stranger from joining to a shop of their own, with no seeded account involved`() async throws { try await withApp { client in // Joining leads to the first-sign-in screen, which carries the intent on. let (cookie, welcome) = try await client.execute( uri: "/sign-in", method: .post, headers: Self.form, body: ByteBuffer(string: "email=ana%40example.com&return=/sell/open-a-shop") ) { response in (String(try #require(response.headers[.setCookie]).prefix { $0 != ";" }), try #require(response.headers[.location])) } #expect(welcome == "/welcome?return=/sell/open-a-shop") let tooShort = try await post(client, "/welcome", "name=A&return=/sell/open-a-shop", as: cookie, expecting: .unprocessableContent) let named = try await post(client, "/welcome", "name=Ana+de+Vries&return=/sell/open-a-shop", as: cookie) #expect(tooShort == nil) #expect(named == "/sell/open-a-shop") try await client.execute(uri: "/sell/open-a-shop", method: .get, headers: [.cookie: cookie]) { response in let body = String(buffer: response.body) #expect(response.status == .ok) #expect(body.contains(#"aria-label="Account of Ana de Vries""#)) #expect(body.contains(">Open a shop<")) } let shop = "name=Ana%27s+Longbox&handle=anas-longbox&city=Leiden&country=NL&kind=private&returns=14-day+returns&handling=2&declaration=1" let taken = try await post(client, "/sell/open-a-shop", shop.replacingOccurrences(of: "anas-longbox", with: "kestrel-row-comics"), as: cookie, expecting: .unprocessableContent) let opened = try await post(client, "/sell/open-a-shop", shop, as: cookie) #expect(taken == nil) // Straight on to the first listing. #expect(opened == "/sell/new") try await client.execute(uri: "/shop/anas-longbox", method: .get) { response in #expect(response.status == .ok) #expect(String(buffer: response.body).contains("Ana's Longbox") || String(buffer: response.body).contains("Ana's Longbox")) } // One shop per account: the form now leads to the dashboard. try await client.execute(uri: "/sell/open-a-shop", method: .get, headers: [.cookie: cookie]) { response in #expect(response.headers[.location] == "/sell") } } } // MARK: Account tests @Test func `renames the account in the session it is signed in with`() async throws { try await withApp { client in let mara = try await signIn(client) let location = try await post(client, "/account/name", "name=Mara+V.", as: mara) #expect(location == "/account") try await client.execute(uri: "/account", method: .get, headers: [.cookie: mara]) { response in let body = String(buffer: response.body) #expect(body.contains(#"aria-label="Account of Mara V.""#)) #expect(body.contains("No passkey yet")) } let blank = try await post(client, "/account/name", "name=+", as: mara, expecting: .unprocessableContent) #expect(blank == nil) } } @Test(arguments: ["/account", "/welcome", "/wants", "/wants/new", "/sell/open-a-shop"]) func `keeps an account's screens to a signed-in account`( uri: String ) async throws { try await withApp { client in try await client.execute(uri: uri, method: .get) { response in #expect(response.status == .found) #expect(response.headers[.location]?.hasPrefix("/sign-in") == true) } } } // MARK: Wants tests @Test func `rings the header's bell for a new match, until the want list is looked at`() async throws { try await withApp { client in let mara = try await signIn(client) _ = try await post(client, "/wants", "title=Nightward+%231&min-grade=9.0&max-price=300", as: mara) try await client.execute(uri: "/search", method: .get, headers: [.cookie: mara]) { response in // The slabbed 9.8 and the raw NM, within the EU; the 9.4 ships from the UK. #expect(String(buffer: response.body).contains(#"aria-label="Want list, 2 new matches""#)) } let id = try await client.execute(uri: "/wants", method: .get, headers: [.cookie: mara]) { response in let body = String(buffer: response.body) #expect(body.contains(#"class="wants__new">2 new"#)) #expect(body.contains("grade ≥ 9.0 · max €300 · ships from EU only")) return String(try #require(body.firstMatch(of: /\/wants\/([0-9a-f-]{36})\/remove/)).output.1) } try await client.execute(uri: "/search", method: .get, headers: [.cookie: mara]) { response in #expect(String(buffer: response.body).contains(#"aria-label="Want list""#)) } // Another account can neither silence nor remove it. let sam = try await signIn(client, email: "sam@example.com") _ = try await post(client, "/wants/\(id)/remove", "", as: sam) try await client.execute(uri: "/wants", method: .get, headers: [.cookie: mara]) { response in #expect(String(buffer: response.body).contains("Nightward #1")) } _ = try await post(client, "/wants/\(id)/alerts", "", as: mara) _ = 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("No wants yet")) } } } @Test func `fills the want form from a copy being saved, or a gap in a series`() async throws { try await withApp { client in let mara = try await signIn(client) try await client.execute(uri: "/wants/new?listing=nightward-2-cgc-9-6", method: .get, headers: [.cookie: mara]) { response in let body = String(buffer: response.body) #expect(body.contains(#"name="title" value="Nightward #2""#)) #expect(body.contains(#"name="max-price" value="85""#)) #expect(body.contains(#"