Improved and documented the DocCModilleware middleware in the library target.
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// Test.swift
|
||||
// DocCRepo
|
||||
//
|
||||
// Created by Javier Cicchelli on 08/03/2025.
|
||||
//
|
||||
|
||||
import Hummingbird
|
||||
import HummingbirdTesting
|
||||
import Testing
|
||||
|
||||
@testable import AppLibrary
|
||||
|
||||
@Suite("DocCMiddleware", .tags(.middleware))
|
||||
struct DoccMiddlewareTests {
|
||||
|
||||
@Test(arguments: zip([String].urisRedirect, [String].pathsRedirect))
|
||||
func redirects(
|
||||
from uri: String,
|
||||
to path: String
|
||||
) async throws {
|
||||
// GIVEN
|
||||
let router = Router.test()
|
||||
let app = Application(router: router)
|
||||
|
||||
// WHEN
|
||||
// THEN
|
||||
try await app.test(.router) { client in
|
||||
try await client.execute(uri: uri, method: .get) { response in
|
||||
#expect(response.status == .seeOther)
|
||||
#expect(response.headers[.location] == path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: zip([String].urisServes, [String].identifiersServes))
|
||||
func servesFile(
|
||||
for uri: String,
|
||||
with identifier: String
|
||||
) async throws {
|
||||
// GIVEN
|
||||
var provider = FileProviderMock()
|
||||
|
||||
provider.setFile(identifier: identifier)
|
||||
|
||||
let router = Router.test(assetProvider: provider)
|
||||
let app = Application(router: router)
|
||||
|
||||
// WHEN
|
||||
// THEN
|
||||
try await app.test(.router) { client in
|
||||
try await client.execute(uri: uri, method: .get) { response in
|
||||
#expect(response.status == .ok)
|
||||
#expect(response.body == ByteBuffer(string: identifier))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(arguments: zip([String].urisInvalid, [HTTPResponse.Status].statusesInvalid))
|
||||
func throwError(
|
||||
for uri: String,
|
||||
with status: HTTPResponse.Status
|
||||
) async throws {
|
||||
// GIVEN
|
||||
let router = Router.test()
|
||||
let app = Application(router: router)
|
||||
|
||||
// WHEN
|
||||
// THEN
|
||||
try await app.test(.router) { client in
|
||||
try await client.execute(uri: uri, method: .get) { response in
|
||||
#expect(response.status == status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Router+Constants
|
||||
|
||||
private extension Router<BasicRequestContext> {
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
static func test(assetProvider: some FileProvider = FileProviderMock()) -> Router {
|
||||
let router = Router()
|
||||
|
||||
router.addMiddleware {
|
||||
DocCMiddleware(assetProvider: assetProvider)
|
||||
}
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Collection+String
|
||||
|
||||
private extension Collection where Element == String {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
static var identifiersServes: [String] {[
|
||||
"/SomeArchive.doccarchive/documentation/somearchive/index.html",
|
||||
"/SomeArchive.doccarchive/tutorials/somearchive/index.html",
|
||||
"/SomeArchive.doccarchive/favicon.ico",
|
||||
"/SomeArchive.doccarchive/favicon.svg",
|
||||
"/SomeArchive.doccarchive/theme-settings.json",
|
||||
"/SomeArchive.doccarchive/data/documentation/somearchive.json",
|
||||
"/SomeArchive.doccarchive/css/some-css-file.css",
|
||||
"/SomeArchive.doccarchive/data/some-data-file.bin",
|
||||
"/SomeArchive.doccarchive/downloads/some-download-file",
|
||||
"/SomeArchive.doccarchive/images/some-image-file.jpg",
|
||||
"/SomeArchive.doccarchive/img/some-image-file.png",
|
||||
"/SomeArchive.doccarchive/index/some-index-file",
|
||||
"/SomeArchive.doccarchive/js/some-js-file.js",
|
||||
"/SomeArchive.doccarchive/videos/some-video-file.mp4",
|
||||
]}
|
||||
|
||||
|
||||
static var pathsRedirect: [String] {[
|
||||
"/archives/SomeArchive/documentation",
|
||||
"/archives/SomeArchive/documentation/",
|
||||
"/archives/SomeArchive/tutorials/",
|
||||
]}
|
||||
|
||||
static var urisInvalid: [String] {[
|
||||
"",
|
||||
"some-path",
|
||||
"some/uri/path",
|
||||
"../",
|
||||
"/../",
|
||||
"/archives",
|
||||
"/archives/SomeArchive/favicon.ico",
|
||||
"/archives/SomeArchive/favicon.svg",
|
||||
"/archives/SomeArchive/theme-settings.json",
|
||||
"/archives/SomeArchive/data/documentation.json",
|
||||
"/archives/SomeArchive/css/some-css-file.css",
|
||||
"/archives/SomeArchive/data/some-data-file.bin",
|
||||
"/archives/SomeArchive/downloads/some-download-file",
|
||||
"/archives/SomeArchive/images/some-image-file.jpg",
|
||||
"/archives/SomeArchive/img/some-image-file.png",
|
||||
"/archives/SomeArchive/index/some-index-file",
|
||||
"/archives/SomeArchive/js/some-js-file.js",
|
||||
"/archives/SomeArchive/videos/some-video-file.mp4",
|
||||
"/archives/SomeArchive/index.html",
|
||||
"/archives/SomeArchive/xxx",
|
||||
"/archives/SomeArchive/xxx/index.html"
|
||||
]}
|
||||
|
||||
static var urisRedirect: [String] {[
|
||||
"/archives/SomeArchive/",
|
||||
"/archives/SomeArchive/documentation",
|
||||
"/archives/SomeArchive/tutorials",
|
||||
]}
|
||||
|
||||
static var urisServes: [String] {[
|
||||
"/archives/SomeArchive/documentation/",
|
||||
"/archives/SomeArchive/tutorials/",
|
||||
"/archives/SomeArchive/favicon.ico",
|
||||
"/archives/SomeArchive/favicon.svg",
|
||||
"/archives/SomeArchive/theme-settings.json",
|
||||
"/archives/SomeArchive/data/documentation.json",
|
||||
"/archives/SomeArchive/css/some-css-file.css",
|
||||
"/archives/SomeArchive/data/some-data-file.bin",
|
||||
"/archives/SomeArchive/downloads/some-download-file",
|
||||
"/archives/SomeArchive/images/some-image-file.jpg",
|
||||
"/archives/SomeArchive/img/some-image-file.png",
|
||||
"/archives/SomeArchive/index/some-index-file",
|
||||
"/archives/SomeArchive/js/some-js-file.js",
|
||||
"/archives/SomeArchive/videos/some-video-file.mp4",
|
||||
]}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Collection+HTTPResponse.Status
|
||||
|
||||
private extension Collection where Element == HTTPResponse.Status {
|
||||
|
||||
// MARK: Computed
|
||||
|
||||
static var statusesInvalid: [HTTPResponse.Status] {[
|
||||
.notFound,
|
||||
.badRequest,
|
||||
.badRequest,
|
||||
.badRequest,
|
||||
.badRequest,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notFound,
|
||||
.notImplemented,
|
||||
.notImplemented,
|
||||
.notImplemented
|
||||
]}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ extension Tag {
|
||||
// MARK: Constants
|
||||
|
||||
@Tag static var enumeration: Tag
|
||||
@Tag static var middleware: Tag
|
||||
@Tag static var provider: Tag
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
import Hummingbird
|
||||
|
||||
struct FileProviderMock {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
private var attributes: [String: Data] = [:]
|
||||
private var identifiers: [String] = []
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
mutating func setFile(identifier: String) {
|
||||
identifiers += [identifier]
|
||||
attributes[identifier] = identifier.data(using: .utf8)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - FileProvider
|
||||
|
||||
extension FileProviderMock: FileProvider {
|
||||
|
||||
// MARK: Type aliases
|
||||
|
||||
typealias FileAttributes = Data
|
||||
typealias FileIdentifier = String
|
||||
|
||||
// MARK: Functions
|
||||
|
||||
func getFileIdentifier(_ path: String) -> String? {
|
||||
identifiers.first { $0 == path }
|
||||
}
|
||||
|
||||
func getAttributes(id: String) async throws -> Data? {
|
||||
attributes[id]
|
||||
}
|
||||
|
||||
func loadFile(id: String, context: some RequestContext) async throws -> ResponseBody {
|
||||
guard let fileData = attributes[id] else {
|
||||
throw HTTPError(.notFound)
|
||||
}
|
||||
|
||||
return .init(byteBuffer: .init(data: fileData))
|
||||
}
|
||||
|
||||
func loadFile(
|
||||
id: String,
|
||||
range: ClosedRange<Int>,
|
||||
context: some RequestContext
|
||||
) async throws -> ResponseBody {
|
||||
try await loadFile(id: id, context: context)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user