mirror of
https://github.com/swift-server/swift-openapi-lambda.git
synced 2026-05-03 07:22:26 +00:00
4f2e953097
Change the legal files and the license header in source files according to AWS standards
439 lines
16 KiB
Swift
439 lines
16 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift OpenAPI Lambda open source project
|
|
//
|
|
// Copyright Swift OpenAPI Lambda project authors
|
|
// Copyright (c) 2023 Amazon.com, Inc. or its affiliates.
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of Swift OpenAPI Lambda project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
import HTTPTypes
|
|
import OpenAPIRuntime
|
|
import Testing
|
|
|
|
@testable import OpenAPILambda
|
|
|
|
struct RouterGraphTests {
|
|
@Test("Path with no parameters")
|
|
func testPathNoParams() async throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph: URIPathCollection = URIPath()
|
|
let path = "/element1/element2"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
// when
|
|
#expect(throws: Never.self) { try graph.add(method: method, path: path, handler: handler) }
|
|
|
|
// then
|
|
var node: Node? = graph.root()
|
|
|
|
// first node is GET
|
|
node = node?.children[strMethod]
|
|
if case .httpMethod(let retrievedMethod) = node?.value {
|
|
#expect(retrievedMethod == method)
|
|
}
|
|
else {
|
|
Issue.record("Not an HTTP method")
|
|
}
|
|
// all other nodes but last are path elements
|
|
node = node?.children["element1"]
|
|
#expect(node != nil)
|
|
#expect(node?.children.count == 1)
|
|
if case .pathElement(let element) = node?.value { #expect(element == "element1") }
|
|
node = node?.children["element2"]
|
|
#expect(node != nil)
|
|
#expect(node?.children.count == 1)
|
|
if case .pathElement(let element) = node?.value { #expect(element == "element2") }
|
|
|
|
// last node is a handler
|
|
node = node?.children["handler"]
|
|
if case .handler(let retrievedHandler) = node?.value {
|
|
let request = HTTPRequest(method: .init("GET")!, scheme: "https", authority: nil, path: "")
|
|
let (response, body) = try await retrievedHandler(request, nil, ServerRequestMetadata())
|
|
#expect(response.status == .ok)
|
|
let retrievedBody = try? await String(collecting: body ?? "", upTo: 10 * 1024 * 1024)
|
|
#expect(retrievedBody == bodyString)
|
|
}
|
|
else {
|
|
Issue.record("Not a handler")
|
|
}
|
|
}
|
|
@Test("Path with one parameter")
|
|
func testPathOneParams() async throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph: URIPathCollection = URIPath()
|
|
let path = "/element1/{param1}"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
// when
|
|
#expect(throws: Never.self) { try graph.add(method: method, path: path, handler: handler) }
|
|
// then
|
|
var node: Node? = graph.root()
|
|
|
|
// first node is GET
|
|
node = node?.children[strMethod]
|
|
if case .httpMethod(let retrievedMethod) = node?.value {
|
|
#expect(retrievedMethod == method)
|
|
}
|
|
else {
|
|
Issue.record("Not an HTTP method")
|
|
}
|
|
// next node is a path elements
|
|
node = node?.children["element1"]
|
|
#expect(node != nil)
|
|
#expect(node?.children.count == 1)
|
|
if case .pathElement(let element) = node?.value { #expect(element == "element1") }
|
|
node = node?.children["param1"]
|
|
#expect(node != nil)
|
|
#expect(node?.children.count == 1)
|
|
if case .pathParameter(let param) = node?.value { #expect(param == "param1") }
|
|
|
|
// last node is a handler
|
|
node = node?.children["handler"]
|
|
if case .handler(let retrievedHandler) = node?.value {
|
|
let request = HTTPRequest(method: .init("GET")!, scheme: "https", authority: nil, path: "")
|
|
let (response, body) = try await retrievedHandler(request, nil, ServerRequestMetadata())
|
|
#expect(response.status == .ok)
|
|
let retrievedBody = try? await String(collecting: body ?? "", upTo: 10 * 1024 * 1024)
|
|
#expect(retrievedBody == bodyString)
|
|
}
|
|
else {
|
|
Issue.record("Not a handler")
|
|
}
|
|
}
|
|
@Test("Two GET paths")
|
|
func testTwoGETPaths() async throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph: URIPathCollection = URIPath()
|
|
let path1 = "/element1"
|
|
let path2 = "/element2"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
// when
|
|
#expect(throws: Never.self) { try graph.add(method: method, path: path1, handler: handler) }
|
|
#expect(throws: Never.self) { try graph.add(method: method, path: path2, handler: handler) }
|
|
|
|
// then
|
|
var node: Node? = graph.root()
|
|
|
|
// first node is GET
|
|
node = node?.children[strMethod]
|
|
if case .httpMethod(let retrievedMethod) = node?.value {
|
|
#expect(retrievedMethod == method)
|
|
}
|
|
else {
|
|
Issue.record("Not an HTTP method")
|
|
}
|
|
// next nodes are a path elements
|
|
let node1 = node?.children["element1"]
|
|
#expect(node1 != nil)
|
|
#expect(node1?.children.count == 1)
|
|
if case .pathElement(let element) = node1?.value { #expect(element == "element1") }
|
|
|
|
let node2 = node?.children["element2"]
|
|
#expect(node2 != nil)
|
|
#expect(node2?.children.count == 1)
|
|
if case .pathElement(let element) = node2?.value { #expect(element == "element2") }
|
|
|
|
// last node is a handler
|
|
let node3 = node1?.children["handler"]
|
|
#expect(node3 != nil)
|
|
if case .handler(let retrievedHandler) = node3?.value {
|
|
let request = HTTPRequest(method: .init("GET")!, scheme: "https", authority: nil, path: "")
|
|
let (response, body) = try await retrievedHandler(request, nil, ServerRequestMetadata())
|
|
#expect(response.status == .ok)
|
|
let retrievedBody = try? await String(collecting: body ?? "", upTo: 10 * 1024 * 1024)
|
|
#expect(retrievedBody == bodyString)
|
|
}
|
|
else {
|
|
Issue.record("Not a handler")
|
|
}
|
|
|
|
let node4 = node2?.children["handler"]
|
|
try #require(node4 != nil)
|
|
if case .handler(let retrievedHandler) = node4?.value {
|
|
let request = HTTPRequest(method: .init("GET")!, scheme: "https", authority: nil, path: "")
|
|
let (response, body) = try await retrievedHandler(request, nil, ServerRequestMetadata())
|
|
#expect(response.status == .ok)
|
|
let retrievedBody = try? await String(collecting: body ?? "", upTo: 10 * 1024 * 1024)
|
|
#expect(retrievedBody == bodyString)
|
|
}
|
|
else {
|
|
Issue.record("Not a handler")
|
|
}
|
|
}
|
|
@Test("Multiple HTTP methods")
|
|
func testMultipleHTTPMethods() throws {
|
|
// given
|
|
let strMethod1 = "GET"
|
|
let strMethod2 = "POST"
|
|
let method1 = HTTPRequest.Method(strMethod1)!
|
|
let method2 = HTTPRequest.Method(strMethod2)!
|
|
let graph: URIPathCollection = URIPath()
|
|
let path = "/element1/element2"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
// when
|
|
#expect(throws: Never.self) { try graph.add(method: method1, path: path, handler: handler) }
|
|
#expect(throws: Never.self) { try graph.add(method: method2, path: path, handler: handler) }
|
|
|
|
// then
|
|
var node: Node? = graph.root()
|
|
|
|
// first node is GET
|
|
let node1 = node?.children[strMethod1]
|
|
if case .httpMethod(let retrievedMethod) = node1?.value {
|
|
#expect(retrievedMethod == method1)
|
|
}
|
|
else {
|
|
Issue.record("Not an HTTP method")
|
|
}
|
|
// but there is also a POST node at level 1
|
|
let node2 = node?.children[strMethod2]
|
|
if case .httpMethod(let retrievedMethod) = node2?.value {
|
|
#expect(retrievedMethod == method2)
|
|
}
|
|
else {
|
|
Issue.record("Not an HTTP method")
|
|
}
|
|
|
|
// all other nodes but last are path elements
|
|
node = node1?.children["element1"]
|
|
#expect(node1 != nil)
|
|
#expect(node1?.children.count == 1)
|
|
if case .pathElement(let element) = node1?.value { #expect(element == "element1") }
|
|
node = node2?.children["element2"]
|
|
#expect(node2 != nil)
|
|
#expect(node2?.children.count == 1)
|
|
if case .pathElement(let element) = node2?.value { #expect(element == "element2") }
|
|
}
|
|
// func testDeepSearch() {
|
|
// // given
|
|
// let strMethod = "GET"
|
|
// let method = HTTPRequest.Method(strMethod)!
|
|
// let graph = prepareGraph(for: method)
|
|
//
|
|
// let root: Node? = graph.root()
|
|
// XCTAssertNotNil(root)
|
|
//
|
|
// // when
|
|
// // search for GET node
|
|
// var node = graph.findFirst(from: root!, name: "GET")
|
|
// // then
|
|
// if case .httpMethod(let retrievedMethod) = node?.value {
|
|
// XCTAssert(retrievedMethod == method)
|
|
// } else {
|
|
// XCTFail("Not an HTTP method")
|
|
// }
|
|
//
|
|
// // when
|
|
// // search for element1 node
|
|
// node = graph.findFirst(from: root!, name: "element1")
|
|
// // then
|
|
// XCTAssertNotNil(node)
|
|
// if case .pathElement(let element) = node?.value {
|
|
// XCTAssert(element == "element1")
|
|
// } else {
|
|
// XCTFail("Not an path element")
|
|
// }
|
|
//
|
|
// // when
|
|
// // search for element2 node
|
|
// node = graph.findFirst(from: root!, name: "element2")
|
|
// // then
|
|
// XCTAssertNotNil(node)
|
|
// if case .pathElement(let element) = node?.value {
|
|
// XCTAssert(element == "element2")
|
|
// } else {
|
|
// XCTFail("Not an path element")
|
|
// }
|
|
//
|
|
// // when
|
|
// // search for element3 node
|
|
// node = graph.findFirst(from: root!, name: "element3")
|
|
// // then
|
|
// XCTAssertNotNil(node)
|
|
// if case .pathElement(let element) = node?.value {
|
|
// XCTAssert(element == "element3")
|
|
// } else {
|
|
// XCTFail("Not an path element")
|
|
// }
|
|
// }
|
|
|
|
@Test("No method path")
|
|
func testNoMethodPath() {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraph(for: method)
|
|
|
|
// when
|
|
// then
|
|
let noExistingMethod = HTTPRequest.Method("POST")!
|
|
// #expect(throws: OpenAPILambdaRouterError.noRouteForMethod(noExistingMethod).self) {
|
|
#expect(throws: OpenAPILambdaRouterError.self) {
|
|
try graph.find(method: noExistingMethod, path: "/dummy")
|
|
}
|
|
}
|
|
|
|
@Test("No two param children")
|
|
func testNoTwoParamChilds() {
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph: URIPathCollection = URIPath()
|
|
let path1 = "/element1/{param1}"
|
|
let path2 = "/element1/{param2}"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
// when
|
|
#expect(throws: Never.self) { try graph.add(method: method, path: path1, handler: handler) }
|
|
|
|
// then
|
|
#expect(throws: URIPathCollectionError.self) {
|
|
try graph.add(method: method, path: path2, handler: handler)
|
|
}
|
|
}
|
|
|
|
@Test("Find handler 1")
|
|
func testFindHandler1() throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraphForFind(for: method)
|
|
let pathToTest = "/element1/element2"
|
|
|
|
//when
|
|
#expect(throws: Never.self) { try graph.find(method: method, path: pathToTest) }
|
|
let (_, metadata) = try graph.find(method: method, path: pathToTest)
|
|
#expect(metadata.count == 0)
|
|
}
|
|
|
|
@Test(
|
|
"Find handler 2",
|
|
arguments: [
|
|
"/element3/value1/element4",
|
|
"/element3/value2/element4",
|
|
"/element3/value1/element4?param1=value1",
|
|
]
|
|
)
|
|
func testFindHandler2(
|
|
pathToTest: String
|
|
) throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraphForFind(for: method)
|
|
|
|
//when
|
|
#expect(throws: Never.self) {
|
|
let (_, metadata) = try graph.find(method: method, path: pathToTest)
|
|
|
|
// then (we can not test if the query string param have been decoded, that's the job of the openapi runtime.)
|
|
#expect(metadata.count == 1)
|
|
}
|
|
|
|
}
|
|
|
|
@Test("Find handler 3")
|
|
func testFindHandler3() throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraphForFind(for: method)
|
|
let pathToTest = "/element5/value1"
|
|
|
|
//when
|
|
#expect(throws: Never.self) { try graph.find(method: method, path: pathToTest) }
|
|
let (_, metadata) = try graph.find(method: method, path: pathToTest)
|
|
#expect(metadata.count == 1)
|
|
}
|
|
|
|
@Test("Find handler error 1")
|
|
func testFindHandlerError1() throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraphForFind(for: method)
|
|
let pathToTest = "/element6/value1"
|
|
|
|
//when
|
|
#expect(throws: OpenAPILambdaRouterError.self) {
|
|
try graph.find(method: method, path: pathToTest)
|
|
}
|
|
}
|
|
|
|
@Test("Find handler error 2")
|
|
func testFindHandlerError2() throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraphForFind(for: method)
|
|
let pathToTest = "/element6"
|
|
|
|
//when
|
|
#expect(throws: OpenAPILambdaRouterError.self) {
|
|
try graph.find(method: method, path: pathToTest)
|
|
}
|
|
}
|
|
|
|
@Test("Find handler error no HTTP method")
|
|
func testFindHandlerErrorNoHttpMethod() throws {
|
|
// given
|
|
let strMethod = "GET"
|
|
let method = HTTPRequest.Method(strMethod)!
|
|
let graph = prepareGraphForFind(for: method)
|
|
let pathToTest = "/element1/element2"
|
|
|
|
//when
|
|
#expect(throws: OpenAPILambdaRouterError.self) {
|
|
try graph.find(method: HTTPRequest.Method(rawValue: "POST")!, path: pathToTest)
|
|
}
|
|
}
|
|
|
|
private func prepareGraph(for method: HTTPRequest.Method) -> URIPath {
|
|
let graph = URIPath()
|
|
let path1 = "/element1/element2"
|
|
let path2 = "/element1/element3"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
try! graph.add(method: method, path: path1, handler: handler)
|
|
try! graph.add(method: method, path: path2, handler: handler)
|
|
return graph
|
|
}
|
|
|
|
private func prepareGraphForFind(for method: HTTPRequest.Method) -> URIPath {
|
|
let graph = URIPath()
|
|
let path1 = "/element1/element2"
|
|
let path2 = "/element3/{param1}/element4"
|
|
let path3 = "/element5/{param2}"
|
|
let bodyString = "bodyString"
|
|
let handler: OpenAPIHandler = { a, b, c in (HTTPResponse(status: .ok), HTTPBody(bodyString)) }
|
|
|
|
try! graph.add(method: method, path: path1, handler: handler)
|
|
try! graph.add(method: method, path: path2, handler: handler)
|
|
try! graph.add(method: method, path: path3, handler: handler)
|
|
|
|
return graph
|
|
}
|
|
|
|
// TODO: add a test to check what happens when two methods + identiacal paths with two different handlers are added
|
|
|
|
}
|