# Copyright 2010-present Basho Technologies, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

require 'spec_helper'

describe Riak::Serializers do
  shared_examples_for "a serializer" do |type, deserialized, serialized|
    context "for #{type}" do
      it "serializes #{deserialized} to #{serialized}" do
        expect(described_class.serialize(type, deserialized)).to eq(serialized)
      end

      it "deserializes #{serialized} to #{deserialized}" do
        expect(described_class.deserialize(type, serialized)).
          to eq(deserialized)
      end

      it "round trips properly" do
        str = described_class.serialize(type, deserialized)
        expect(described_class.deserialize(type, str)).to eq(deserialized)
      end
    end
  end

  it_behaves_like "a serializer", "text/plain", "a string", "a string"
  it_behaves_like "a serializer", "application/json", { "a" => 7 }, '{"a":7}'
  it_behaves_like "a serializer",
                  "application/x-ruby-marshal",
                  { a: 3 },
                  Marshal.dump({ a: 3 })

  described_class::YAML_MIME_TYPES.each do |mime_type|
    it_behaves_like "a serializer",
                    mime_type,
                    { "a" => 7 },
                    YAML.dump({ "a" => 7 })
  end

  %w[ serialize deserialize ].each do |meth|
    describe ".#{meth}" do
      it 'raises an IOError when given an unrecognized content type' do
        expect {
          described_class.send(meth, "application/unrecognized", "string")
        }.to raise_error(IOError)
      end
    end
  end

  describe "plain text serializer" do
    it 'calls #to_s to convert the object to a string' do
      expect(described_class.serialize('text/plain', :a_string)).
        to eq('a_string')
    end
  end

  describe "JSON serializer" do
    it "respects the max nesting option" do
      # Sadly, this spec will not fail for me when using yajl-ruby
      # on Ruby 1.9, even when passing the options to #to_json is
      # not implemented.
      Riak.json_options = {:max_nesting => 51}
      h = {}
      p = h
      (1..50).each do |i|
        p['a'] = {}
        p = p['a']
      end
      s = h.to_json(Riak.json_options)
      expect {
        described_class.serialize('application/json', h)
      }.to_not raise_error

      expect {
        described_class.deserialize('application/json', s)
      }.to_not raise_error
    end
  end

  describe "a custom serializer" do
    let(:custom_serializer) do
      Object.new.tap do |o|
        def o.dump(string)
          "The string is: #{string}"
        end

        def o.load(string)
          string.sub(/^The string is: /, '')
        end
      end
    end

    it 'can be registered' do
      described_class['application/custom-type-1'] = custom_serializer
      expect(described_class['application/custom-type-1']).
        to be(custom_serializer)
      # fail
    end

    it_behaves_like "a serializer",
                    "application/custom-type-a",
                    "foo",
                    "The string is: foo" do
      before(:each) do
        described_class['application/custom-type-a'] = custom_serializer
      end
    end

    after(:each) do
      # Make sure to clean up the registered serializer
      %w{application/custom-type-1 application/custom-type-a}.each do |ctype|
        described_class.send(:serializers).delete(ctype)
      end
    end
  end

  it_behaves_like "a serializer",
                  "application/json; charset=UTF-8",
                  { "a" => 7 },
                  '{"a":7}'
  it_behaves_like "a serializer",
                  "application/json ;charset=UTF-8",
                  { "a" => 7 },
                  '{"a":7}'
end
