<commit_before>//
//  ResourceRequestsSpec.swift
//  Siesta
//
//  Created by Paul on 2015/7/5.
//  Copyright © 2015 Bust Out Solutions. All rights reserved.
//

import Siesta
import Quick
import Nimble
import Nocilla

class ResourceRequestsSpec: ResourceSpecBase
    {
    override func resourceSpec(service: () -> Service, _ resource: () -> Resource)
        {
        it("starts in a blank state")
            {
            expect(resource().latestData).to(beNil())
            expect(resource().latestError).to(beNil())

            expect(resource().isLoading).to(beFalse())
            expect(resource().allRequests).to(beIdentialObjects([]))
            expect(resource().loadRequests).to(beIdentialObjects([]))
            }

        describe("request()")
            {
            it("fetches the resource")
                {
                stubRequest(resource, "GET").andReturn(200)
                awaitNewData(resource().request(.GET))
                }

            it("handles various HTTP methods")
                {
                stubRequest(resource, "PATCH").andReturn(200)
                awaitNewData(resource().request(.PATCH))
                }

            it("sends headers from configuration")
                {
                service().configure { $0.config.headers["Zoogle"] = "frotz" }
                stubRequest(resource, "GET")
                    .withHeader("Zoogle", "frotz")
                    .andReturn(200)
                awaitNewData(resource().request(.GET))
                }

            describe("beforeStartingRequest hook from configuation")
                {
                it("is called for every request")
                    {
                    var beforeHookCount = 0
                    service().configure
                        {
                        $0.config.beforeStartingRequest
                            {
                            res, req in
                            expect(res).to(beIdenticalTo(resource()))
                            beforeHookCount++
                            }
                        }

                    stubRequest(resource, "GET").andReturn(200)
                    stubRequest(resource, "POST").andReturn(200)
                    awaitNewData(resource().load())
                    awaitNewData(resource().request(.POST))

                    expect(beforeHookCount).to(equal(2))
                    }

                it("can attach request hooks")
                    {
                    var successHookCalled = false
                    service().configure
                        {
                        $0.config.beforeStartingRequest
                            { $1.onSuccess { _ in successHookCalled = true } }
                        }

                    stubRequest(resource, "GET").andReturn(200)
                    awaitNewData(resource().load())

                    expect(successHookCalled).to(beTrue())
                    }

                it("can cancel requests")
                    {
                    service().configure
                        {
                        $0.config.beforeStartingRequest
                            { $1.cancel() }
                        }

                    awaitFailure(resource().load(), alreadyCompleted: true)  // Nocilla will flag if network call goes through
                    }
                }

            it("does not update the resource state")
                {
                stubRequest(resource, "GET").andReturn(200)
                awaitNewData(resource().request(.GET))
                expect(resource().latestData).to(beNil())
                expect(resource().latestError).to(beNil())
                }

            it("can be cancelled")
                {
                let reqStub = stubRequest(resource, "GET").andReturn(200).delay()
                let req = resource().request(.GET)
                req.onFailure
                    { expect($0.cause is Error.Cause.RequestCancelled).to(beTrue()) }
                req.cancel()
                reqStub.go()
                awaitFailure(req, alreadyCompleted: true)
                }

            it(".cancel() has no effect if it already succeeded")
                {
                stubRequest(resource, "GET").andReturn(200)
                let req = resource().request(.GET)
                awaitNewData(req)
                req.cancel()
                awaitNewData(req, alreadyCompleted: true)
                }

            it(".cancel() has no effect if it never started")
                {
                let req = resource().request(.POST, json: ["unencodable": UIView()])
                awaitFailure(req, alreadyCompleted: true)
                req.cancel()
                }

            // TODO: How to reproduce these conditions in tests?
            pending("server response has no effect if it arrives but cancel() already called") { }
            pending("cancel() has no effect after request completed") { }

            it("tracks concurrent requests")
                {
                func stubDelayedAndRequest(ident: String) -> (LSStubResponseDSL, Request)
                    {
                    let reqStub = stubRequest(resource, "GET")
                        .withHeader("Request-ident", ident)
                        .andReturn(200)
                        .delay()
                    let req = resource().request(.GET)
                        { $0.setValue(ident, forHTTPHeaderField: "Request-ident") }
                    return (reqStub, req)
                    }

                let (reqStub0, req0) = stubDelayedAndRequest("zero"),
                    (reqStub1, req1) = stubDelayedAndRequest("one")

                expect(resource().isRequesting).to(beTrue())
                expect(resource().allRequests).to(beIdentialObjects([req0, req1]))

                reqStub0.go()
                awaitNewData(req0)<commit_msg>isCancellation assertions<commit_after>//
//  ResourceRequestsSpec.swift
//  Siesta
//
//  Created by Paul on 2015/7/5.
//  Copyright © 2015 Bust Out Solutions. All rights reserved.
//

import Siesta
import Quick
import Nimble
import Nocilla

class ResourceRequestsSpec: ResourceSpecBase
    {
    override func resourceSpec(service: () -> Service, _ resource: () -> Resource)
        {
        it("starts in a blank state")
            {
            expect(resource().latestData).to(beNil())
            expect(resource().latestError).to(beNil())

            expect(resource().isLoading).to(beFalse())
            expect(resource().allRequests).to(beIdentialObjects([]))
            expect(resource().loadRequests).to(beIdentialObjects([]))
            }

        describe("request()")
            {
            it("fetches the resource")
                {
                stubRequest(resource, "GET").andReturn(200)
                awaitNewData(resource().request(.GET))
                }

            it("handles various HTTP methods")
                {
                stubRequest(resource, "PATCH").andReturn(200)
                awaitNewData(resource().request(.PATCH))
                }

            it("sends headers from configuration")
                {
                service().configure { $0.config.headers["Zoogle"] = "frotz" }
                stubRequest(resource, "GET")
                    .withHeader("Zoogle", "frotz")
                    .andReturn(200)
                awaitNewData(resource().request(.GET))
                }

            describe("beforeStartingRequest hook from configuation")
                {
                it("is called for every request")
                    {
                    var beforeHookCount = 0
                    service().configure
                        {
                        $0.config.beforeStartingRequest
                            {
                            res, req in
                            expect(res).to(beIdenticalTo(resource()))
                            beforeHookCount++
                            }
                        }

                    stubRequest(resource, "GET").andReturn(200)
                    stubRequest(resource, "POST").andReturn(200)
                    awaitNewData(resource().load())
                    awaitNewData(resource().request(.POST))

                    expect(beforeHookCount).to(equal(2))
                    }

                it("can attach request hooks")
                    {
                    var successHookCalled = false
                    service().configure
                        {
                        $0.config.beforeStartingRequest
                            { $1.onSuccess { _ in successHookCalled = true } }
                        }

                    stubRequest(resource, "GET").andReturn(200)
                    awaitNewData(resource().load())

                    expect(successHookCalled).to(beTrue())
                    }

                it("can cancel requests")
                    {
                    service().configure
                        {
                        $0.config.beforeStartingRequest
                            { $1.cancel() }
                        }

                    awaitFailure(resource().load(), alreadyCompleted: true)  // Nocilla will flag if network call goes through
                    }
                }

            it("does not update the resource state")
                {
                stubRequest(resource, "GET").andReturn(200)
                awaitNewData(resource().request(.GET))
                expect(resource().latestData).to(beNil())
                expect(resource().latestError).to(beNil())
                }

            it("can be cancelled")
                {
                let reqStub = stubRequest(resource, "GET").andReturn(200).delay()
                let req = resource().request(.GET)
                req.onFailure
                    { expect($0.cause is Error.Cause.RequestCancelled).to(beTrue()) }
                req.onCompletion
                    { expect($0.isCancellation).to(beTrue()) }
                req.cancel()
                reqStub.go()
                awaitFailure(req, alreadyCompleted: true)
                }

            it(".cancel() has no effect if it already succeeded")
                {
                stubRequest(resource, "GET").andReturn(200)
                let req = resource().request(.GET)
                req.onCompletion
                    { expect($0.isCancellation).to(beFalse()) }
                awaitNewData(req)
                req.cancel()
                awaitNewData(req, alreadyCompleted: true)
                }

            it(".cancel() has no effect if it never started")
                {
                let req = resource().request(.POST, json: ["unencodable": UIView()])
                req.onCompletion
                    { expect($0.isCancellation).to(beFalse()) }
                awaitFailure(req, alreadyCompleted: true)
                req.cancel()
                }

            // TODO: How to reproduce these conditions in tests?
            pending("server response has no effect if it arrives but cancel() already called") { }
            pending("cancel() has no effect after request completed") { }

            it("tracks concurrent requests")
                {
                func stubDelayedAndRequest(ident: String) -> (LSStubResponseDSL, Request)
                    {
                    let reqStub = stubRequest(resource, "GET")
                        .withHeader("Request-ident", ident)
                        .andReturn(200)
                        .delay()
                    let req = resource().request(.GET)
                        { $0.setValue(ident, forHTTPHeaderField: "Request-ident") }
                    return (reqStub, req)
                    }

                let (reqStub0, req0) = stubDelayedAndRequest("zero"),
                    (reqStub1, req1) = stubDelayedAndRequest("one")

                expect(resource().isRequesting).to(beTrue())
                expect(resource().allRequests).to(beIdentialObjects([req0, req1]))

                reqStub0.go()
                awaitNewData(req0)
{'fraction_non_alphanumeric': 0.10500329163923634, 'fraction_numerical': 0.009792626728110598, 'mean_word_length': 1.2319559228650139, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2468029', 'n_tokens_mistral': 2988, 'n_tokens_neox': 2731, 'n_words': 528}