require 'spec_helper'
require 'net/http'
require 'pact/consumer'
require 'pact/consumer/rspec'

describe "A service consumer side of a pact", :pact => true  do

  context "with more than one matching interaction found" do
    let(:expected_response) do
      {"message"=>"Multiple interaction found for GET /path", "matching_interactions"=>[{"description"=>"a request", "request"=>{"method"=>"get", "path"=>"/path", "body"=>{"a"=>"some body"}, "headers"=>{"Content-Type"=>"application/json"}}}, {"description"=>"an identical request", "request"=>{"method"=>"get", "path"=>"/path", "body"=>{"a"=>"some body"}, "headers"=>{"Content-Type"=>"application/json"}}}]}
    end

    it "returns an error" do
      Pact.clear_configuration

      Pact.service_consumer "Consumer" do
        has_pact_with "Mary Service" do
          mock_service :mary_service do
            verify false
            port 1237
          end
        end
      end

      mary_service
        .given("something")
        .upon_receiving("a request")
        .with(method: 'get', path: '/path', body: {a: 'some body'}, headers: {'Content-Type' => 'application/json'})
        .will_respond_with(status: 200)


      mary_service
        .upon_receiving("an identical request")
        .with(method: 'get', path: '/path', body: {a: 'some body'}, headers: {'Content-Type' => 'application/json'})
        .will_respond_with(status: 200)

      uri = URI('http://localhost:1237/path')
      post_req = Net::HTTP::Get.new(uri.path)
      post_req['Content-Type'] = "application/json"
      post_req.body = {a: "some body"}.to_json
      response = Net::HTTP.start(uri.hostname, uri.port) do |http|
        http.request post_req
      end

      expect(JSON.load(response.body)).to eq expected_response
    end

  end

  context "with no matching interaction found" do

    let(:expected_response) do
      {
        "message"=>"No interaction found for GET /path",
        "interaction_diffs"=>[{
          "description" => "a request that will not be properly matched",
          "provider_state" => "something",
          "body"=>{
            "a"=>{
              "expected"=>"some body",
              "actual"=>"not matching body"
            }
          }
        }]
      }
    end

    it "returns an error" do
      Pact.clear_configuration

      Pact.service_consumer "Consumer" do
        has_pact_with "Mary Service" do
          mock_service :mary_service do
            verify false
            port 1236
          end
        end
      end

      mary_service
        .given("something")
        .upon_receiving("a request that will not be properly matched")
        .with(method: 'get', path: '/path', body: {a: 'some body'}, headers: {'Content-Type' => 'application/json'})
        .will_respond_with(status: 200)

      uri = URI('http://localhost:1236/path')
      post_req = Net::HTTP::Get.new(uri.path)
      post_req['Content-Type'] = "application/json"
      post_req.body = {a: "not matching body"}.to_json
      response = Net::HTTP.start(uri.hostname, uri.port) do |http|
        http.request post_req
      end

      expect(JSON.load(response.body)).to eq expected_response
    end
  end

  context "with a provider state" do
    before do
      Pact.clear_configuration

      Pact.service_consumer "Consumer" do
        has_pact_with "Zebra Service" do
          mock_service :zebra_service do
            verify false
            port 1235
          end
        end
      end
    end

    it "goes like this" do
      zebra_service.
        given(:the_zebras_are_here).
        upon_receiving("a retrieve Mallory request").with({
          method: :get,
          path: '/mallory',
          headers: {'Accept' => 'text/html'}
        }).
        will_respond_with({
          status: 200,
          headers: { 'Content-Type' => 'text/html' },
          body: Pact::Term.new(matcher: /Mallory/, generate: 'That is some good Mallory.')
        })

        interactions = Pact::ConsumerContract.from_json(File.read(zebra_service.consumer_contract.pactfile_path)).interactions
        interactions.first.provider_state.should eq("the_zebras_are_here")
        sleep 1
    end
  end

  context "with a async interaction with provider" do
    before do
      Pact.clear_configuration

      Pact.service_consumer "Consumer" do
        has_pact_with "Zebra Service" do
          mock_service :zebra_service do
            verify true
            port 1239
          end
        end
      end
    end

    it "goes like this" do
      zebra_service.
          given(:the_zebras_are_here).
          upon_receiving("a retrieve Mallory request").
          with({
            method: :get,
            path: '/mallory'
          }).
          will_respond_with({status: 200})

      async_interaction { Net::HTTP.get_response(URI('http://localhost:1239/mallory'))}

      zebra_service.wait_for_interactions wait_max_seconds: 1, poll_interval: 0.1
    end

    def async_interaction
      Thread.new do
        sleep 0.2
        yield
      end
    end

  end

end