require "spec_helper"

describe Garage::HypermediaResponder do
  before do
    controller.responder = responder
    controller.resource = resource
  end

  let(:responder) do
    Class.new(ActionController::Responder) do
      include Garage::HypermediaResponder
    end
  end

  let(:controller) do
    Class.new(ActionController::Base) do
      respond_to :json

      class << self
        attr_accessor :resource

        def name
          "ExamplesController"
        end
      end

      attr_accessor :field_selector, :representation

      def show
        respond_with self.class.resource
      end
    end
  end

  let(:resource_class) do
    Class.new do
      include Garage::Representer

      def represent!
      end

      def render_hash(options = {})
        { name: "example" }
      end
    end
  end

  let(:resource) do
    resource_class.new
  end

  let(:hash) do
    { name: "example" }
  end

  let(:env) do
    {
      "HTTP_ACCEPT" => "application/json",
      "PATH_INFO" => "/",
      "REQUEST_METHOD" => "GET",
      "rack.input" => "",
    }
  end

  describe "#display" do
    it "calls resource.represent! method" do
      expect(resource).to receive(:represent!)
      controller.action(:show).call(env)
    end

    context "with resource which changes depending on controller.params" do
      before do
        env["QUERY_STRING"] = "key1=value1&key2=value2"
      end

      let(:resource_class) do
        Class.new(super()) do
          param :key1

          def render_hash(options = {})
            params.as_json
          end
        end
      end

      it "allows resource to refer to specified params" do
        expect(controller.action(:show).call(env)[2].body).to eq({ key1: "value1" }.to_json)
      end
    end

    context "with non-mappable resource" do
      it "renders a given resource as a Hash" do
        expect(controller.action(:show).call(env)[2].body).to eq({ name: "example" }.to_json)
      end
    end

    context "with mappable resource" do
      let(:resource) do
        [super()]
      end

      it "renders a given resource as an Array of Hashes" do
        expect(controller.action(:show).call(env)[2].body).to eq([{ name: "example" }].to_json)
      end
    end
  end
end

describe Garage::HypermediaResponder::DataRenderer do
  describe ".render" do
    subject do
      described_class.render(rendered, options)
    end

    let(:resource) do
      { "id" => 1 }
    end

    let(:options) do
      {}
    end

    context "with a resource" do
      let(:rendered) do
        resource
      end

      context "by default" do
        it "returns argument as JSON" do
          is_expected.to eq(rendered.to_json)
        end
      end

      context "with :dictionary option" do
        before do
          options[:dictionary] = true
        end

        it "returns argument as JSON" do
          is_expected.to eq(rendered.to_json)
        end
      end

      context "with '<' & '>'" do
        let(:resource) do
          { "name" => "<x>" }
        end

        it "replaces them with '\\u003C' & '\\u003E'" do
          is_expected.not_to match /<x>/
          is_expected.to eq(%<{"name":"\\u003Cx\\u003E"}>)
        end
      end
    end

    context "with resources" do
      let(:rendered) do
        [resource]
      end

      context "by default" do
        it "returns argument as JSON in Array" do
          is_expected.to eq(rendered.to_json)
        end
      end

      context "with :dictionary option" do
        before do
          options[:dictionary] = true
        end

        it "returns argument as JSON in Hash" do
          is_expected.to eq({ resource["id"] => resource }.to_json)
        end
      end

      context "with :dictionary option & non-convertible resources" do
        let(:resource) do
          { "name" => "foo" }
        end

        it "returns argument as JSON in Array" do
          is_expected.to eq(rendered.to_json)
        end
      end
    end
  end
end

describe Garage::HypermediaResponder::Representation do
  let(:representation) do
    described_class.new(controller)
  end

  let(:controller) do
    double(representation: representation_type, request: request)
  end

  let(:representation_type) do
    :dictionary
  end

  let(:request) do
    double(format: format)
  end

  let(:format) do
    "application/json"
  end

  describe "#dictionary?" do
    context "with controller.representation == :dictionary" do
      it "returns true" do
        expect(representation.dictionary?).to eq(true)
      end
    end

    context "with controller.representation != :dictionary" do
      let(:representation_type) do
        nil
      end

      it "returns false" do
        expect(representation.dictionary?).to eq(false)
      end
    end
  end

  describe "#content_type" do
    context "with controller.request.format == 'application/json'" do
      it "returns 'application/vnd.cookpad.dictionary+json'" do
        expect(representation.content_type).to eq("application/vnd.cookpad.dictionary+json")
      end
    end
  end
end
