IBM-Swift/Kitura

View on GitHub
Tests/KituraTests/TestServer.swift

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * Copyright IBM Corporation 2016, 2017
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

import XCTest
import Dispatch

import KituraNet
@testable import Kitura

final class TestServer: KituraTest, KituraTestSuite {

    static var allTests: [(String, (TestServer) -> () throws -> Void)] {
        var listOfTests: [(String, (TestServer) -> () throws -> Void)] = [
            ("testServerStartStop", testServerStartStop),
            ("testServerRun", testServerRun),
            ("testServerFail", testServerFail),
            ("testServerStartWithStatus", testServerStartWithStatus),
            ("testServerStartWaitStop", testServerStartWaitStop),
            ("testServerRestart", testServerRestart)
        ]
        #if !SKIP_UNIX_SOCKETS
            listOfTests.append(("testUnixSocketServerRestart", testUnixSocketServerRestart))
        #endif
        return listOfTests
    }

    var httpPort = 8080
    #if DISABLE_FASTCGI
    let useNIO = true
    #else
    let useNIO = false
    #endif

    override func setUp() {
        super.setUp()
        stopServer() // stop common server so we can run these tests
    }

    private func setupServerAndExpectations(expectStart: Bool, expectStop: Bool, expectFail: Bool, httpPort: Int?=nil, fastCgiPort: Int?=nil) {
        let router = Router()
        let httpServer = Kitura.addHTTPServer(onPort: httpPort ?? 0, onAddress: "localhost", with: router)
        let fastCgiServer = useNIO ? FastCGIServer() : Kitura.addFastCGIServer(onPort: fastCgiPort ?? 0,
                                                                               onAddress: "localhost", with: router)

        if expectStart {
            let httpStarted = expectation(description: "HTTPServer started()")
            let fastCgiStarted = expectation(description: "FastCGIServer started()")

            httpServer.started {
                httpStarted.fulfill()
            }

            if useNIO {
                fastCgiStarted.fulfill()
            } else {
                fastCgiServer.started {
                    fastCgiStarted.fulfill()
                }
            }

        } else {
            httpServer.started {
                XCTFail("httpServer.started should not have been called")
            }

            if !useNIO {
                fastCgiServer.started {
                    XCTFail("fastCgiServer.started should not have been called")
                }
            }
        }

        if expectStop {
            let httpStopped = expectation(description: "HTTPServer stopped()")
            let fastCgiStopped = expectation(description: "FastCGIServer stopped()")

            httpServer.stopped {
                httpStopped.fulfill()
            }

            if useNIO {
                fastCgiStopped.fulfill()
            } else {
                fastCgiServer.stopped {
                    fastCgiStopped.fulfill()
                }
            }
        }

        if expectFail {
            let httpFailed = expectation(description: "HTTPServer failed()")
            let fastCgiFailed = expectation(description: "FastCGIServer failed()")

            httpServer.failed { error in
                httpFailed.fulfill()
            }

            if useNIO {
                fastCgiFailed.fulfill()
            } else {
                fastCgiServer.failed { error in
                    fastCgiFailed.fulfill()
                }
            }

        } else {
            httpServer.failed { error in
                XCTFail("\(error)")
            }

            if !useNIO {
                fastCgiServer.failed { error in
                    XCTFail("\(error)")
                }
            }
        }
    }

    func testServerStartStop() {
        setupServerAndExpectations(expectStart: true, expectStop: true, expectFail: false)

        let requestQueue = DispatchQueue(label: "Request queue")
        requestQueue.async() {
            Kitura.start()
            Kitura.stop()
        }

        waitForExpectations(timeout: 10) { error in
            XCTAssertNil(error)
        }
    }

    /// Sets up two servers on the same port. One will start, and one will fail to start
    /// as the address is already in use.
    private func setupConflictingServers(onPort: Int) {
        let router1 = Router()
        let router2 = Router()
        let server1 = Kitura.addHTTPServer(onPort: onPort, with: router1)
        let server2 = Kitura.addHTTPServer(onPort: onPort, with: router2)
        // Expect server 1 to start
        let server1Started = expectation(description: "Server 1 started on port \(onPort)")
        server1.started {
            server1Started.fulfill()
        }
        // Expect server 2 to fail (same port)
        let server2Failed = expectation(description: "Server 2 failed - port should already be in use")
        server2.failed { error in
            XCTAssertNotNil(error)
            server2Failed.fulfill()
        }
    }

    /// Tests that Kitura.startWithStatus() returns the correct number of servers
    /// that failed to start.
    func testServerStartWithStatus() {
        setupConflictingServers(onPort: 12345)
        let numFailures = Kitura.startWithStatus()
        XCTAssertEqual(numFailures, 1, "Expected startWithStatus() to report 1 server fail")
        waitForExpectations(timeout: 10) { error in
            Kitura.stop()
            XCTAssertNil(error)
        }
    }

    /// Tests that Kitura.wait() will wait until all successful servers are stopped.
    func testServerStartWaitStop() {
        setupConflictingServers(onPort: 23456)
        let waitComplete = DispatchSemaphore(value: 0)
        let requestQueue = DispatchQueue(label: "Request queue")
        requestQueue.async() {
            let failures = Kitura.startWithStatus()
            XCTAssertEqual(failures, 1, "Expected one server to fail to start")
            // As soon as the server has started, wait for it to stop
            Kitura.wait()
            // Signal that Kitura.wait() has completed
            waitComplete.signal()
        }
        waitForExpectations(timeout: 10) { error in
            // Check that Kitura.wait() is still waiting
            let preStopStatus = waitComplete.wait(timeout: DispatchTime.now())
            XCTAssertEqual(preStopStatus, DispatchTimeoutResult.timedOut, "Kitura.wait() unexpectedly completed")
            // Stop the server, allowing wait() to complete
            Kitura.stop()
            XCTAssertNil(error)
            // Check that the Kitura.wait() call completes
            let postStopStatus = waitComplete.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(10))
            XCTAssertEqual(postStopStatus, DispatchTimeoutResult.success, "Timed out waiting to Kitura.wait() to complete")
        }
    }

    func testServerRun() {
        setupServerAndExpectations(expectStart: true, expectStop: false, expectFail: false)

        let requestQueue = DispatchQueue(label: "Request queue")
        requestQueue.async() {
            Kitura.run()
        }

        waitForExpectations(timeout: 10) { error in
            Kitura.stop()
            XCTAssertNil(error)
        }
    }

    func testServerFail() {
        // setupServer startup should fail as we are passing in illegal ports
        setupServerAndExpectations(expectStart: false, expectStop: false, expectFail: true,
                                   httpPort: -1, fastCgiPort: -2)

        let requestQueue = DispatchQueue(label: "Request queue")
        requestQueue.async() {
            Kitura.start()
        }

        waitForExpectations(timeout: 10) { error in
            Kitura.stop()
            XCTAssertNil(error)
        }
    }

    func testServerRestart() {
        var port = httpPort
        let path = "/testServerRestart"
        let body = "Server is running."

        let router = Router()
        router.get(path) { _, response, next in
            response.send(body)
            next()
        }

        let server = Kitura.addHTTPServer(onPort: 0, with: router)
        server.sslConfig = KituraTest.sslConfig.config

        let stopped = DispatchSemaphore(value: 0)
        server.stopped {
            stopped.signal()
        }

        Kitura.start()
        port = server.port!
        testResponse(port: port, path: path, expectedBody: body)
        Kitura.stop(unregister: false)
        stopped.wait()

        XCTAssertEqual(Kitura.httpServersAndPorts.count, 1, "Kitura.httpServersAndPorts.count is \(Kitura.httpServersAndPorts.count), should be 1")
        testResponse(port: port, path: path, expectedBody: nil, expectedStatus: nil)

        Kitura.start()
        port = server.port!
        testResponse(port: port, path: path, expectedBody: body)
        Kitura.stop() // default for unregister is true

        XCTAssertEqual(Kitura.httpServersAndPorts.count, 0, "Kitura.httpServersAndPorts.count is \(Kitura.httpServersAndPorts.count), should be 0")
    }

    func testUnixSocketServerRestart() {
#if !SKIP_UNIX_SOCKETS
        let path = "/testSocketServerRestart"
        let body = "Server is running."

        // Create a temporary socket path for this server
        let socketPath = uniqueTemporaryFilePath()
        defer {
            removeTemporaryFilePath(socketPath)
        }

        let router = Router()
        router.get(path) { _, response, next in
            response.send(body)
            next()
        }

        let server = Kitura.addHTTPServer(onUnixDomainSocket: socketPath, with: router)
        server.sslConfig = KituraTest.sslConfig.config

        let stopped = DispatchSemaphore(value: 0)
        let started = DispatchSemaphore(value: 0)
        server.stopped {
            stopped.signal()
        }
        server.started {
            started.signal()
        }

        Kitura.start()
        if started.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(5)) != DispatchTimeoutResult.success {
            return XCTFail("Server failed to start")
        }
        XCTAssertEqual(server.unixDomainSocketPath, socketPath, "Server listening on wrong path")
        testResponse(socketPath: socketPath, path: path, expectedBody: body)
        Kitura.stop(unregister: false)
        if stopped.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(5)) != DispatchTimeoutResult.success {
            return XCTFail("Server failed to stop")
        }

        XCTAssertEqual(Kitura.httpServersAndUnixSocketPaths.count, 1, "Kitura.httpServersAndUnixSocketPaths.count is \(Kitura.httpServersAndUnixSocketPaths.count), should be 1")
        testResponse(socketPath: socketPath, path: path, expectedBody: nil, expectedStatus: nil)

        Kitura.start()
        if started.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(5)) != DispatchTimeoutResult.success {
            return XCTFail("Server failed to start")
        }
        XCTAssertEqual(server.unixDomainSocketPath, socketPath, "Server listening on wrong path")
        testResponse(socketPath: socketPath, path: path, expectedBody: body)
        Kitura.stop() // default for unregister is true
        if stopped.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(5)) != DispatchTimeoutResult.success {
            return XCTFail("Server failed to stop")
        }

        XCTAssertEqual(Kitura.httpServersAndUnixSocketPaths.count, 0, "Kitura.httpServersAndUnixSocketPaths.count is \(Kitura.httpServersAndUnixSocketPaths.count), should be 0")
#endif
    }

    private func testResponse(port: Int? = nil, socketPath: String? = nil, method: String = "get", path: String, expectedBody: String?, expectedStatus: HTTPStatusCode? = HTTPStatusCode.OK) {

        performRequest(method, path: path, port: port, socketPath: socketPath, useSSL: true, useUnixSocket: (socketPath != nil), callback: { response in
            let status = response?.statusCode
            XCTAssertEqual(status, expectedStatus, "status was \(String(describing: status)), expected \(String(describing: expectedStatus))")
            do {
                let body = try response?.readString()
                XCTAssertEqual(body, expectedBody, "body was '\(String(describing: body))', expected '\(String(describing: expectedBody))'")
            } catch {
                XCTFail("Error reading body: \(error)")
            }
        })
    }
}