Files
Sébastien Stormacq 4f2e953097 Adjust license files for /awslabs (#28)
Change the legal files and the license header in source files according
to AWS standards
2025-10-21 21:06:59 +02:00

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
}