$:.unshift File.join(File.dirname(__FILE__), 'lib')

$DBG = true

require 'test/unit'

require 'og'

class TCOgStore < Test::Unit::TestCase # :nodoc: all
  include Og

# Not needed thanks to Og's two-pass algorithm.
#  class Comment; end 

  class User
    property :name, Og::VarChar(32), :unique => true

    def initialize(name = nil)
      @name = name
    end
  end

  class Category
    property :title, Og::VarChar(32), :unique => true
    joins_many Article

    def initialize(title = nil)
      @title = title
    end
  end
    
  class Article
    property :title, :body, String
    has_many Comment
    has_one :author, User
    refers_to :owner, User
    many_to_many Category

    def initialize(body = nil)
      @body = body
    end
  end

  class NewArticle < Article
    property :more_text, String
  end

  class Comment
    property :body, String
    property :hits, Fixnum
    # lets define the relation name just for fun.
    belongs_to :article, Article
    belongs_to User
    after "$schema_after=1", :on => :og_create_schema

    order 'hits ASC'
    
    def initialize(body = nil, user = nil)
      @body = body
      @user = user
      @hits = 0
    end
  end
  
  class Bugger
    property :name, String
    many_to_many Bugger
  end

  def setup
    @og = nil
  end
  
  def teardown
    @og.store.close
    @og.store.class.destroy(@og.options)
    @og = nil
  end

=begin
  def test_kirby
    @og = Og.setup(
      :destroy => true,
      :store => :kirby,
      :name => 'test',
      :embedded => true
    )
    features_test
#    conversions_test
  end
=end
=begin  
  def test_psql
    @og = Og.setup(
      :destroy => true,
      :store => :psql,
      :name => 'test',
      :min_messages => 'ERROR',
      :user => 'postgres',
      :password => 'navelrulez'
    )
    features_test
    conversions_test
  end
=end
#=begin
  def test_mysql
    @og = Og.setup(
      :destroy => true,
      :store => :mysql,
      :name => 'test',
      :user => 'root',
      :table_type => 'InnoDB',
      :password => 'navelrulez'
    )
    features_test
#    conversions_test
  end
#=end
=begin
  def test_sqlite
    @og = Og.setup(
      :destroy => true,
      :store => :sqlite,
      :name => 'test'
    )    
    features_test
    conversions_test
  end
=end
=begin
  def test_memory
    @og = Og.setup(
      :store => :memory,
      :name => :test,
      :destroy => true
    )  
    features_test
  end
=end

  def features_test
    u = User.create('gmosx')

    a1 = Article.new('Article 1')
    @og.store.save(a1)  

    a2 = @og.store.load(1, Article)

    assert_equal a1.body, a2.body

    # custom select.
    
    acs = Article.select("SELECT * FROM #{Article.table} WHERE oid=1")
    assert_equal 'Article 1', acs.first.body

    acs = Article.select_one("SELECT * FROM #{Article.table} WHERE oid=1")
    assert_equal 'Article 1', acs.body

    acs = Article.select_one("WHERE oid=1")
    assert_equal 'Article 1', acs.body

    acs = Article.find(:sql => "SELECT * FROM #{Article.table} WHERE oid=1")
    assert_equal 'Article 1', acs.first.body

    # prefered way?
    
    acs = Article.find(:sql => "WHERE oid=1")
    assert_equal 'Article 1', acs.first.body
    
    # exist?
    
    assert Article.exist?(1)
    assert_equal nil, Article.exist?(999)

    # update
    
    a = Article[1]
    a.body = 'Changed'
    # test affected rows.
    assert_equal 1, a.save    
    
    # 
    
    a3 = Article.create do |a|
      a.title = 'Title 3'
      a.body = 'Article 3'
    end

    a0 = Article.one(:condition => "body='Article 3'")
    assert_equal 'Article 3', a0.body
    
    @og.store.delete(a3)

    assert @og.store.load(1, Article)
    assert !Article[2]
    assert Article.load(1)

    a2.delete

    a4 = Article.new('Article 4')
    a4.author = u
    a4.owner = u
    a4.save

    # add some children.

    a4.comments << Comment.new('Comment 1', u)
    a4.comments.push(Comment.new('Comment 2', u))
    a4.comments.add(Comment.new('Comment 3', u))

    assert_equal 3, a4.comments.size
    
    # test override bug.
    assert a4.comments(:condition => '1 = 1').first
    assert_not_equal a4.comments[1].body, a4.comments[0].body
    assert_not_equal a4.comments[0].body, a4.comments[1].body
  
    c1 = Comment.new('Comment 4')
    c1.article = a4
    c1.user = u
    c1.save

    c1.reload
    assert_equal 'Comment 4', c1.body

    # count
    
    assert_equal 4, @og.store.count(:class => Comment)
    assert_equal 1, @og.store.count(:class => Comment, :condition => "body = 'Comment 4'")
    assert_equal 4, Comment.count
    assert_equal 1, Comment.count(:condition => "body = 'Comment 2'")

    # update_properties

    assert_equal 4, Comment.update('@hits = @hits + 1')
    cc = Comment[1]
    assert_equal 1, cc.hits
    Comment.update('@hits = @hits + 1', :condition => 'oid = 1')
    cc.reload
    assert_equal 2, cc.hits
    cc.update_by_sql '@hits = @hits + 1'
    cc.reload
    assert_equal 3, cc.hits

    # update selected properties.

    cc.hits = 5    
    cc.update :only => :hits
    assert_equal 5, cc.hits
    cc.reload
    assert_equal 5, cc.hits
    
    cc.hits += 1
    cc.body = 'Wow!'
    cc.update_properties :hits, :body
    cc.reload
    assert_equal 'Wow!', cc.body
        
    # join

    c3 = Comment.one(:condition => "body = 'Comment 4'", :include => :user)
    assert_equal 'Comment 4', c3.body
    # is eagerly loaded.
    assert_equal 'gmosx', c3.user.name
    assert_equal 'Article 4', c3.article.body
    
    comments = a4.comments(:reload => true, :include => [:user, :article])
    assert_equal 'gmosx', comments[0].user.name
    assert_equal 'Article 4', comments.first.article.body
    
    # thanks to reloading detects the manually added
    # comment.

    assert_equal 4, a4.comments(:reload => true).size

    assert_equal 2, a4.comments(:limit => 2, :reload => true).size
    
    # bug:
    assert_equal 4, a4.comments(:reload => true).size

    a4.comments.delete(c1)
    assert_equal 3, a4.comments.size

    assert_equal 3, @og.store.count(:class => Comment)

    a4.comments.delete_all
    assert_equal 0, a4.comments.size

    # has_one
    
    assert_equal 'gmosx', a4.author.name

    # refers_to
    
    assert_equal 'gmosx', a4.owner.name

    # generated finders
        
    assert User.respond_to?(:find_by_name)
    assert Comment.respond_to?(:find_by_body)
    
    u = User.find_by_name('gmosx')
    assert_equal 'gmosx', u.name
    
    # inspection.

    assert_equal 2, Comment.relations.size

    rel = Comment.relation(:article)
    assert_equal Og::BelongsTo, rel.class
    
    # joins_many / many_to_many relations.

    c1 = Category.create('News')
    c2 = Category.create('Sports')
    a = Article.create('Hello')
    a.categories << c1
    a.save
    a.categories << c2
    a.save

    assert_equal 2, a.categories.size

    a = Article.find_by_body('Hello').first
    assert_equal 2, a.categories.size
    assert_equal 'News', a.categories[0].title

    c = Category.find_by_title('News')
    assert_equal 1, c.articles.size
     
     
    # bug: extended class and many_to_many.

    na = NewArticle.create('Bug')
    na.categories << c1
    na.categories << c2
    assert_equal 2, na.categories.size

    # bug: self join bug.
     
    b1 = Bugger.create
    b2 = Bugger.create

    b1.buggers << b2
    
    assert b1.buggers.first
    
    # bug: refers_to nil
    
    a = Article.create
    a.owner = nil
    
    assert_equal 1, $schema_after
  end
  
  def conversions_test
    store = @og.store

    assert_equal '13', store.quote(13)
    assert_equal '13.23', store.quote(13.23)
    assert_equal "'can''t quote'", store.quote("can't quote")

    t = Time.now

    assert_equal t.day, store.parse_timestamp(store.timestamp(t)).day
    assert_equal t.year, store.parse_timestamp(store.timestamp(t)).year
    assert_equal t.month, store.parse_timestamp(store.timestamp(t)).month
    assert_equal t.hour, store.parse_timestamp(store.timestamp(t)).hour
    assert_equal t.min, store.parse_timestamp(store.timestamp(t)).min
    assert_equal t.sec, store.parse_timestamp(store.timestamp(t)).sec

    d = Date.new

    assert_equal d, store.parse_date(store.date(d))
  end
  
end