require 'core/spec_helper'

describe ZendeskAPI::Association do
  let(:instance) { ZendeskAPI::TestResource.new(client, :id => 1) }
  let(:child) { ZendeskAPI::TestResource::TestChild.new(client, :id => 1, :test_resource_id => 2) }

  describe "setting/getting" do
    context "has" do
      before do
        ZendeskAPI::TestResource.associations.clear
        ZendeskAPI::TestResource.has :child, :class => ZendeskAPI::TestResource::TestChild
      end

      it "should not try and fetch nil child" do
        instance.child_id = nil
        instance.child.should be_nil
      end

      it "should cache an set object" do
        instance.child = child
        instance.child.should == child
      end

      it "should set id on set if it was there" do
        instance.child_id = nil
        instance.child = child
        instance.child_id.should == child.id
      end

      it "should build a object set via hash" do
        instance.child = {:id => 2}
        instance.child.id.should == 2
      end

      it "should build a object set via id" do
        instance.child = 2
        instance.child.id.should == 2
      end

      it "should not fetch an unknown object" do
       instance.child.should be_nil
      end

      it "should fetch an object known by id" do
        stub_json_request(:get, %r{test_resources/1/child/5}, json(:test_child => {:id => 5}))
        instance.child_id = 5
        instance.child.id.should == 5
      end

      it "should handle client errors" do
        stub_request(:get, %r{test_resources/1/child/5}).to_return(:status => 500)
        instance.child_id = 5
        expect { silence_logger { instance.child } }.to_not raise_error
      end

      it "should handle resource not found errors" do
        stub_request(:get, %r{test_resources/1/child/5}).to_return(:status => 404)
        instance.child_id = 5
        silence_logger{ instance.child.should be_nil }
      end

      it "is not used when not used" do
        instance.child_used?.should == false
      end

      it "is used when used" do
        instance.child = child
        instance.child_used?.should == true
      end
    end

    context "has_many" do
      it "should cache a set object" do
        instance.children = [child]
        instance.children.map(&:id).should == [1]
        instance.children.should be_instance_of(ZendeskAPI::Collection)
      end

      it "should set ids" do
        instance.children_ids = []
        instance.children = [child]
        instance.children_ids.should == [child.id]
        instance.children.should be_instance_of(ZendeskAPI::Collection)
      end

      it "should build and cache objects set via hash" do
        instance.children = [{:id => 2}]
        instance.children.map(&:id).should == [2]
        instance.children.should be_instance_of(ZendeskAPI::Collection)
      end

      it "should build a object set via id" do
        instance.children = [2]
        instance.children.map(&:id).should == [2]
        instance.children.should be_instance_of(ZendeskAPI::Collection)
      end

      it "should fetch unknown objects" do
        stub_json_request(:get, %r{test_resources/1/children}, json(:test_children => [{:id => 2}, {:id => 3}]))
        instance.children.map(&:id).should == [2,3]
        instance.children.should be_instance_of(ZendeskAPI::Collection)
      end

      it "is not used when not used" do
        instance.children_used?.should == false
      end

      it "is used when used" do
        instance.children = [child]
        instance.children_used?.should == true
      end
    end
  end

  context "class only" do
    subject { described_class.new(:class => ZendeskAPI::TestResource) }

    it "should generate resource path" do
      subject.generate_path.should == "test_resources"
    end

    context "with an instance" do
      it "should generate a specific resource path" do
        subject.generate_path(instance).should == "test_resources/1"
      end

      context "with_id => false" do
        it "should generate general resource path" do
          subject.generate_path(instance, :with_id => false).should == "test_resources"
        end
      end

      context "with an instance that is a new record" do
        it "should generate general resource path" do
          subject.generate_path(ZendeskAPI::TestResource.new(client)).should == "test_resources"
        end
      end
    end

    context "with a specified path" do
      before(:each) { subject.options[:path] = "blergh" }

      it "should generate general resource path" do
        subject.generate_path.should == "blergh"
      end
    end

    context "with a passed in id" do
      it "should generate specific resource path" do
        opts = { :id => 1 }
        subject.generate_path(opts).should == "test_resources/1"
        opts.should be_empty
      end
    end
  end

  context "class with a specified parent" do
    subject { described_class.new(:class => ZendeskAPI::TestResource::TestChild, :parent => instance) }

    it "should generate nested resource path" do
      subject.generate_path.should == "test_resources/1/children"
    end

    context "with an instance" do
      it "should generate a specific nested resource path" do
        subject.generate_path(child).should == "test_resources/1/children/1"
      end

      context "with_id => false" do
        it "should generate nested resource path" do
          subject.generate_path(child, :with_id => false).should == "test_resources/1/children"
        end
      end
    end

    context "with a specified path" do
      before(:each) { subject.options[:path] = "blergh" }

      it "should generate nested resource path" do
        subject.generate_path.should == "test_resources/1/blergh"
      end
    end

    context "with a path on the association" do
      before(:each) do
        association = ZendeskAPI::TestResource.associations.detect {|a| a[:name] == :children}
        association[:path] = "blergh"
      end

      it "should generate nested resource path" do
        subject.generate_path.should == "test_resources/1/blergh"
      end
    end

    context "with no association" do
      before(:each) do
        ZendeskAPI::TestResource.associations.clear
      end

      it "should generate nested resource path" do
        subject.generate_path.should == "test_resources/test_children"
      end
    end
  end

  context "class with a parent id" do
    subject { described_class.new(:class => ZendeskAPI::TestResource::TestChild) }

    it "should raise an error if not passed an instance or id" do
      expect { subject.generate_path }.to raise_error(ArgumentError)
    end

    it "should generate specific nested resource path" do
      subject.generate_path(child).should == "test_resources/2/children/1"
    end

    context "with parent id passed in" do
      it "should generate nested resource path" do
        opts = { :test_resource_id => 3 }
        subject.generate_path(opts).should == "test_resources/3/children"
        opts.should be_empty
      end
    end
  end

  context "with a singular resource" do
    subject { described_class.new(:class => ZendeskAPI::SingularTestResource) }

    context "with an instance" do
      it "should not generate a specific resource path" do
        subject.generate_path(ZendeskAPI::SingularTestResource.new(client, :id => 1)).should == "singular_test_resources"
      end
    end
  end
end