require 'spec_helper'

describe Webmachine::Dispatcher::Route do
  let(:method) { "GET" }
  let(:uri) { URI.parse("http://localhost:8080/") }
  let(:request){ Webmachine::Request.new(method, uri, Webmachine::Headers.new, "") }
  let(:resource){ Class.new(Webmachine::Resource) }

  matcher :match_route do |*expected|
    route = described_class.new(expected[0], resource, expected[1] || {})
    match do |actual|
      request.uri.path = actual if String === actual
      route.match?(request)
    end

    failure_message_for_should do |_|
      "expected route #{expected[0].inspect} to match path #{request.uri.path}"
    end
    failure_message_for_should_not do |_|
      "expected route #{expected[0].inspect} not to match path #{request.uri.path}"
    end
  end

  context "matching a request" do
    context "on the root path" do
      subject { "/" }
      it { should match_route([]) }
      it { should match_route ['*'] }
      it { should_not match_route %w{foo} }
      it { should_not match_route [:id] }
    end

    context "on a deep path" do
      subject { "/foo/bar/baz" }
      it { should match_route %w{foo bar baz} }
      it { should match_route ['foo', :id, "baz"] }
      it { should match_route %w{foo *} }
      it { should match_route [:id, '*'] }
      it { should_not match_route [] }
      it { should_not match_route %w{bar *} }
    end

    context "with a guard on the request method" do
      let(:uri){ URI.parse("http://localhost:8080/notes") }
      let(:route) do
        described_class.new(
                            ["notes"],
                            lambda { |request| request.method == "POST" },
                            resource
                            )
      end
      subject { route }

      context "when guard passes" do
        let(:method){ "POST" }
        it { should be_match(request) }

        context "but the path match fails" do
          let(:uri){ URI.parse("http://localhost:8080/other") }
          it { should_not be_match(request) }
        end
      end

      context "when guard fails" do
        let(:method) { "GET" }
        it { should_not be_match(request) }
      end

      context "when the guard responds to #call" do
        let(:guard_class) do
          Class.new do
            def initialize(method)
              @method = method
            end

            def call(request)
              request.method == @method
            end
          end
        end

        let(:route) do
          described_class.new(["notes"], guard_class.new("POST"), resource)
        end

        context "when the guard passes" do
          let(:method){ "POST" }
          it { should be_match(request) }
        end

        context "when the guard fails" do
          # let(:method){ "GET" }
          it { should_not be_match(request) }
        end
      end
    end
  end

  context "applying bindings" do
    context "on the root path" do
      subject { described_class.new([], resource) }
      before { subject.apply(request) }

      it "should assign the dispatched path to the empty string" do
        request.disp_path.should == ""
      end

      it "should assign empty bindings" do
        request.path_info.should == {}
      end

      it "should assign empty path tokens" do
        request.path_tokens.should == []
      end

      context "with extra user-defined bindings" do
        subject { described_class.new([], resource, "bar" => "baz") }

        it "should assign the user-defined bindings" do
          request.path_info.should == {"bar" => "baz"}
        end
      end

      context "with a splat" do
        subject { described_class.new(['*'], resource) }

        it "should assign empty path tokens" do
          request.path_tokens.should == []
        end
      end
    end

    context "on a deep path" do
      subject { described_class.new(%w{foo bar baz}, resource) }
      before { request.uri.path = "/foo/bar/baz"; subject.apply(request) }

      it "should assign the dispatched path as the path past the initial slash" do
        request.disp_path.should == "foo/bar/baz"
      end

      it "should assign empty bindings" do
        request.path_info.should == {}
      end

      it "should assign empty path tokens" do
        request.path_tokens.should == []
      end

      context "with path variables" do
        subject { described_class.new(['foo', :id, 'baz'], resource) }

        it "should assign the path variables in the bindings" do
          request.path_info.should == {:id => "bar"}
        end
      end

      context "with a splat" do
        subject { described_class.new(%w{foo *}, resource) }

        it "should capture the path tokens matched by the splat" do
          request.path_tokens.should == %w{ bar baz }
        end
      end
    end
  end
end
