require File.join(File.dirname(__FILE__), "..", "..", "helper.rb")

require "og/adapter/dbi"

# TODO: Fully specify the disconnect behavior.
# TODO: @manager.store.disconnect if @manager.instance_of?(Og::Manager)


################################################################
#
# Please insert any spec.'s where indicated further down.
# 
################################################################

module OgSpecExpectations
  attr_accessor :og_manager_options
  attr_accessor :og_store_options
  attr_accessor :og_type_map
  attr_accessor :dbi_dbd_map
  
  def og_default_manager_options 
    {
   :name=>:memory,
   :dbi_driver=>:sqlite,
   :user=>"",
   :password=>"",
   :called_by_og_setup=>true,
   :adapter=>:dbi,
   :evolve_schema=>:warn
    }
  end
  def og_default_store_options 
    {
     :name=>:memory,
     :dbi_driver=>:sqlite,
     :evolve_schema=>:warn,
     :called_by_og_setup=>true,
     :adapter=>:dbi
     }
  end
  def og_default_type_map 
    {
      Integer => 'integer',
      Fixnum => 'integer',
      Float => 'float',
      String => 'text',
      Time => 'timestamp',
      Date => 'date',
      TrueClass => 'boolean',
      Object => 'text',
      Array => 'text',
      Hash => 'text',
      Og::Blob => 'bytea' # postgreSQL
    }
  end
  def og_custom_manager_options 
    {
   :name=>"test",
   :dbi_driver=>:sqlite,
   :user=>"",
   :password=>"",
   :called_by_og_setup=>true,
   :adapter=>:dbi,
   :evolve_schema=>true,
   :destroy => false
    }
  end
  def og_custom_store_options 
    {
     :name=>:memory,
     :dbi_driver=>:sqlite,
     :evolve_schema=>:warn,
     :called_by_og_setup=>true,
     :adapter=>:dbi
     }
  end
  def og_custom_type_map 
    {
      Integer => 'integer',
      Fixnum => 'integer',
      Float => 'float',
      String => 'text',
      Time => 'timestamp',
      Date => 'date',
      TrueClass => 'boolean',
      Object => 'text',
      Array => 'text',
      Hash => 'text',
      Og::Blob => 'bytea' # postgreSQL
    }
  end
  def self.dbi_dbd_map 
    {
   :ado=>"ADO",
   :db2=>"DB2",
   :frontbase=>"FrontBase",
   :interbase=>"InterBase",
   :msql=>"Msql",
   :mysql=>"Mysql",
   :odbc=>"ODBC",
   :oracle=>"Oracle",
   :pg=>"Pg",
   :proxy=>"Proxy",
   :sqlite=>"SQLite",
   :sqlrelay=>"SQLRelay"
   }
  end

end

describe "The DBI adapter" do
  before(:each) do
    OgSpecHelper.setup({:config_only=>true})
  end
  
  it "should have known OG ancestors" do
     # The order of the ancestors is the order that the methods 
     # will be looked up when the object receives a message, 
     # That is from left to right.
      adapter_ancestors = [ Og::DbiAdapter, Og::SqlStore, 
                          Og::Evolution, Og::Store, Og::Mixin ]
      (Og::DbiAdapter.ancestors & adapter_ancestors).should == adapter_ancestors
  end
  it "should have known Nitro ancestors" do
      adapter_ancestors = [ Glue ]
      (Og::DbiAdapter.ancestors & adapter_ancestors).should == adapter_ancestors
  end
end

describe "A repeated default DBI adapter connect/disconnect to/from an Og store manager" do
  before(:all) do    
    @config = OgSpecHelper.setup({:config_only=>true})
  end 
  TRIES = ["first", "second", "third", "fourth"] # After all, it is the world cup now...
  TRIES.each do |try|
    it "should not raise any error when started on the #{try} occasion." do
      lambda { Og.start }.should_not raise_error
    end
    it "should not raise any error when the store is closed on the #{try} occasion." do
      lambda { Og.manager.close_store }.should_not raise_error
    end
    it "should not raise any error when class are unmanaged on the #{try} occasion." do
      lambda { Og.manager.unmanage_classes }.should_not raise_error
    end
  end
end

describe "A repeated customized DBI adapter connect/disconnect to/from an Og store manager" do
  before(:all) do    
    @config = OgSpecHelper.setup({:config_only=>true})
    @opts = {:evolve_schema => true, :destroy => false, :name => "test"}
  end 
  TRIES.each do |try|
    it "should not raise any error when started on the #{try} occasion." do
      lambda { Og.start(@opts) }.should_not raise_error
    end
    it "should not raise any error when the store is closed on the #{try} occasion." do
      lambda { Og.manager.close_store }.should_not raise_error
    end
    it "should not raise any error when class are unmanaged on the #{try} occasion." do
      lambda { Og.manager.unmanage_classes }.should_not raise_error
    end
  end
end

describe "A default DBI adapter connect/disconnect to/from a Og store manager" do
  before(:all) do    
    @config = OgSpecHelper.setup({:config_only=>true})
    @manager = Og.start
    Aspects.setup
  end 
  it "should return an instance of Og::Manager after opening the store connection." do  
    @manager.should be_an_instance_of(Og::Manager)
  end
 it "should return nil after closing the store connection." do  
    @manager.close_store.should be_nil
  end
  it "should return an empty array after classes are unmanaged." do  
    @manager.unmanage_classes.should == []
  end
end

# This is a convenience stop Og function - its definition
# depends on the return values from the last two spec's.
module OgSpecHelper
  class << self
    def stop(manager) 
        if manager.close_store == nil && manager.unmanage_classes == []
          true
        else
          false
        end
     end
  end
end

################################################################
#
# Insert any spec.'s below this point.
# 
################################################################

describe "a manager containing expected instance classes", :shared => true do
  
  it "should be an instance of the Og::Manager object." do
    @manager.should be_an_instance_of(Og::Manager)
  end
  it "should contain a 'store' that is an instance of the Og::DBIAdapter." do
    @manager.store.should be_an_instance_of(Og::DbiAdapter)
  end
  it "should contain an instance of a DBI database handle in its 'store.conn' attribute." do
    @manager.store.conn.should be_an_instance_of(DBI::DatabaseHandle)
  end
  it "should contain an reference to itself (an instance of Og::Manager) in its 'store.ogmanager' attribute." do
    @manager.store.ogmanager.object_id.should == @manager.object_id
  end
  #
  #   	 actual.should respond_to(method)
  #
#  it "should refer to the default Og::DbiAdapter options in its 'store.options' attribute." do
#    @manager.store.options.should == OgSpecHelper.og_default_store_options
#  end
#  it "should share Og::Manager#options and Og::DbiAdapter#store_details[:options]." do
#    @manager.options.object_id.should == @manager.store.store_details[:options].object_id.should
#  end
# attributes to spec via store_details: store_class, transaction_nesting
# dbi_version, dbi_driver, dbi_driver_version
#  it "should initialize a thread safe manager." do
#    @manager.thread_safe.should_not be_nil
#  end
end

describe "a manager with default options", :shared => true do
  
  include OgSpecExpectations
  extend OgSpecExpectations
  
  it "should be have an empty array as its 'models' attribute." do
    @manager.models.should == []
  end
  it "should refer to the default database in its 'name' attribute." do
    @manager.name.should == og_default_manager_options[:name]
  end
  it "should refer to the default Og::Manager options in its 'options' attribute." do
    @manager.options.should == og_default_manager_options
  end
  it "should refer to the default Og::SqlStore typemap in its 'store.typemap' attribute." do
    @manager.store.typemap.should == og_default_type_map
  end
  it "should have DBI trace mode of 2 in its 'store.conn.trace_mode' attribute." do
    @manager.store.conn.trace_mode.should == 2
  end
  it "should output the DBI trace to an IO instance through the 'store.conn.trace_output' attribute." do
    @manager.store.conn.trace_output.should be_an_instance_of(IO)
  end
end

#  describe "a manager with expected methods" do
#    
#  end

describe "A default (DbiAdapter) connection to a new Og store" do
  before(:all) do 
    OgSpecHelper.setup({:config_only=>true})
    @manager = Og.start
    Aspects.setup
  end
  
  after(:all) do
      #The following currently break the spec's.  They must be used in after(:all){}
      #Object.module_eval{remove_const('@manager')}
      OgSpecHelper.stop(@manager)
  end

  it_should_behave_like "a manager containing expected instance classes"
  it_should_behave_like "a manager with default options"

end

describe "Alternative default (DbiAdapter) connection to a new Og store manager" do
  before(:all) do
    OgSpecHelper.setup({:config_only=>true})
    @manager = Og.start({})
    Aspects.setup
  end
  after(:all) do
      #The following currently break the spec's.  They must be used in after(:all){}
      #Object.module_eval{remove_const('@manager')}
      OgSpecHelper.stop(@manager)
  end

  it_should_behave_like "a manager containing expected instance classes"
  it_should_behave_like "a manager with default options"

end

#  it "should use sqlite memory, when given no option." do
#    @manager = Og.start
#    @manager.should be_an_instance_of(Og::Manager)
#    @manager.options.should == OgSpecHelper.og_default_manager_options
#  end
#  it "should initialize a thread safe manager." do
#    @manager = Og.start
#    @manager.should be_an_instance_of(Og::Manager)
#    Og.thread_safe.should_not be_nil
#  end
#  it "should use sqlite memory, when given an empty option hash." do
#    @manager = Og.start({})
#    @manager.should be_an_instance_of(Og::Manager)
#    @manager.options.should == OgSpecHelper.og_default_manager_options
#  end
#  it "should initialize a thread safe manager." do
#    @manager = Og.start({})
#    @manager.should be_an_instance_of(Og::Manager)
#    Og.thread_safe.should_not be_nil
#  end

describe "A customized DBI adapter connection to a new Og store manager" do
  include OgSpecExpectations
  extend OgSpecExpectations
  
  before(:all) do  
    @config = OgSpecHelper.setup({:config_only=>true})
    @opts = {:evolve_schema => true, :destroy => false, :name => "test"}
    @custom_manager = Og.start(@opts)
    Aspects.setup
  end
  after(:all) do
      #The following currently break the spec's.  They must be used in after(:all){}
      #Object.module_eval{remove_const('@manager')}
      OgSpecHelper.stop(@custom_manager)
  end

#  it_should_behave_like "a manager containing expected instance classes"
#  it_should_behave_like "a manager with expected methods"
#  it_should_behave_like "a manager with expected options"
  it "should be an instance of the Og::Manager object." do
    @custom_manager.should be_an_instance_of(Og::Manager)
  end
  it "should contain a 'store' that is an instance of the Og::DBIAdapter." do
    @custom_manager.store.should be_an_instance_of(Og::DbiAdapter)
  end
  it "should contain an instance of a DBI database handle in its 'store.conn' attribute." do
    @custom_manager.store.conn.should be_an_instance_of(DBI::DatabaseHandle)
  end
  it "should contain an reference to itself (an instance of Og::Manager) in its 'store.ogmanager' attribute." do
    @custom_manager.store.ogmanager.object_id.should == @custom_manager.object_id
  end
  it "should be have an empty array as its 'models' attribute." do
    @custom_manager.models.should == []
  end
  it "should refer to the default database in its 'name' attribute." do
    @custom_manager.name.should == og_custom_manager_options[:name]
  end
  it "should refer to the default Og::Manager options in its 'options' attribute." do
    @custom_manager.options.should == og_custom_manager_options
  end
  it "should refer to the default Og::SqlStore typemap in its 'store.typemap' attribute." do
    @custom_manager.store.typemap.should == og_custom_type_map
  end
  it "should have DBI trace mode of 2 in its 'store.conn.trace_mode' attribute." do
    @custom_manager.store.conn.trace_mode.should == 2
  end
  it "should output the DBI trace to an IO instance through the 'store.conn.trace_output' attribute." do
    @custom_manager.store.conn.trace_output.should be_an_instance_of(IO)
  end
end

#describe "A connection to a new Og store manager" do
#  after(:each) do
#    @manager.store.disconnect if @manager.instance_of?(Og::Manager)
#  end 
#  it "should raise a DBI::InterfaceError for an unknown DBI driver." do
#    opt = OgSpecHelper.default_options.update(OG_CONFIG[:dbi_unknown])
#    lambda{Og.start(opt)}.should raise_error(DBI::InterfaceError, "Unable to load driver 'Unknown'")
#  end
#end 
  