require File.expand_path("../../spec_helper", File.dirname(__FILE__))
require 'active_record'
ActiveRecord.load_all! if ActiveRecord.respond_to?(:load_all!)  # Lazy loading of active_record was added to rails 2.3.2
                                                                # so we have to explicitly load it this way for cpk to work.
require 'composite_primary_keys'
require 'bcdatabase/active_record/schema_qualified_tables'

describe "SchemaQualifiedTables" do
  before do
    @conn = mock(ActiveRecord::ConnectionAdapters::AbstractAdapter)
    @conn.stub!(:default_sequence_name).and_return("default_sequence_name")
    ActiveRecord::Base.connection_handler.stub!(:retrieve_connection).and_return(@conn)
  end

  after do
    Object.class_eval do
      %w(ReadingMaterialBase Book Magazine Newspaper Pamphlet).each do |clazz|
        remove_const clazz if const_defined? clazz
      end
    end
  end

  describe "tables" do
    describe "with no schema name" do
      it "uses the inferred table name" do
        class Book < ActiveRecord::Base
        end

        Book.table_name.should == 'books'
      end

      it "uses just the explicit tablename" do
        class Novel < ActiveRecord::Base
          set_table_name "books"
        end

        Novel.table_name.should == 'books'
      end
    end

    describe "with schema name" do
      it "uses the inferred table name" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
        end

        Magazine.table_name.should == 'reading_material.magazines'
      end

      it "uses the explicit table name, if first" do
        class Magazine < ActiveRecord::Base
          set_table_name "some_magazines"
          set_schema :reading_material
        end

        Magazine.table_name.should == "reading_material.some_magazines"
      end

      it "uses the explicit table name, if second" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
          set_table_name "some_magazines"
        end

        Magazine.table_name.should == "reading_material.some_magazines"
      end

      it "preserves the schema name if the table name is set several times" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
          set_table_name "some_magazines"

          set_table_name "other_magazines"
        end

        Magazine.table_name.should == "reading_material.other_magazines"
      end

      it "uses the last schema if set several times" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
          set_table_name "some_magazines"
          set_schema :periodicals
        end

        Magazine.table_name.should == "periodicals.some_magazines"
      end

      it "works if set_schema is called without a connection" do
        ActiveRecord::Base.connection_handler.should_receive(:retrieve_connection).
          and_raise(ActiveRecord::ConnectionNotEstablished)
        lambda {
          class Magazine < ActiveRecord::Base
            set_schema :periodicals
          end
        }.should_not raise_error

        Magazine.table_name.should == "periodicals.magazines"
      end

      describe "from the parent" do
        before do
          class ReadingMaterialBase < ActiveRecord::Base
            self.abstract_class = true
            set_schema :reading_material
          end
        end

        it "inherits the schema from its parent class" do
          class Magazine < ReadingMaterialBase
            set_table_name "some_magazines"
          end

          Magazine.schema.should == :reading_material
          Magazine.table_name.should == "reading_material.some_magazines"
        end

        it "uses the inferred table name for a child" do
          pending "bug in ActiveRecord::Base.reset_table_name"
                  # Fails because the first call to reset_table_name
                  # always returns just the inferred table name.  Usually
                  # the first call is during set_table_name or set_schema.
                  # In this spec, it is in the call to table_name, below.
          class Newspaper < ReadingMaterialBase; end

          Newspaper.schema.should == :reading_material
          Newspaper.table_name.should == "reading_material.newspapers"
        end

        it "uses separate schemas for subclasses" do
          class Magazine < ReadingMaterialBase
            set_table_name "some_magazines"
            set_schema :periodicals
          end
          class Newspaper < ReadingMaterialBase
            set_schema :deprecated
          end

          Magazine.table_name.should == "periodicals.some_magazines"
          Newspaper.table_name.should == "deprecated.newspapers"
        end
      end

      describe "with name overrides" do
        before do
          ActiveRecord::Base.schemas = {
            :reading_material => 'reading_material_test'
          }
        end

        after do
          ActiveRecord::Base.schemas.clear
        end

        it "uses the inferred table name" do
          class Pamphlet < ActiveRecord::Base
            set_schema :reading_material
          end

          Pamphlet.table_name.should == 'reading_material_test.pamphlets'
        end

        it "uses the explicit table name, if first" do
          class Pamphlet < ActiveRecord::Base
            set_table_name "some_pamphlets"
            set_schema :reading_material
          end

          Pamphlet.table_name.should == "reading_material_test.some_pamphlets"
        end

        it "uses the explicit table name, if second" do
          class Pamphlet < ActiveRecord::Base
            set_schema :reading_material
            set_table_name "some_pamphlets"
          end

          Pamphlet.table_name.should == "reading_material_test.some_pamphlets"
        end

        it "applies name overrides that come after the model is loaded" do
          class Newspaper < ActiveRecord::Base
            set_table_name "newspaperos"
            set_schema :periodicals
          end

          ActiveRecord::Base.schemas = { :periodicals => "periodicals_test" }

          Newspaper.table_name.should == "periodicals_test.newspaperos"
        end

        it "applies name overrides that come after the model is loaded when using the inferred table name" do
          class Newspaper < ActiveRecord::Base
            set_schema :periodicals
          end

          ActiveRecord::Base.schemas = { :periodicals => "periodicals_test" }

          Newspaper.table_name.should == "periodicals_test.newspapers"
        end
      end
    end
  end

  # TODO: bad, bad, bad copying
  describe "sequences" do
    describe "with no schema name" do
      it "uses the inferred sequence name" do
        class Book < ActiveRecord::Base
        end

        Book.sequence_name.should == 'default_sequence_name'
      end

      it "uses just the explicit sequencename" do
        class Novel < ActiveRecord::Base
          set_sequence_name "books"
        end

        Novel.sequence_name.should == 'books'
      end

      it "gives nil if the adapter doesn't specify a default sequence name" do
        conn = mock(ActiveRecord::ConnectionAdapters::AbstractAdapter)
        conn.stub!(:default_sequence_name).and_return(nil)
        ActiveRecord::Base.connection_handler.stub!(:retrieve_connection).and_return(conn)

        class Book < ActiveRecord::Base
        end

        Book.sequence_name.should be_nil
      end
    end

    describe "with schema name" do
      it "uses the inferred sequence name" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
        end

        Magazine.sequence_name.should == 'reading_material.default_sequence_name'
      end

      it "gives nil if the adapter doesn't specify a default sequence name" do
        conn = mock(ActiveRecord::ConnectionAdapters::AbstractAdapter)
        conn.stub!(:default_sequence_name).and_return(nil)
        ActiveRecord::Base.connection_handler.stub!(:retrieve_connection).and_return(conn)

        class Book < ActiveRecord::Base
          set_schema :reading_material
        end

        Book.sequence_name.should be_nil
      end

      describe "with CPK" do
        it "doesn't fail when setting the schema" do
          class Newspaper < ActiveRecord::Base
            set_primary_keys "address", "telephone"
            set_schema :reading_material
          end
        end
      end

      it "uses the explicit sequence name, if first" do
        class Magazine < ActiveRecord::Base
          set_sequence_name "some_magazines_seq"
          set_schema :reading_material
        end

        Magazine.sequence_name.should == "reading_material.some_magazines_seq"
      end

      it "uses the explicit sequence name, if second" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
          set_sequence_name "some_magazines_seq"
        end

        Magazine.sequence_name.should == "reading_material.some_magazines_seq"
      end

      it "preserves the schema name if the sequence name is set several times" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
          set_sequence_name "some_magazines_seq"

          set_sequence_name "other_magazines_seq"
        end

        Magazine.sequence_name.should == "reading_material.other_magazines_seq"
      end

      it "uses the last schema if set several times" do
        class Magazine < ActiveRecord::Base
          set_schema :reading_material
          set_sequence_name "some_magazines_seq"
          set_schema :periodicals
        end

        Magazine.sequence_name.should == "periodicals.some_magazines_seq"
      end

      it "works if set_schema is called without a connection" do
        ActiveRecord::Base.connection_handler.should_receive(:retrieve_connection).
          and_raise(ActiveRecord::ConnectionNotEstablished)
        lambda {
          class Magazine < ActiveRecord::Base
            set_schema :periodicals
            set_sequence_name 'mag_seq'
          end
        }.should_not raise_error

        Magazine.sequence_name.should == "periodicals.mag_seq"
      end

      describe "with name overrides" do
        before do
          ActiveRecord::Base.schemas = {
            :reading_material => 'reading_material_test'
          }
        end

        after do
          ActiveRecord::Base.schemas.clear
        end

        it "uses the inferred sequence name" do
          class Pamphlet < ActiveRecord::Base
            set_schema :reading_material
          end

          Pamphlet.sequence_name.should == 'reading_material_test.default_sequence_name'
        end

        it "uses the explicit sequence name, if first" do
          class Pamphlet < ActiveRecord::Base
            set_sequence_name "some_pamphlets"
            set_schema :reading_material
          end

          Pamphlet.sequence_name.should == "reading_material_test.some_pamphlets"
        end

        it "uses the explicit sequence name, if second" do
          class Pamphlet < ActiveRecord::Base
            set_schema :reading_material
            set_sequence_name "some_pamphlets"
          end

          Pamphlet.sequence_name.should == "reading_material_test.some_pamphlets"
        end
      end
    end
  end
end