Initial commit.

This commit is contained in:
2026-08-19 23:19:08 +02:00
commit 22e737d9c2
153 changed files with 11680 additions and 0 deletions
@@ -0,0 +1,72 @@
import Foundation
import HTTPTypes
import Hummingbird
import HummingbirdTesting
import NIOCore
import Testing
@testable import Infrastructure
@Suite(
"LocalizationMiddleware middleware",
.tags(.middleware)
)
struct LocalizationMiddlewareTests {
// MARK: Constants
private let app: Application = .init(router: {
let router = Router(context: StubRequestContext.self)
router.addMiddleware {
LocalizationMiddleware(bundle: .module)
}
router.get("language") { _, context in
context.language
}
return router
}())
// MARK: Functional tests
@Test
func `negotiates a supported language from the header`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/language",
method: .get,
headers: [.acceptLanguage: "de-DE,de;q=0.9"]
) { response in
#expect(String(buffer: response.body) == "de")
}
}
}
@Test
func `falls back to the default without a header`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/language",
method: .get
) { response in
#expect(String(buffer: response.body) == "en")
}
}
}
@Test
func `falls back to the default for an unsupported language`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/language",
method: .get,
headers: [.acceptLanguage: "fr-FR,fr;q=0.9"]
) { response in
#expect(String(buffer: response.body) == "en")
}
}
}
}
@@ -0,0 +1,194 @@
import Foundation
import Hummingbird
import HummingbirdTesting
import NIOCore
import Testing
@testable import Infrastructure
@Suite(
"NotFoundMiddleware middleware",
.tags(.middleware)
)
struct NotFoundMiddlewareTests {
// MARK: Constants
private let app: Application = .init(router: {
let router = Router(context: StubRequestContext.self)
router.addMiddleware {
LocalizationMiddleware(bundle: .module)
NotFoundMiddleware(bundle: .module) {
StubPage(locale: $0)
}
}
router.get("hello") { _, _ in
"Hello!"
}
router.get("boom") { _, _ -> String in
throw HTTPError(.badRequest)
}
return router
}())
// MARK: Functional tests
@Test
func `renders the error page for an unmatched request`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/this-path-does-not-exist",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(response.status == .notFound)
#expect(response.headers[.contentType] == "text/html; charset=utf-8")
#expect(response.headers[.contentLanguage] == "en")
#expect(response.headers[.vary] == "Accept-Language")
#expect(body.contains("Stub content"))
}
}
}
@Test
func `renders the error page in the negotiated language`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/this-path-does-not-exist",
method: .get,
headers: [.acceptLanguage: "de-DE,de;q=0.9"]
) { response in
#expect(response.status == .notFound)
#expect(response.headers[.contentLanguage] == "de")
}
}
}
@Test
func `passes a matched response through untouched`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .ok)
#expect(response.body == ByteBuffer(string: "Hello!"))
}
}
}
@Test
func `rethrows a non-not-found error unchanged`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/boom",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(response.status == .badRequest)
#expect(!body.contains("Stub content"))
}
}
}
@Test
func `renders versioned asset URLs when given a version`() async throws {
try await app(
assetVersion: "0123456789abcdef"
).test(.router) { client in
try await client.execute(
uri: "/this-path-does-not-exist",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(body.contains("/css/stub.css?v=0123456789abcdef"))
#expect(body.contains("/js/stub.js?v=0123456789abcdef"))
}
}
}
@Test
func `renders unversioned asset URLs by default`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/this-path-does-not-exist",
method: .get
) { response in
let body = String(buffer: response.body)
#expect(body.contains(#"href="/css/stub.css""#))
#expect(!body.contains("?v="))
}
}
}
@Test
func `serves the error page without revalidation headers`() async throws {
// A `304 Not Modified` only ever stands in for a success, so the error page must not
// invite revalidation with an entity tag or a cache policy.
try await app.test(.router) { client in
try await client.execute(
uri: "/this-path-does-not-exist",
method: .get
) { response in
#expect(response.status == .notFound)
#expect(response.headers[.eTag] == nil)
#expect(response.headers[.cacheControl] == nil)
}
}
}
@Test
func `serves the full error page to a conditional request`() async throws {
try await app.test(.router) { client in
try await client.execute(
uri: "/this-path-does-not-exist",
method: .get,
headers: [.ifNoneMatch: "*"]
) { response in
let body = String(buffer: response.body)
#expect(response.status == .notFound)
#expect(body.contains("Stub content"))
}
}
}
}
// MARK: - Helpers
private extension NotFoundMiddlewareTests {
// MARK: Methods
/// Builds an application whose not-found middleware appends the given version token to the
/// error page's asset URLs.
/// - Parameter assetVersion: the version token appended to the page's asset URLs.
/// - Returns: the configured application.
func app(
assetVersion: String?
) -> some ApplicationProtocol {
let router = Router(context: StubRequestContext.self)
router.addMiddleware {
LocalizationMiddleware(bundle: .module)
NotFoundMiddleware(bundle: .module) {
StubPage(
locale: $0,
assetVersion: assetVersion
)
}
}
return Application(router: router)
}
}
@@ -0,0 +1,173 @@
import Hummingbird
import HummingbirdTesting
import Testing
@testable import Infrastructure
@Suite(
"RateLimitMiddleware middleware",
.tags(.middleware)
)
struct RateLimitMiddlewareTests {
// MARK: Functional tests
@Test
func `admits requests within the limit`() async throws {
try await app(
configuration: .init(limit: 3)
).test(.router) { client in
for _ in 1 ... 3 {
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .ok)
}
}
}
}
@Test
func `rejects a request over the limit with a retry-after header`() async throws {
try await app(
configuration: .init(limit: 2)
).test(.router) { client in
for _ in 1 ... 2 {
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .ok)
}
}
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .tooManyRequests)
let retryAfter = try #require(response.headers[.retryAfter])
#expect(try #require(Int(retryAfter)) >= 1)
}
}
}
@Test
func `admits requests again once the window resets`() async throws {
try await app(
configuration: .init(
limit: 1,
window: .milliseconds(50)
)
).test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .ok)
}
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .tooManyRequests)
}
try await Task.sleep(for: .milliseconds(100))
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .ok)
}
}
}
@Test
func `separates clients by their forwarded address when trusted`() async throws {
try await app(
configuration: .init(
limit: 1,
trustForwardedFor: true
)
).test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get,
headers: [.xForwardedFor: "203.0.113.7"]
) { response in
#expect(response.status == .ok)
}
try await client.execute(
uri: "/hello",
method: .get,
headers: [.xForwardedFor: "203.0.113.8"]
) { response in
#expect(response.status == .ok)
}
// The first entry names the client; the appended proxy hop must not change its key.
try await client.execute(
uri: "/hello",
method: .get,
headers: [.xForwardedFor: "203.0.113.7, 10.0.0.1"]
) { response in
#expect(response.status == .tooManyRequests)
}
}
}
@Test
func `ignores the forwarded address when not trusted`() async throws {
try await app(
configuration: .init(limit: 1)
).test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get,
headers: [.xForwardedFor: "203.0.113.7"]
) { response in
#expect(response.status == .ok)
}
// Without trust (and without a connection address in router-only testing), every client
// shares one bucket, so a rotated header must not mint a fresh budget.
try await client.execute(
uri: "/hello",
method: .get,
headers: [.xForwardedFor: "203.0.113.8"]
) { response in
#expect(response.status == .tooManyRequests)
}
}
}
}
// MARK: - Helpers
private extension RateLimitMiddlewareTests {
// MARK: Methods
/// Builds an application whose router applies the rate-limit middleware ahead of a single
/// `/hello` route returning a plain body.
func app(
configuration: RateLimitMiddleware<BasicRequestContext>.Configuration
) -> some ApplicationProtocol {
let router = Router()
router.addMiddleware {
RateLimitMiddleware(configuration: configuration)
}
router.get("hello") { _, _ in
"Hello!"
}
return Application(router: router)
}
}
@@ -0,0 +1,159 @@
import Hummingbird
import HummingbirdTesting
import Testing
@testable import Infrastructure
@Suite(
"SecurityHeadersMiddleware middleware",
.tags(.middleware)
)
struct SecurityHeadersMiddlewareTests {
// MARK: Functional tests
@Test
func `applies the default security headers to a response`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.status == .ok)
#expect(response.headers[.contentSecurityPolicy] == .Security.contentSecurityPolicy)
#expect(response.headers[.xContentTypeOptions] == .Security.contentTypeOptions)
#expect(response.headers[.frameOptions] == .Security.frameOptions)
#expect(response.headers[.referrerPolicy] == .Security.referrerPolicy)
#expect(response.headers[.permissionsPolicy] == .Security.permissionsPolicy)
}
}
}
@Test
func `omits strict-transport-security by default`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.headers[.strictTransportSecurity] == nil)
}
}
}
@Test
func `applies strict-transport-security when configured`() async throws {
let value = "max-age=31536000; includeSubDomains"
try await app(
configuration: .init(strictTransportSecurity: value)
).test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.headers[.strictTransportSecurity] == value)
}
}
}
@Test
func `applies a custom header value`() async throws {
let value = "default-src 'none'"
try await app(
configuration: .init(contentSecurityPolicy: value)
).test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.headers[.contentSecurityPolicy] == value)
}
}
}
@Test
func `omits a header whose configured value is nil`() async throws {
try await app(
configuration: .init(contentTypeOptions: nil)
).test(.router) { client in
try await client.execute(
uri: "/hello",
method: .get
) { response in
#expect(response.headers[.xContentTypeOptions] == nil)
}
}
}
@Test
func `replaces an existing header value set downstream`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/weak",
method: .get
) { response in
#expect(response.headers[.xContentTypeOptions] == .Security.contentTypeOptions)
}
}
}
@Test
func `applies the security headers to an error response`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/throws",
method: .get
) { response in
#expect(response.status == .badRequest)
#expect(response.headers[.contentSecurityPolicy] == .Security.contentSecurityPolicy)
#expect(response.headers[.xContentTypeOptions] == .Security.contentTypeOptions)
#expect(response.headers[.frameOptions] == .Security.frameOptions)
#expect(response.headers[.referrerPolicy] == .Security.referrerPolicy)
#expect(response.headers[.permissionsPolicy] == .Security.permissionsPolicy)
}
}
}
}
// MARK: - Helpers
private extension SecurityHeadersMiddlewareTests {
// MARK: Methods
/// Builds an application whose router applies the security-headers middleware ahead of three
/// routes: `/hello` returns a plain body, `/weak` returns a response that already carries a
/// deliberately weak `X-Content-Type-Options` value for the middleware to override, and
/// `/throws` fails with an `HTTPError` the way the controllers do on invalid input.
func app(
configuration: SecurityHeadersMiddleware<BasicRequestContext>.Configuration = .init()
) -> some ApplicationProtocol {
let router = Router()
router.addMiddleware {
SecurityHeadersMiddleware(configuration: configuration)
}
router.get("hello") { _, _ in
"Hello!"
}
router.get("weak") { _, _ -> Response in
var response = Response(status: .ok)
response.headers[.xContentTypeOptions] = "weak"
return response
}
router.get("throws") { _, _ -> Response in
throw HTTPError(.badRequest)
}
return Application(router: router)
}
}
@@ -0,0 +1,134 @@
import HTTPTypes
import Hummingbird
import HummingbirdTesting
import Testing
@testable import Infrastructure
@Suite(
"VaryMiddleware middleware",
.tags(.middleware)
)
struct VaryMiddlewareTests {
// MARK: Functional tests
@Test
func `adds the default field to a response without a vary header`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/plain",
method: .get
) { response in
#expect(response.status == .ok)
#expect(response.headers[.vary] == "Accept-Encoding")
}
}
}
@Test
func `appends to an existing vary header`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/localized",
method: .get
) { response in
#expect(response.headers[.vary] == "Accept-Language, Accept-Encoding")
}
}
}
@Test
func `does not duplicate a name already present`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/encoded",
method: .get
) { response in
#expect(response.headers[.vary] == "Accept-Encoding")
}
}
}
@Test
func `matches an existing name regardless of its casing`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/lowercased",
method: .get
) { response in
#expect(response.headers[.vary] == "accept-encoding")
}
}
}
@Test
func `normalizes the whitespace of an existing list`() async throws {
try await app().test(.router) { client in
try await client.execute(
uri: "/spaced",
method: .get
) { response in
#expect(response.headers[.vary] == "Accept-Language, User-Agent, Accept-Encoding")
}
}
}
@Test
func `appends every configured field`() async throws {
try await app(
fields: [.acceptEncoding, .acceptLanguage]
).test(.router) { client in
try await client.execute(
uri: "/plain",
method: .get
) { response in
#expect(response.headers[.vary] == "Accept-Encoding, Accept-Language")
}
}
}
}
// MARK: - Helpers
private extension VaryMiddlewareTests {
// MARK: Methods
/// Builds an application whose router applies the vary middleware ahead of routes whose
/// responses carry different `Vary` starting points: `/plain` none, `/localized` an
/// `Accept-Language`, `/encoded` an `Accept-Encoding` already, `/lowercased` a lowercase
/// `accept-encoding`, and `/spaced` a list with irregular whitespace.
func app(
fields: [HTTPField.Name] = [.acceptEncoding]
) -> some ApplicationProtocol {
let router = Router()
router.addMiddleware {
VaryMiddleware(fields: fields)
}
router.get("plain") { _, _ in
"Hello!"
}
for (path, vary) in [
("localized", "Accept-Language"),
("encoded", "Accept-Encoding"),
("lowercased", "accept-encoding"),
("spaced", "Accept-Language , User-Agent"),
] {
router.get(RouterPath(path)) { _, _ -> Response in
var response = Response(status: .ok)
response.headers[.vary] = vary
return response
}
}
return Application(router: router)
}
}