/
XCTApplication.swift
128 lines (118 loc) · 4.15 KB
/
XCTApplication.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
extension Application: XCTApplicationTester {
public func performTest(request: XCTHTTPRequest) throws -> XCTHTTPResponse {
try self.testable().performTest(request: request)
}
}
extension Application {
public enum Method {
case inMemory
case running(port: Int)
public static var running: Method {
return .running(port: 8080)
}
}
public func testable(method: Method = .inMemory) throws -> XCTApplicationTester {
try self.boot()
switch method {
case .inMemory:
return try InMemory(app: self)
case .running(let port):
return try Live(app: self, port: port)
}
}
private struct Live: XCTApplicationTester {
let app: Application
let port: Int
init(app: Application, port: Int) throws {
self.app = app
self.port = port
}
func performTest(request: XCTHTTPRequest) throws -> XCTHTTPResponse {
try app.server.start(hostname: "localhost", port: self.port)
defer { app.server.shutdown() }
let client = HTTPClient(eventLoopGroupProvider: .createNew)
defer { try! client.syncShutdown() }
var path = request.url.path
path = path.hasPrefix("/") ? path : "/\(path)"
var url = "http://localhost:\(self.port)\(path)"
if let query = request.url.query {
url += "?\(query)"
}
var clientRequest = try HTTPClient.Request(
url: url,
method: request.method,
headers: request.headers
)
clientRequest.body = .byteBuffer(request.body)
let response = try client.execute(request: clientRequest).wait()
return XCTHTTPResponse(
status: response.status,
headers: response.headers,
body: response.body ?? ByteBufferAllocator().buffer(capacity: 0)
)
}
}
private struct InMemory: XCTApplicationTester {
let app: Application
init(app: Application) throws {
self.app = app
}
@discardableResult
public func performTest(
request: XCTHTTPRequest
) throws -> XCTHTTPResponse {
var headers = request.headers
headers.replaceOrAdd(
name: .contentLength,
value: request.body.readableBytes.description
)
let request = Request(
application: app,
method: request.method,
url: request.url,
headers: headers,
collectedBody: request.body,
remoteAddress: nil,
on: self.app.eventLoopGroup.next()
)
let res = try self.app.responder.respond(to: request).wait()
return try XCTHTTPResponse(
status: res.status,
headers: res.headers,
body: res.body.collect(on: request.eventLoop).wait() ?? ByteBufferAllocator().buffer(capacity: 0)
)
}
}
}
public protocol XCTApplicationTester {
func performTest(request: XCTHTTPRequest) throws -> XCTHTTPResponse
}
extension XCTApplicationTester {
@discardableResult
public func test(
_ method: HTTPMethod,
_ path: String,
headers: HTTPHeaders = [:],
body: ByteBuffer? = nil,
file: StaticString = #file,
line: UInt = #line,
beforeRequest: (inout XCTHTTPRequest) throws -> () = { _ in },
afterResponse: (XCTHTTPResponse) throws -> () = { _ in }
) throws -> XCTApplicationTester {
var request = XCTHTTPRequest(
method: method,
url: .init(path: path),
headers: headers,
body: body ?? ByteBufferAllocator().buffer(capacity: 0)
)
try beforeRequest(&request)
do {
let response = try self.performTest(request: request)
try afterResponse(response)
} catch {
XCTFail("\(error)", file: (file), line: line)
throw error
}
return self
}
}