//
//  ServerThreadingTests.swift
//  Swifter
//
//  Created by Victor Sigler on 4/22/19.
//  Copyright © 2019 Damian Kołakowski. All rights reserved.
//

import XCTest
@testable import Swifter

class ServerThreadingTests: XCTestCase {
    
    var server: HttpServer!
    
    override func setUp() {
        super.setUp()
        server = HttpServer()
    }
    
    override func tearDown() {
        if server.operating {
            server.stop()
        }
        server = nil
        super.tearDown()
    }
    
    func testShouldHandleTheSameRequestWithDifferentTimeIntervals() {
        
        let path = "/a/:b/c"
        let queue = DispatchQueue(label: "com.swifter.threading")
        let hostURL: URL
        
        server.GET[path] = { .ok(.html("You asked for " + $0.path)) }
        
        do {
            
            #if os(Linux)
            try server.start(9081)
            hostURL = URL(string: "http://localhost:9081")!
            #else
            try server.start()
            hostURL = defaultLocalhost
            #endif
            
            let requestExpectation = expectation(description: "Request should finish.")
            requestExpectation.expectedFulfillmentCount = 3
            
            (1...3).forEach { index in
                queue.asyncAfter(deadline: .now() + .seconds(index)) {
                    let task = URLSession.shared.executeAsyncTask(hostURL: hostURL, path: path) { (_, response, _ ) in
                        requestExpectation.fulfill()
                        let statusCode = (response as? HTTPURLResponse)?.statusCode
                        XCTAssertNotNil(statusCode)
                        XCTAssertEqual(statusCode, 200, "\(hostURL)")
                    }
                    
                    task.resume()
                }
            }
            
        } catch let error {
            XCTFail("\(error)")
        }
        
        waitForExpectations(timeout: 10, handler: nil)
    }
    
    func testShouldHandleTheSameRequestConcurrently() {
        
        let path = "/a/:b/c"
        server.GET[path] = { .ok(.html("You asked for " + $0.path)) }
        
        var requestExpectation: XCTestExpectation? = expectation(description: "Should handle the request concurrently")
        
        do {
            
            try server.start()
            let downloadGroup = DispatchGroup()
            
            DispatchQueue.concurrentPerform(iterations: 3) { _ in
                downloadGroup.enter()
                
                let task = URLSession.shared.executeAsyncTask(path: path) { (_, response, _ ) in
                    
                    let statusCode = (response as? HTTPURLResponse)?.statusCode
                    XCTAssertNotNil(statusCode)
                    XCTAssertEqual(statusCode, 200)
                    requestExpectation?.fulfill()
                    requestExpectation = nil
                    downloadGroup.leave()
                }
                
                task.resume()
            }
            
        } catch let error {
            XCTFail("\(error)")
        }
        
        waitForExpectations(timeout: 15, handler: nil)
    }
}

extension URLSession {
    
    func executeAsyncTask(
        hostURL: URL = defaultLocalhost,
        path: String,
        completionHandler handler: @escaping (Data?, URLResponse?, Error?) -> Void
        ) -> URLSessionDataTask {
        return self.dataTask(with: hostURL.appendingPathComponent(path), completionHandler: handler)
    }
}
