Renamed the Web package as Infrastructure (#25)
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>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import Infrastructure
|
||||
import Testing
|
||||
|
||||
@testable import WebsiteLibrary
|
||||
@@ -8,7 +9,6 @@ struct StaticFileTests {
|
||||
// MARK: Type aliases
|
||||
|
||||
typealias File = StaticFile
|
||||
typealias FileExtension = StaticFile.Extension
|
||||
|
||||
// MARK: Computed tests
|
||||
|
||||
@@ -18,7 +18,7 @@ struct StaticFileTests {
|
||||
))
|
||||
func `file extensions`(
|
||||
for file: File,
|
||||
expects extensions: [FileExtension]
|
||||
expects extensions: [AssetExtension]
|
||||
) {
|
||||
#expect(file.fileExtensions == extensions)
|
||||
}
|
||||
@@ -34,81 +34,6 @@ struct StaticFileTests {
|
||||
#expect(file.fileName == fileName)
|
||||
}
|
||||
|
||||
@Test(arguments: zip(
|
||||
Self.extensions,
|
||||
Self.contentTypes
|
||||
))
|
||||
func `content type`(
|
||||
for fileExtension: FileExtension,
|
||||
expects contentType: String
|
||||
) {
|
||||
#expect(fileExtension.contentType == contentType)
|
||||
}
|
||||
|
||||
@Test(arguments: zip(
|
||||
Self.extensions,
|
||||
Self.subdirectories
|
||||
))
|
||||
func `subdirectory`(
|
||||
for fileExtension: FileExtension,
|
||||
expects subdirectory: String?
|
||||
) {
|
||||
#expect(fileExtension.subdirectory == subdirectory)
|
||||
}
|
||||
|
||||
// MARK: Method tests
|
||||
|
||||
@Test(arguments: zip(
|
||||
File.allCases,
|
||||
Self.relativePaths
|
||||
))
|
||||
func `relative path for`(
|
||||
for file: File,
|
||||
expects relativePaths: [String]
|
||||
) {
|
||||
for (fileExtension, relativePath) in zip(file.fileExtensions, relativePaths) {
|
||||
#expect(file.relativePath(for: fileExtension) == relativePath)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: zip(
|
||||
File.allCases,
|
||||
Self.relativePaths
|
||||
))
|
||||
func `url path for`(
|
||||
for file: File,
|
||||
expects relativePaths: [String]
|
||||
) {
|
||||
for (fileExtension, relativePath) in zip(file.fileExtensions, relativePaths) {
|
||||
#expect(file.urlPath(for: fileExtension) == "/\(relativePath)")
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: [
|
||||
"",
|
||||
".",
|
||||
"Resources/Static"
|
||||
])
|
||||
func `path relative to`(
|
||||
_ basePath: String
|
||||
) {
|
||||
for file in File.allCases {
|
||||
for fileExtension in file.fileExtensions {
|
||||
let pathRelativeToBasePath = file.path(
|
||||
relativeTo: basePath,
|
||||
for: fileExtension
|
||||
)
|
||||
let relativePath = file.relativePath(for: fileExtension)
|
||||
|
||||
if basePath.isEmpty {
|
||||
#expect(pathRelativeToBasePath == relativePath)
|
||||
} else {
|
||||
#expect(pathRelativeToBasePath == "\(basePath)/\(relativePath)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CaseIterable tests
|
||||
|
||||
@Test
|
||||
@@ -124,37 +49,7 @@ private extension StaticFileTests {
|
||||
|
||||
// MARK: Constants
|
||||
|
||||
static let extensions: [FileExtension] = [
|
||||
.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
|
||||
]
|
||||
static let fileExtensions: [[FileExtension]] = [
|
||||
static let fileExtensions: [[AssetExtension]] = [
|
||||
[.png],
|
||||
[.css, .js],
|
||||
[.ico],
|
||||
@@ -180,18 +75,5 @@ private extension StaticFileTests {
|
||||
"site",
|
||||
"sitemap"
|
||||
]
|
||||
static let relativePaths: [[String]] = [
|
||||
["apple-touch-icon.png"],
|
||||
["css/error.css", "js/error.js"],
|
||||
["favicon.ico"],
|
||||
["icon.svg"],
|
||||
["icon-192.png"],
|
||||
["icon-512.png"],
|
||||
["css/index.css", "js/index.js"],
|
||||
["robots.txt"],
|
||||
["css/shared.css", "js/shared.js"],
|
||||
["site.webmanifest"],
|
||||
["sitemap.xml"]
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user