Optimizations for the Website service (#9)
This PR contains the work done to provide optimizations to the current service, such as a health-check endpoint, pre-renders static HTML pages, and hardens the error page's CSP. To provide further details about the work: * Added the `HealthController` controller serving GET `/health` with a static JSON payload. * Added the `CachedHTMLResponse` response, which renders a static HTMLDocument to bytes once and reuses them per request (no Content-Length, so responses stay compressible). * Integrated the response into the `RootController` and the `NotFoundMiddleware` middleware to avoid re-rendering on hot paths. * Added a `RouterMethods.addRoutes(_:)` extension and switched the router in App+build to use it. * Moved the inline style from the `ErrorPage` page into a dedicated style file so the CSP needs no inline-style escape hatch. * Fixed the `IndexPage` page path inconsistencies. * Written the `README` file. Reviewed-on: rock-n-code/loud-amsterdam#9 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit is contained in:
@@ -82,7 +82,7 @@ struct StaticFileTests {
|
||||
|
||||
@Test
|
||||
func `all cases`() {
|
||||
#expect(File.allCases.count == 7)
|
||||
#expect(File.allCases.count == 8)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,6 +95,7 @@ private extension StaticFileTests {
|
||||
|
||||
static let contentTypes: [String] = [
|
||||
"text/javascript",
|
||||
"text/css",
|
||||
"image/vnd.microsoft.icon",
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
@@ -104,6 +105,7 @@ private extension StaticFileTests {
|
||||
]
|
||||
static let fileExtensions: [FileExtension] = [
|
||||
.js,
|
||||
.css,
|
||||
.ico,
|
||||
.png,
|
||||
.svg,
|
||||
@@ -113,6 +115,7 @@ private extension StaticFileTests {
|
||||
]
|
||||
static let fileNames: [String] = [
|
||||
"app",
|
||||
"error",
|
||||
"favicon",
|
||||
"icon",
|
||||
"icon",
|
||||
@@ -122,6 +125,7 @@ private extension StaticFileTests {
|
||||
]
|
||||
static let relativePaths: [String] = [
|
||||
"js/app.js",
|
||||
"css/error.css",
|
||||
"favicon.ico",
|
||||
"icon.png",
|
||||
"icon.svg",
|
||||
|
||||
@@ -15,6 +15,7 @@ struct ErrorPageTests {
|
||||
#expect(html.contains("<!DOCTYPE html>"))
|
||||
#expect(html.contains("Page Not Found"))
|
||||
#expect(html.contains("does not exist"))
|
||||
#expect(html.contains("/css/error.css"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,9 +13,13 @@ struct IndexPageTests {
|
||||
let html = IndexPage().render()
|
||||
|
||||
#expect(html.contains("<!DOCTYPE html>"))
|
||||
#expect(html.contains("/css/style.css"))
|
||||
#expect(html.contains("/favicon.ico"))
|
||||
#expect(html.contains("/icon.svg"))
|
||||
#expect(html.contains("/icon.png"))
|
||||
#expect(html.contains("/site.webmanifest"))
|
||||
#expect(html.contains("Hello world!"))
|
||||
#expect(html.contains("css/style.css"))
|
||||
#expect(html.contains("js/app.js"))
|
||||
#expect(html.contains("/js/app.js"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import Hummingbird
|
||||
import HummingbirdTesting
|
||||
import NIOCore
|
||||
import Testing
|
||||
|
||||
@testable import WebsiteCore
|
||||
|
||||
@Suite("HealthController controller")
|
||||
struct HealthControllerTests {
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
private let app: Application = .init(router: {
|
||||
let router = Router()
|
||||
|
||||
router.addRoutes(HealthController<BasicRequestContext>().routes)
|
||||
|
||||
return router
|
||||
}())
|
||||
|
||||
// MARK: Functional tests
|
||||
|
||||
@Test
|
||||
func `serves the status payload at the health path`() async throws {
|
||||
try await app.test(.router) { client in
|
||||
try await client.execute(
|
||||
uri: "/health",
|
||||
method: .get
|
||||
) { response in
|
||||
let body = String(buffer: response.body)
|
||||
|
||||
#expect(response.status == .ok)
|
||||
#expect(response.headers[.contentType] == "application/json")
|
||||
#expect(body == #"{"status":"ok"}"#)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user