This PR contains the work done to rename the _Web_ package as _Infrastructure_, to provide a clear naming and purpose to this particular package within the project. To provide further details about the work: * Infrastructure * Asset fingerprinting: an FNV-1a token derived from the static files directory, appended as ?v= to asset URLs so deploys bust caches; pre-rendered pages also revalidate via weak ETags. * New middlewares: fixed-window RateLimitMiddleware (per-client budgets keyed by trusted X-Forwarded-For or remote address) and VaryMiddleware (Accept-Encoding on every response); SecurityHeadersMiddleware now also stamps error responses. * Auto-generated HEAD endpoints, cache max-age configuration, and Docker build/Compose refinements. * Protocols and scaffolding: Asset/AssetExtension, the Page protocol (viewport, stylesheets, scripts, versioned URLs), and LocalizedRequestContext. * Rate limiter's counter store swapped from an actor to a Mutex (no executor hop per request) with amortized batch eviction instead of O(n²) scans under client floods. * FingerprintAssets reports unreadable files to a logger instead of silently producing a token that never busts their cache. Reviewed-on: rock-n-code/loud-amsterdam#25 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
72 lines
1.3 KiB
Swift
72 lines
1.3 KiB
Swift
import Testing
|
|
|
|
@testable import Infrastructure
|
|
|
|
@Suite("AssetExtension enumeration")
|
|
struct AssetExtensionTests {
|
|
|
|
// MARK: Computed tests
|
|
|
|
@Test(arguments: zip(
|
|
Self.extensions,
|
|
Self.contentTypes
|
|
))
|
|
func `content type`(
|
|
for fileExtension: AssetExtension,
|
|
expects contentType: String
|
|
) {
|
|
#expect(fileExtension.contentType == contentType)
|
|
}
|
|
|
|
@Test(arguments: zip(
|
|
Self.extensions,
|
|
Self.subdirectories
|
|
))
|
|
func `subdirectory`(
|
|
for fileExtension: AssetExtension,
|
|
expects subdirectory: String?
|
|
) {
|
|
#expect(fileExtension.subdirectory == subdirectory)
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private extension AssetExtensionTests {
|
|
|
|
// MARK: Constants
|
|
|
|
static let extensions: [AssetExtension] = [
|
|
.css,
|
|
.js,
|
|
.png,
|
|
.ico,
|
|
.svg,
|
|
.txt,
|
|
.webmanifest,
|
|
.xml
|
|
]
|
|
static let contentTypes: [String] = [
|
|
"text/css",
|
|
"text/javascript",
|
|
"image/png",
|
|
"image/vnd.microsoft.icon",
|
|
"image/svg+xml",
|
|
"text/plain",
|
|
"application/manifest+json",
|
|
"application/xml"
|
|
]
|
|
static let subdirectories: [String?] = [
|
|
"css",
|
|
"js",
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil
|
|
]
|
|
|
|
}
|