require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")

describe "Sequel::Plugins::Timestamps" do
  before do
    dc = Object.new
    dc.instance_eval do
      def now
        '2009-08-01'
      end
    end
    Sequel.datetime_class = dc
    @c = Class.new(Sequel::Model(:t))
    @c.class_eval do
      columns :id, :created_at, :updated_at
      plugin :timestamps
      def _save_refresh(*) end
      db.reset
    end
  end 
  after do
    Sequel.datetime_class = Time
  end
  
  it "should handle validations on the timestamp fields for new objects" do
    @c.plugin :timestamps, :update_on_create=>true
    o = @c.new
    def o.validate
      errors.add(model.create_timestamp_field, 'not present') unless send(model.create_timestamp_field)
      errors.add(model.update_timestamp_field, 'not present') unless send(model.update_timestamp_field)
    end
    o.valid?.must_equal true
  end

  it "should set timestamp fields when skipping validations" do
    @c.plugin :timestamps
    @c.new.save(:validate=>false)
    @c.db.sqls.must_equal ["INSERT INTO t (created_at) VALUES ('2009-08-01')"]
  end

  it "should set the create timestamp field on creation" do
    o = @c.create
    @c.db.sqls.must_equal ["INSERT INTO t (created_at) VALUES ('2009-08-01')"]
    o.created_at.must_equal '2009-08-01'
  end

  it "should set the update timestamp field on update" do
    o = @c.load(:id=>1).save
    @c.db.sqls.must_equal ["UPDATE t SET updated_at = '2009-08-01' WHERE (id = 1)"]
    o.updated_at.must_equal '2009-08-01'
  end

  it "should leave manually set update timestamp, if :allow_manual_update was given" do
    o = @c.load(:id=>1).update(:updated_at=>Date.new(2016))
    @c.db.sqls.must_equal ["UPDATE t SET updated_at = '2009-08-01' WHERE (id = 1)"]
    o.updated_at.must_equal '2009-08-01'

    @c.plugin :timestamps, :allow_manual_update=>true
    o = @c.load(:id=>1).update(:updated_at=>Date.new(2016))
    @c.db.sqls.must_equal ["UPDATE t SET updated_at = '2016-01-01' WHERE (id = 1)"]
    o.updated_at.must_equal Date.new(2016)
  end

  it "should work with current_datetime_timestamp extension" do
    Sequel.datetime_class = Time
    @c.dataset = @c.dataset.extension(:current_datetime_timestamp)
    o = @c.create
    @c.db.sqls.must_equal ["INSERT INTO t (created_at) VALUES (CURRENT_TIMESTAMP)"]
    o = @c.load(:id=>1).save
    @c.db.sqls.must_equal ["UPDATE t SET updated_at = CURRENT_TIMESTAMP WHERE (id = 1)"]
  end

  it "should not update the update timestamp on creation" do
    @c.create.updated_at.must_be_nil
  end

  it "should use the same value for the creation and update timestamps when creating if the :update_on_create option is given" do
    @c.plugin :timestamps, :update_on_create=>true
    o = @c.create
    sqls = @c.db.sqls
    sqls.shift.must_match(/INSERT INTO t \((creat|updat)ed_at, (creat|updat)ed_at\) VALUES \('2009-08-01', '2009-08-01'\)/)
    sqls.must_equal []
    o.created_at.must_be :===, o.updated_at
  end

  it "should allow specifying the create timestamp field via the :create option" do
    c = Class.new(Sequel::Model(:t))
    c.class_eval do
      columns :id, :c
      plugin :timestamps, :create=>:c
      def _save_refresh(*) end
    end
    o = c.create
    c.db.sqls.must_equal ["INSERT INTO t (c) VALUES ('2009-08-01')"]
    o.c.must_equal '2009-08-01'
  end

  it "should allow specifying the update timestamp field via the :update option" do
    c = Class.new(Sequel::Model(:t))
    c.class_eval do
      columns :id, :u
      plugin :timestamps, :update=>:u
      db.reset
    end
    o = c.load(:id=>1).save
    c.db.sqls.must_equal ["UPDATE t SET u = '2009-08-01' WHERE (id = 1)"]
    o.u.must_equal '2009-08-01'
  end

  it "should not raise an error if the model doesn't have the timestamp columns" do
    c = Class.new(Sequel::Model(:t))
    c.class_eval do
      columns :id, :x
      plugin :timestamps
      db.reset
      def _save_refresh; self end
    end
    c.create(:x=>2)
    c.load(:id=>1, :x=>2).save
    c.db.sqls.must_equal ["INSERT INTO t (x) VALUES (2)", "UPDATE t SET x = 2 WHERE (id = 1)"] 
  end

  it "should not overwrite an existing create timestamp" do
    o = @c.create(:created_at=>'2009-08-03')
    @c.db.sqls.must_equal ["INSERT INTO t (created_at) VALUES ('2009-08-03')"]
    o.created_at.must_equal '2009-08-03'
  end

  it "should overwrite an existing create timestamp if the :force option is used" do
    @c.plugin :timestamps, :force=>true
    o = @c.create(:created_at=>'2009-08-03')
    @c.db.sqls.must_equal ["INSERT INTO t (created_at) VALUES ('2009-08-01')"]
    o.created_at.must_equal '2009-08-01'
  end

  it "should set update timestamp to same timestamp as create timestamp when setting creating timestamp" do
    i = 1
    (class << (Sequel.datetime_class); self end).send(:define_method, :now){"2009-08-0#{i+=1}"}
    @c.plugin :timestamps, :update_on_create=>true
    o = @c.create
    sqls = @c.db.sqls
    sqls.length.must_equal 1
    ["INSERT INTO t (created_at, updated_at) VALUES ('2009-08-02', '2009-08-02')",
     "INSERT INTO t (updated_at, created_at) VALUES ('2009-08-02', '2009-08-02')"].must_include sqls.first
    o.created_at.must_equal '2009-08-02'
    o.updated_at.must_equal '2009-08-02'
  end

  it "should set update timestamp when using not overriding create timestamp" do
    i = 1
    (class << (Sequel.datetime_class); self end).send(:define_method, :now){"2009-08-0#{i+=1}"}
    @c.plugin :timestamps, :update_on_create=>true
    o = @c.create(:created_at=>'2009-08-10')
    sqls = @c.db.sqls
    sqls.length.must_equal 1
    ["INSERT INTO t (created_at, updated_at) VALUES ('2009-08-10', '2009-08-02')",
     "INSERT INTO t (updated_at, created_at) VALUES ('2009-08-02', '2009-08-10')"].must_include sqls.first
    o.created_at.must_equal '2009-08-10'
    o.updated_at.must_equal '2009-08-02'
  end

  it "should have create_timestamp_field give the create timestamp field" do
    @c.create_timestamp_field.must_equal :created_at
    @c.plugin :timestamps, :create=>:c
    @c.create_timestamp_field.must_equal :c
  end

  it "should have update_timestamp_field give the update timestamp field" do
    @c.update_timestamp_field.must_equal :updated_at
    @c.plugin :timestamps, :update=>:u
    @c.update_timestamp_field.must_equal :u
  end

  it "should have create_timestamp_overwrite? give the whether to overwrite an existing create timestamp" do
    @c.create_timestamp_overwrite?.must_equal false
    @c.plugin :timestamps, :force=>true
    @c.create_timestamp_overwrite?.must_equal true
  end

  it "should have set_update_timestamp_on_create? give whether to set the update timestamp on create" do
    @c.set_update_timestamp_on_create?.must_equal false
    @c.plugin :timestamps, :update_on_create=>true
    @c.set_update_timestamp_on_create?.must_equal true
  end

  it "should work with subclasses" do
    c = Class.new(@c)
    o = c.create
    o.created_at.must_equal '2009-08-01'
    o.updated_at.must_be_nil
    o = c.load(:id=>1).save
    o.updated_at.must_equal '2009-08-01'
    c.db.sqls.must_equal ["INSERT INTO t (created_at) VALUES ('2009-08-01')", "UPDATE t SET updated_at = '2009-08-01' WHERE (id = 1)"]
    c.create(:created_at=>'2009-08-03').created_at.must_equal '2009-08-03'

    c.class_eval do
      columns :id, :c, :u
      plugin :timestamps, :create=>:c, :update=>:u, :force=>true, :update_on_create=>true
    end
    c2 = Class.new(c)
    c2.db.reset
    o = c2.create
    o.c.must_equal '2009-08-01'
    o.u.must_be :===, o.c 
    c2.db.sqls.first.must_match(/INSERT INTO t \([cu], [cu]\) VALUES \('2009-08-01', '2009-08-01'\)/)
    c2.db.reset
    o = c2.load(:id=>1).save
    o.u.must_equal '2009-08-01'
    c2.db.sqls.must_equal ["UPDATE t SET u = '2009-08-01' WHERE (id = 1)"]
    c2.create(:c=>'2009-08-03').c.must_equal '2009-08-01'
  end
end
