require 'spec_helper'
require 'puppet/indirector_testing'

require 'puppet/network/http'

describe Puppet::Network::HTTP::Route do
  def request(method, path)
    Puppet::Network::HTTP::Request.from_hash({
      :method => method,
      :path => path,
      :routing_path => path })
  end

  def respond(text)
    lambda { |req, res| res.respond_with(200, "text/plain", text) }
  end

  let(:req) { request("GET", "/vtest/foo") }
  let(:res) { Puppet::Network::HTTP::MemoryResponse.new }

  describe "an HTTP Route" do
    it "can match a request" do
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest})
      expect(route.matches?(req)).to be_truthy
    end

    it "will raise a Method Not Allowed error when no handler for the request's method is given" do
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).post(respond("ignored"))
      expect do
        route.process(req, res)
      end.to raise_error(Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError)
    end

    it "can match any HTTP method" do
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).any(respond("used"))
      expect(route.matches?(req)).to be_truthy

      route.process(req, res)

      expect(res.body).to eq("used")
    end

    it "processes DELETE requests" do
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).delete(respond("used"))

      route.process(request("DELETE", "/vtest/foo"), res)

      expect(res.body).to eq("used")
    end

    it "does something when it doesn't know the verb" do
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo})

      expect do
        route.process(request("UNKNOWN", "/vtest/foo"), res)
      end.to raise_error(Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError, /UNKNOWN/)
    end

    it "calls the method handlers in turn" do
      call_count = 0
      handler = lambda { |request, response| call_count += 1 }
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(handler, handler)

      route.process(req, res)
      expect(call_count).to eq(2)
    end

    it "stops calling handlers if one of them raises an error" do
      ignored_called = false
      ignored = lambda { |req, res| ignored_called = true }
      raise_error = lambda { |req, res| raise Puppet::Network::HTTP::Error::HTTPNotAuthorizedError, "go away" }
      route = Puppet::Network::HTTP::Route.path(%r{^/vtest/foo}).get(raise_error, ignored)

      expect do
        route.process(req, res)
      end.to raise_error(Puppet::Network::HTTP::Error::HTTPNotAuthorizedError)
      expect(ignored_called).to be_falsey
    end

    it "chains to other routes after calling its handlers" do
      inner_route = Puppet::Network::HTTP::Route.path(%r{^/inner}).any(respond("inner"))
      unused_inner_route = Puppet::Network::HTTP::Route.path(%r{^/unused_inner}).any(respond("unused"))

      top_route = Puppet::Network::HTTP::Route.path(%r{^/vtest}).any(respond("top")).chain(unused_inner_route, inner_route)
      top_route.process(request("GET", "/vtest/inner"), res)

      expect(res.body).to eq("topinner")
    end
  end
end
