@@ -9,6 +9,7 @@ import APITesting
99/// Controls basic CRUD operations on `Todo`s.
1010final class APITestController {
1111
12+ static let zipPathPrefix = Environment . archivesPath
1213 let outputPath : String
1314 let openAPISource : OpenAPISource
1415 let database : Database
@@ -55,6 +56,27 @@ final class APITestController {
5556 }
5657 }
5758
59+ func files( _ req: TypedRequest < FilesContext > ) throws -> EventLoopFuture < Response > {
60+ guard let id = req. parameters. get ( " id " , as: UUID . self) else {
61+ return req. response. badRequest
62+ }
63+
64+ let query = APITestDescriptor . query ( on: database)
65+ . filter ( \APITestDescriptor . $id == id)
66+
67+ return query. first ( )
68+ . unwrap ( or: Abort ( . notFound) )
69+ . flatMap { req. fileio. collectFile ( at: self . zipPath ( for: $0) ) }
70+ . flatMap { req. response. success. encode ( $0) }
71+ . flatMapError { error in
72+ guard let abortError = error as? Abort ,
73+ abortError. status == . notFound else {
74+ return req. response. serverError
75+ }
76+ return req. response. notFound
77+ }
78+ }
79+
5880 /// Create an `APITestDescriptor` and run a new test suite.
5981 func create( _ req: TypedRequest < CreateContext > ) throws -> EventLoopFuture < Response > {
6082 let reqUUIDGuess = req
@@ -72,18 +94,30 @@ final class APITestController {
7294
7395 savedDescriptor. whenSuccess { [ weak self] in
7496
75- guard let source = self ? . openAPISource,
76- let database = self ? . database,
77- let outPath = self ? . outputPath,
78- let eventLoop = self ? . testEventLoop ( ) else { return } // this just happens if the controller has been released from memory
97+ // this just happens if the controller has been released from memory
98+ // which we consider possible here because this whole process is async
99+ // and independent of the API request completion.
100+ guard let self = self else { return }
101+
102+ let source = self . openAPISource
103+ let database = self . database
104+ let outPath = self . outPath ( for: descriptor)
105+ let zipPath = self . zipPath ( for: descriptor)
106+ let eventLoop = self . testEventLoop ( )
107+
108+ req. logger. info ( " Running tests in \( outPath) " )
79109
80110 prepOutputFolder ( on: eventLoop, at: outPath, logger: logger)
81111 . flatMap { descriptor. markBuilding ( ) . save ( on: database) }
82112 . flatMap { openAPIDoc ( on: eventLoop, from: source) }
83- . flatMap { openAPIDoc in produceAPITestPackage ( on: eventLoop, given: openAPIDoc, to: outPath, logger: logger) }
113+ . flatMap { openAPIDoc in produceAPITestPackage ( on: eventLoop, given: openAPIDoc, to: outPath, zipToPath : zipPath , logger: logger) }
84114 . flatMap { descriptor. markRunning ( ) . save ( on: database) }
85115 . flatMap { runAPITestPackage ( on: eventLoop, at: outPath, logger: logger) }
86116 . flatMap { descriptor. markPassed ( ) . save ( on: database) }
117+ . always { _ in
118+ try ? cleanupOutFolder ( outPath, logger: logger)
119+ req. logger. info ( " Cleaning up tests in \( outPath) " )
120+ }
87121 . whenFailure { error in
88122 req. logger. error ( " Testing Failed " ,
89123 metadata: [ " error " : . stringConvertible( String ( describing: error) ) ] )
@@ -110,6 +144,16 @@ final class APITestController {
110144 return testEventLoopGroup. next ( )
111145 }
112146
147+ private func zipPath( for test: APITestDescriptor ) -> String {
148+ return Self . zipPathPrefix
149+ + " / \( test. id!. uuidString) .zip "
150+ }
151+
152+ private func outPath( for test: APITestDescriptor ) -> String {
153+ return self . outputPath
154+ + " / \( test. id!. uuidString) / "
155+ }
156+
113157 // MARK: - Contexts
114158
115159 struct CreateContext : RouteContext {
@@ -214,6 +258,36 @@ final class APITestController {
214258
215259 static let builder = { return Self ( ) }
216260 }
261+
262+ struct FilesContext : RouteContext {
263+ typealias RequestBodyType = EmptyRequestBody
264+
265+ let success : ResponseContext < ByteBuffer > =
266+ . init { response in
267+ response. status = . ok
268+ response. headers. contentType = . zip
269+ }
270+
271+ let notFound : CannedResponse < EmptyResponseBody > =
272+ . init( response: Response (
273+ status: . notFound
274+ )
275+ )
276+
277+ let badRequest : CannedResponse < EmptyResponseBody > =
278+ . init( response: Response (
279+ status: . badRequest
280+ )
281+ )
282+
283+ let serverError : CannedResponse < EmptyResponseBody > =
284+ . init( response: Response (
285+ status: . internalServerError
286+ )
287+ )
288+
289+ static let builder = { return Self ( ) }
290+ }
217291}
218292
219293extension APITestController {
0 commit comments