1
0
Fork 0
mirror of https://github.com/paper-trail-gem/paper_trail.git synced 2022-11-09 11:33:19 -05:00
paper-trail-gem--paper_trail/test/unit/model_test.rb

1841 lines
64 KiB
Ruby
Raw Normal View History

2010-03-18 14:02:55 -04:00
require 'test_helper'
2014-11-03 20:20:37 -05:00
require 'time_travel_helper'
2009-05-27 11:21:20 -04:00
2011-02-08 12:16:35 -05:00
class HasPaperTrailModelTest < ActiveSupport::TestCase
context "A record with defined 'only' and 'ignore' attributes" do
2009-10-28 09:12:36 -04:00
setup { @article = Article.create }
should 'creation should change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
2010-03-18 14:02:55 -04:00
2009-10-28 09:12:36 -04:00
context 'which updates an ignored column' do
setup { @article.update_attributes :title => 'My first title' }
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
2009-10-28 09:12:36 -04:00
end
2013-09-19 07:34:13 -04:00
context 'which updates an ignored column with truly Proc' do
setup { @article.update_attributes :abstract => 'ignore abstract' }
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
end
context 'which updates an ignored column with falsy Proc' do
setup { @article.update_attributes :abstract => 'do not ignore abstract!' }
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
end
context 'which updates an ignored column, ignored with truly Proc and a selected column' do
setup { @article.update_attributes :title => 'My first title',
:content => 'Some text here.',
:abstract => 'ignore abstract'
}
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
should "show the new version in the model's `versions` association" do
assert_equal(2, @article.versions.size)
end
should 'have stored only non-ignored attributes' do
assert_equal ({'content' => [nil, 'Some text here.']}), @article.versions.last.changeset
end
2009-10-28 09:12:36 -04:00
end
2011-04-01 05:36:56 -04:00
2013-09-19 07:34:13 -04:00
context 'which updates an ignored column, ignored with falsy Proc and a selected column' do
setup { @article.update_attributes :title => 'My first title',
:content => 'Some text here.',
:abstract => 'do not ignore abstract'
}
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
should "show the new version in the model's `versions` association" do
assert_equal(2, @article.versions.size)
end
should 'have stored only non-ignored attributes' do
assert_equal ({'content' => [nil, 'Some text here.'], 'abstract' => [nil, 'do not ignore abstract']}), @article.versions.last.changeset
end
end
context 'which updates a selected column' do
setup { @article.update_attributes :content => 'Some text here.' }
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
should "show the new version in the model's `versions` association" do
assert_equal(2, @article.versions.size)
end
end
context 'which updates a non-ignored and non-selected column' do
setup { @article.update_attributes :abstract => 'Other abstract'}
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
end
2011-11-09 05:22:27 -05:00
context 'which updates a skipped column' do
setup { @article.update_attributes :file_upload => 'Your data goes here' }
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
end
2011-11-09 05:22:27 -05:00
context 'which updates a skipped column and a selected column' do
setup { @article.update_attributes :file_upload => 'Your data goes here', :content => 'Some text here.' }
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
2011-04-01 05:36:56 -04:00
should "show the new version in the model's `versions` association" do
assert_equal(2, @article.versions.size)
end
should 'have stored only non-skipped attributes' do
assert_equal ({'content' => [nil, 'Some text here.']}), @article.versions.last.changeset
end
2011-11-09 05:22:27 -05:00
context 'and when updated again' do
setup do
@article.update_attributes :file_upload => 'More data goes here', :content => 'More text here.'
@old_article = @article.versions.last
end
2011-11-09 05:22:27 -05:00
should 'have removed the skipped attributes when saving the previous version' do
assert_equal nil, PaperTrail.serializer.load(@old_article.object)['file_upload']
end
2011-11-09 05:22:27 -05:00
should 'have kept the non-skipped attributes in the previous version' do
assert_equal 'Some text here.', PaperTrail.serializer.load(@old_article.object)['content']
end
end
end
context 'which gets destroyed' do
setup { @article.destroy }
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
should "show the new version in the model's `versions` association" do
assert_equal(2, @article.versions.size)
end
end
end
context "A record with defined 'ignore' attribute" do
setup { @legacy_widget = LegacyWidget.create }
2011-11-09 05:22:27 -05:00
context 'which updates an ignored column' do
setup { @legacy_widget.update_attributes :version => 1 }
should 'not change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
end
2009-10-28 09:12:36 -04:00
end
context 'A record with defined "if" and "unless" attributes' do
2012-01-11 09:26:10 -05:00
setup { @translation = Translation.new :headline => 'Headline' }
context 'for non-US translations' do
setup { @translation.save }
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
2012-01-11 09:26:10 -05:00
context 'after update' do
setup { @translation.update_attributes :content => 'Content' }
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
2012-01-11 09:26:10 -05:00
end
context 'after destroy' do
setup { @translation.destroy }
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
end
2012-01-11 09:26:10 -05:00
end
context 'for US translations' do
setup { @translation.language_code = "US" }
context 'that are drafts' do
setup do
@translation.type = 'DRAFT'
@translation.save
end
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
context 'after update' do
setup { @translation.update_attributes :content => 'Content' }
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
end
2012-01-11 09:26:10 -05:00
end
context 'that are not drafts' do
setup { @translation.save }
should 'change the number of versions' do assert_equal(1, PaperTrail::Version.count) end
context 'after update' do
setup { @translation.update_attributes :content => 'Content' }
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
should "show the new version in the model's `versions` association" do
assert_equal(2, @translation.versions.size)
end
end
context 'after destroy' do
setup { @translation.destroy }
should 'change the number of versions' do assert_equal(2, PaperTrail::Version.count) end
should "show the new version in the model's `versions` association" do
assert_equal(2, @translation.versions.size)
end
end
2012-01-11 09:26:10 -05:00
end
end
end
2010-01-06 07:57:54 -05:00
2009-05-27 11:21:20 -04:00
context 'A new record' do
setup { @widget = Widget.new }
should 'not have any previous versions' do
assert_equal [], @widget.versions
end
should 'be live' do
assert @widget.live?
end
2009-05-27 11:21:20 -04:00
context 'which is then created' do
2014-05-30 17:38:23 -04:00
setup { @widget.update_attributes :name => 'Henry', :created_at => Time.now - 1.day }
2009-05-27 11:21:20 -04:00
should 'have one previous version' do
assert_equal 1, @widget.versions.length
end
should 'be nil in its previous version' do
assert_nil @widget.versions.first.object
assert_nil @widget.versions.first.reify
end
should 'record the correct event' do
assert_match /create/i, @widget.versions.first.event
end
should 'be live' do
assert @widget.live?
end
2014-05-30 17:38:23 -04:00
should 'use the widget created_at' do
assert_equal @widget.created_at.to_i, @widget.versions.first.created_at.to_i
end
should 'have changes' do
#TODO Postgres does not appear to pass back ActiveSupport::TimeWithZone,
# so chosing the lowest common denominator to test.
changes = {
'name' => [nil, 'Henry'],
'created_at' => [nil, @widget.created_at.to_time.utc],
'updated_at' => [nil, @widget.updated_at.to_time.utc],
'id' => [nil, @widget.id]
}
assert_equal Time, @widget.versions.last.changeset['updated_at'][1].class
assert_equal changes, @widget.versions.last.changeset
2011-07-13 04:23:13 -04:00
end
2009-05-27 11:21:20 -04:00
context 'and then updated without any changes' do
setup { @widget.touch }
2009-05-27 11:21:20 -04:00
should 'not have a new version' do
assert_equal 1, @widget.versions.length
end
end
context 'and then updated with changes' do
setup { @widget.update_attributes :name => 'Harry' }
should 'have two previous versions' do
assert_equal 2, @widget.versions.length
end
should 'be available in its previous version' do
assert_equal 'Harry', @widget.name
assert_not_nil @widget.versions.last.object
widget = @widget.versions.last.reify
assert_equal 'Henry', widget.name
assert_equal 'Harry', @widget.name
end
should 'have the same ID in its previous version' do
assert_equal @widget.id, @widget.versions.last.reify.id
end
should 'record the correct event' do
assert_match /update/i, @widget.versions.last.event
end
should 'have versions that are not live' do
assert @widget.versions.map(&:reify).compact.all? { |w| !w.live? }
end
should 'have stored changes' do
# Behavior for ActiveRecord 4 is different than ActiveRecord 3;
# AR4 includes the `updated_at` column in changes for updates, which is why we reject it from the right side of this assertion.
assert_equal ({'name' => ['Henry', 'Harry']}), PaperTrail.serializer.load(@widget.versions.last.object_changes).reject { |k,v| k.to_sym == :updated_at }
assert_equal ({'name' => ['Henry', 'Harry']}), @widget.versions.last.changeset.reject { |k,v| k.to_sym == :updated_at }
end
should 'return changes with indifferent access' do
2011-08-31 03:54:19 -04:00
assert_equal ['Henry', 'Harry'], @widget.versions.last.changeset[:name]
assert_equal ['Henry', 'Harry'], @widget.versions.last.changeset['name']
end
if defined?(ActiveRecord::IdentityMap) && ActiveRecord::IdentityMap.respond_to?(:without)
should 'not clobber the IdentityMap when reifying' do
module ActiveRecord::IdentityMap
class << self
alias :__without :without
def without(&block)
@unclobbered = true
__without(&block)
end
end
end
@widget.versions.last.reify
assert ActiveRecord::IdentityMap.instance_variable_get("@unclobbered")
end
end
2010-03-18 14:02:55 -04:00
context 'and has one associated object' do
setup do
@wotsit = @widget.create_wotsit :name => 'John'
end
should 'not copy the has_one association by default when reifying' do
reified_widget = @widget.versions.last.reify
assert_equal @wotsit, reified_widget.wotsit # association hasn't been affected by reifying
assert_equal @wotsit, @widget.wotsit(true) # confirm that the association is correct
end
should 'copy the has_one association when reifying with :has_one => true' do
reified_widget = @widget.versions.last.reify(:has_one => true)
assert_nil reified_widget.wotsit # wotsit wasn't there at the last version
assert_equal @wotsit, @widget.wotsit(true) # wotsit should still exist on live object
end
end
context 'and has many associated objects' do
setup do
@f0 = @widget.fluxors.create :name => 'f-zero'
@f1 = @widget.fluxors.create :name => 'f-one'
@reified_widget = @widget.versions.last.reify
end
should 'copy the has_many associations when reifying' do
assert_equal @widget.fluxors.length, @reified_widget.fluxors.length
assert_same_elements @widget.fluxors, @reified_widget.fluxors
assert_equal @widget.versions.length, @reified_widget.versions.length
assert_same_elements @widget.versions, @reified_widget.versions
end
end
2009-05-27 11:21:20 -04:00
context 'and then destroyed' do
setup do
@fluxor = @widget.fluxors.create :name => 'flux'
@widget.destroy
@reified_widget = PaperTrail::Version.last.reify
end
should 'record the correct event' do
assert_match /destroy/i, PaperTrail::Version.last.event
end
2009-05-27 11:21:20 -04:00
should 'have three previous versions' do
assert_equal 3, PaperTrail::Version.with_item_keys('Widget', @widget.id).length
2009-05-27 11:21:20 -04:00
end
should 'be available in its previous version' do
assert_equal @widget.id, @reified_widget.id
assert_equal @widget.attributes, @reified_widget.attributes
2009-05-27 11:21:20 -04:00
end
should 'be re-creatable from its previous version' do
assert @reified_widget.save
end
should 'restore its associations on its previous version' do
@reified_widget.save
assert_equal 1, @reified_widget.fluxors.length
2009-05-27 11:21:20 -04:00
end
2011-08-31 04:18:45 -04:00
should 'not have changes' do
assert_equal Hash.new, @widget.versions.last.changeset
end
2009-05-27 11:21:20 -04:00
end
end
end
end
# Test the serialisation and deserialisation.
2009-05-27 11:21:20 -04:00
# TODO: binary
context "A record's papertrail" do
setup do
@date_time = DateTime.now.utc
@time = Time.now
@date = Date.new 2009, 5, 29
@widget = Widget.create :name => 'Warble',
:a_text => 'The quick brown fox',
:an_integer => 42,
:a_float => 153.01,
:a_decimal => 2.71828,
:a_datetime => @date_time,
:a_time => @time,
:a_date => @date,
:a_boolean => true
@widget.update_attributes :name => nil,
:a_text => nil,
:an_integer => nil,
:a_float => nil,
:a_decimal => nil,
:a_datetime => nil,
:a_time => nil,
:a_date => nil,
:a_boolean => false
@previous = @widget.versions.last.reify
end
should 'handle strings' do
assert_equal 'Warble', @previous.name
end
should 'handle text' do
assert_equal 'The quick brown fox', @previous.a_text
end
should 'handle integers' do
assert_equal 42, @previous.an_integer
end
should 'handle floats' do
assert_in_delta 153.01, @previous.a_float, 0.001
end
should 'handle decimals' do
assert_in_delta 2.71828, @previous.a_decimal, 0.00001
end
should 'handle datetimes' do
2011-05-02 21:21:28 -04:00
assert_equal @date_time.to_time.utc.to_i, @previous.a_datetime.to_time.utc.to_i
2009-05-27 11:21:20 -04:00
end
should 'handle times' do
2011-05-02 21:21:28 -04:00
assert_equal @time.utc.to_i, @previous.a_time.utc.to_i
2009-05-27 11:21:20 -04:00
end
should 'handle dates' do
assert_equal @date, @previous.a_date
end
should 'handle booleans' do
assert @previous.a_boolean
end
context "after a column is removed from the record's schema" do
setup do
change_schema
Widget.connection.schema_cache.clear!
2009-05-27 11:21:20 -04:00
Widget.reset_column_information
assert_raise(NoMethodError) { Widget.new.sacrificial_column }
@last = @widget.versions.last
end
teardown do
restore_schema
end
2009-05-27 11:21:20 -04:00
should 'reify previous version' do
assert_kind_of Widget, @last.reify
end
should 'restore all forward-compatible attributes' do
2011-05-02 21:21:28 -04:00
assert_equal 'Warble', @last.reify.name
assert_equal 'The quick brown fox', @last.reify.a_text
assert_equal 42, @last.reify.an_integer
assert_in_delta 153.01, @last.reify.a_float, 0.001
assert_in_delta 2.71828, @last.reify.a_decimal, 0.00001
assert_equal @date_time.to_time.utc.to_i, @last.reify.a_datetime.to_time.utc.to_i
assert_equal @time.utc.to_i, @last.reify.a_time.utc.to_i
assert_equal @date, @last.reify.a_date
2009-05-27 11:21:20 -04:00
assert @last.reify.a_boolean
end
end
end
context 'A record' do
setup { @widget = Widget.create :name => 'Zaphod' }
context 'with PaperTrail globally disabled' do
setup do
PaperTrail.enabled = false
@count = @widget.versions.length
end
teardown { PaperTrail.enabled = true }
context 'when updated' do
setup { @widget.update_attributes :name => 'Beeblebrox' }
should 'not add to its trail' do
assert_equal @count, @widget.versions.length
end
end
end
2009-05-27 11:21:20 -04:00
context 'with its paper trail turned off' do
setup do
Widget.paper_trail_off!
2009-05-27 11:21:20 -04:00
@count = @widget.versions.length
end
teardown { Widget.paper_trail_on! }
2009-05-27 11:21:20 -04:00
context 'when updated' do
setup { @widget.update_attributes :name => 'Beeblebrox' }
should 'not add to its trail' do
assert_equal @count, @widget.versions.length
end
end
context 'when destroyed "without versioning"' do
should 'leave paper trail off after call' do
@widget.without_versioning :destroy
assert !Widget.paper_trail_enabled_for_model?
end
end
2009-05-27 11:21:20 -04:00
context 'and then its paper trail turned on' do
setup { Widget.paper_trail_on! }
2009-05-27 11:21:20 -04:00
context 'when updated' do
setup { @widget.update_attributes :name => 'Ford' }
should 'add to its trail' do
assert_equal @count + 1, @widget.versions.length
end
end
context 'when updated "without versioning"' do
setup do
@widget.without_versioning do
@widget.update_attributes :name => 'Ford'
end
# The model instance should yield itself for convenience purposes
@widget.without_versioning { |w| w.update_attributes :name => 'Nixon' }
end
should 'not create new version' do
assert_equal @count, @widget.versions.length
end
should 'enable paper trail after call' do
assert Widget.paper_trail_enabled_for_model?
end
end
context 'when receiving a method name as an argument' do
setup { @widget.without_versioning(:touch_with_version) }
should 'not create new version' do
assert_equal @count, @widget.versions.length
end
should 'enable paper trail after call' do
assert Widget.paper_trail_enabled_for_model?
end
end
2009-05-27 11:21:20 -04:00
end
end
end
context 'A papertrail with somebody making changes' do
setup do
@widget = Widget.new :name => 'Fidget'
end
context 'when a record is created' do
setup do
PaperTrail.whodunnit = 'Alice'
@widget.save
@version = @widget.versions.last # only 1 version
end
2009-05-27 11:21:20 -04:00
should 'track who made the change' do
assert_equal 'Alice', @version.whodunnit
assert_nil @version.originator
assert_equal 'Alice', @version.terminator
assert_equal 'Alice', @widget.originator
2009-05-27 11:21:20 -04:00
end
context 'when a record is updated' do
setup do
PaperTrail.whodunnit = 'Bob'
@widget.update_attributes :name => 'Rivet'
@version = @widget.versions.last
end
2009-05-27 11:21:20 -04:00
should 'track who made the change' do
assert_equal 'Bob', @version.whodunnit
assert_equal 'Alice', @version.originator
assert_equal 'Bob', @version.terminator
assert_equal 'Bob', @widget.originator
2009-05-27 11:21:20 -04:00
end
context 'when a record is destroyed' do
setup do
PaperTrail.whodunnit = 'Charlie'
@widget.destroy
@version = PaperTrail::Version.last
end
2009-05-27 11:21:20 -04:00
should 'track who made the change' do
assert_equal 'Charlie', @version.whodunnit
assert_equal 'Bob', @version.originator
assert_equal 'Charlie', @version.terminator
assert_equal 'Charlie', @widget.originator
2009-05-27 11:21:20 -04:00
end
end
end
end
end
context 'Timestamps' do
setup do
@wotsit = Wotsit.create! :name => 'wotsit'
end
should 'record timestamps' do
@wotsit.update_attributes! :name => 'changed'
assert_not_nil @wotsit.versions.last.reify.created_at
assert_not_nil @wotsit.versions.last.reify.updated_at
end
# Currently the gem generates a bunch of deprecation warnings about serialized attributes on AR 4.2
should 'not generate warning' do
# Tests that it doesn't try to write created_on as an attribute just because a created_on
# method exists.
warnings = capture(:stderr) { # Deprecation warning in Rails 3.2
assert_nothing_raised { # ActiveModel::MissingAttributeError in Rails 4
@wotsit.update_attributes! :name => 'changed'
}
}
assert_equal '', warnings
end
end
context 'A subclass' do
setup do
@foo = FooWidget.create
@foo.update_attributes! :name => 'Foo'
end
should 'reify with the correct type' do
# For some reason this test appears to be broken on AR4 in the test env. Executing it manually in the Rails console seems to work.. not sure what the issues is here.
assert_kind_of FooWidget, @foo.versions.last.reify if ActiveRecord::VERSION::MAJOR < 4
assert_equal @foo.versions.first, PaperTrail::Version.last.previous
assert_nil PaperTrail::Version.last.next
end
should 'should return the correct originator' do
PaperTrail.whodunnit = 'Ben'
@foo.update_attribute(:name, 'Geoffrey')
assert_equal PaperTrail.whodunnit, @foo.originator
end
context 'when destroyed' do
setup { @foo.destroy }
should 'reify with the correct type' do
assert_kind_of FooWidget, @foo.versions.last.reify
assert_equal @foo.versions[1], PaperTrail::Version.last.previous
assert_nil PaperTrail::Version.last.next
end
end
end
2009-06-19 11:46:33 -04:00
context 'An item with versions' do
setup do
@widget = Widget.create :name => 'Widget'
@widget.update_attributes :name => 'Fidget'
@widget.update_attributes :name => 'Digit'
end
context 'which were created over time' do
setup do
@created = 2.days.ago
@first_update = 1.day.ago
@second_update = 1.hour.ago
@widget.versions[0].update_attributes :created_at => @created
@widget.versions[1].update_attributes :created_at => @first_update
@widget.versions[2].update_attributes :created_at => @second_update
@widget.update_attribute :updated_at, @second_update
end
should 'return nil for version_at before it was created' do
assert_nil @widget.version_at(@created - 1)
end
should 'return how it looked when created for version_at its creation' do
assert_equal 'Widget', @widget.version_at(@created).name
end
should "return how it looked when created for version_at just before its first update" do
assert_equal 'Widget', @widget.version_at(@first_update - 1).name
end
should "return how it looked when first updated for version_at its first update" do
assert_equal 'Fidget', @widget.version_at(@first_update).name
end
should 'return how it looked when first updated for version_at just before its second update' do
assert_equal 'Fidget', @widget.version_at(@second_update - 1).name
end
should 'return how it looked when subsequently updated for version_at its second update' do
assert_equal 'Digit', @widget.version_at(@second_update).name
end
should 'return the current object for version_at after latest update' do
assert_equal 'Digit', @widget.version_at(1.day.from_now).name
end
context 'passing in a string representation of a timestamp' do
should 'still return a widget when appropriate' do
# need to add 1 second onto the timestamps before casting to a string, since casting a Time to a string drops the microseconds
assert_equal 'Widget', @widget.version_at((@created + 1.second).to_s).name
assert_equal 'Fidget', @widget.version_at((@first_update + 1.second).to_s).name
assert_equal 'Digit', @widget.version_at((@second_update + 1.second).to_s).name
end
end
end
context '.versions_between' do
setup do
@created = 30.days.ago
@first_update = 15.days.ago
@second_update = 1.day.ago
@widget.versions[0].update_attributes :created_at => @created
@widget.versions[1].update_attributes :created_at => @first_update
@widget.versions[2].update_attributes :created_at => @second_update
@widget.update_attribute :updated_at, @second_update
end
should 'return versions in the time period' do
assert_equal ['Fidget'], @widget.versions_between(20.days.ago, 10.days.ago).map(&:name)
assert_equal ['Widget', 'Fidget'], @widget.versions_between(45.days.ago, 10.days.ago).map(&:name)
2014-05-30 17:38:23 -04:00
assert_equal ['Fidget', 'Digit', 'Digit'], @widget.versions_between(16.days.ago, 1.minute.ago).map(&:name)
assert_equal [], @widget.versions_between(60.days.ago, 45.days.ago).map(&:name)
end
end
2009-06-19 11:46:33 -04:00
context 'on the first version' do
setup { @version = @widget.versions.first }
should 'have a nil previous version' do
assert_nil @version.previous
end
should 'return the next version' do
assert_equal @widget.versions[1], @version.next
end
2009-06-22 11:37:00 -04:00
should 'return the correct index' do
assert_equal 0, @version.index
end
2009-06-19 11:46:33 -04:00
end
context 'on the last version' do
setup { @version = @widget.versions.last }
should 'return the previous version' do
assert_equal @widget.versions[@widget.versions.length - 2], @version.previous
end
should 'have a nil next version' do
assert_nil @version.next
end
2009-06-22 11:37:00 -04:00
should 'return the correct index' do
assert_equal @widget.versions.length - 1, @version.index
end
2009-06-19 11:46:33 -04:00
end
end
2010-01-06 07:57:54 -05:00
context 'An item' do
setup do
@initial_title = 'Foobar'
@article = Article.new :title => @initial_title
end
2010-01-06 07:57:54 -05:00
context 'which is created' do
setup { @article.save }
should 'store fixed meta data' do
assert_equal 42, @article.versions.last.answer
end
should 'store dynamic meta data which is independent of the item' do
assert_equal '31 + 11 = 42', @article.versions.last.question
end
should 'store dynamic meta data which depends on the item' do
assert_equal @article.id, @article.versions.last.article_id
end
should 'store dynamic meta data based on a method of the item' do
assert_equal @article.action_data_provider_method, @article.versions.last.action
end
should 'store dynamic meta data based on an attribute of the item prior to creation' do
assert_equal nil, @article.versions.last.title
end
2010-01-06 07:57:54 -05:00
context 'and updated' do
setup do
@article.update_attributes! :content => 'Better text.', :title => 'Rhubarb'
end
2010-01-06 07:57:54 -05:00
should 'store fixed meta data' do
assert_equal 42, @article.versions.last.answer
end
should 'store dynamic meta data which is independent of the item' do
assert_equal '31 + 11 = 42', @article.versions.last.question
end
should 'store dynamic meta data which depends on the item' do
assert_equal @article.id, @article.versions.last.article_id
end
should 'store dynamic meta data based on an attribute of the item prior to the update' do
assert_equal @initial_title, @article.versions.last.title
end
2010-01-06 07:57:54 -05:00
end
2010-06-22 07:29:07 -04:00
context 'and destroyed' do
2010-01-06 07:57:54 -05:00
setup { @article.destroy }
should 'store fixed meta data' do
assert_equal 42, @article.versions.last.answer
end
should 'store dynamic meta data which is independent of the item' do
assert_equal '31 + 11 = 42', @article.versions.last.question
end
should 'store dynamic meta data which depends on the item' do
assert_equal @article.id, @article.versions.last.article_id
end
should 'store dynamic meta data based on an attribute of the item prior to the destruction' do
assert_equal @initial_title, @article.versions.last.title
end
2010-01-06 07:57:54 -05:00
end
end
end
context 'A reified item' do
setup do
widget = Widget.create :name => 'Bob'
%w( Tom Dick Jane ).each { |name| widget.update_attributes :name => name }
@version = widget.versions.last
@widget = @version.reify
end
should 'know which version it came from' do
assert_equal @version, @widget.version
end
should 'return its previous self' do
assert_equal @widget.versions[-2].reify, @widget.previous_version
end
end
context 'A non-reified item' do
setup { @widget = Widget.new }
should 'not have a previous version' do
assert_nil @widget.previous_version
end
should 'not have a next version' do
assert_nil @widget.next_version
end
context 'with versions' do
setup do
@widget.save
%w( Tom Dick Jane ).each { |name| @widget.update_attributes :name => name }
end
should 'have a previous version' do
assert_equal @widget.versions.last.reify.name, @widget.previous_version.name
end
should 'not have a next version' do
assert_nil @widget.next_version
end
end
end
context 'A reified item' do
setup do
@widget = Widget.create :name => 'Bob'
%w(Tom Dick Jane).each { |name| @widget.update_attributes :name => name }
@second_widget = @widget.versions[1].reify # first widget is `nil`
@last_widget = @widget.versions.last.reify
end
should 'have a previous version' do
assert_nil @second_widget.previous_version # `create` events return `nil` for `reify`
assert_equal @widget.versions[-2].reify.name, @last_widget.previous_version.name
end
should 'have a next version' do
assert_equal @widget.versions[2].reify.name, @second_widget.next_version.name
assert_equal @last_widget.next_version.name, @widget.name
end
end
context ":has_many :through" do
setup do
@book = Book.create :title => 'War and Peace'
@dostoyevsky = Person.create :name => 'Dostoyevsky'
@solzhenitsyn = Person.create :name => 'Solzhenitsyn'
end
should 'store version on source <<' do
count = PaperTrail::Version.count
@book.authors << @dostoyevsky
assert_equal 1, PaperTrail::Version.count - count
assert_equal PaperTrail::Version.last, @book.authorships.first.versions.first
end
should 'store version on source create' do
count = PaperTrail::Version.count
@book.authors.create :name => 'Tolstoy'
assert_equal 2, PaperTrail::Version.count - count
2014-12-17 00:33:07 -05:00
assert_same_elements [Person.last, Authorship.last], [PaperTrail::Version.order(:id).to_a[-2].item, PaperTrail::Version.last.item]
end
should 'store version on join destroy' do
@book.authors << @dostoyevsky
count = PaperTrail::Version.count
@book.authorships(true).last.destroy
assert_equal 1, PaperTrail::Version.count - count
assert_equal @book, PaperTrail::Version.last.reify.book
assert_equal @dostoyevsky, PaperTrail::Version.last.reify.person
end
should 'store version on join clear' do
@book.authors << @dostoyevsky
count = PaperTrail::Version.count
@book.authorships(true).destroy_all
assert_equal 1, PaperTrail::Version.count - count
assert_equal @book, PaperTrail::Version.last.reify.book
assert_equal @dostoyevsky, PaperTrail::Version.last.reify.person
end
end
# `serialized_attributes` is deprecated in ActiveRecord 5.0
if ::ActiveRecord::VERSION::MAJOR < 5
context 'When an attribute has a custom serializer' do
setup do
PaperTrail.config.serialized_attributes = true
@person = Person.new(:time_zone => "Samoa")
end
teardown { PaperTrail.config.serialized_attributes = false }
should "be an instance of ActiveSupport::TimeZone" do
assert_equal ActiveSupport::TimeZone, @person.time_zone.class
end
context 'when the model is saved' do
setup do
@changes_before_save = @person.changes.dup
@person.save!
end
# Test for serialization:
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
assert @person.versions.last.object_changes.length < 105, "object_changes length was #{@person.versions.last.object_changes.length}"
end
# It should store the serialized value.
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
assert_equal [nil, 'Samoa'], as_stored_in_version[:time_zone]
serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
assert_equal serialized_value, as_stored_in_version[:time_zone].last
end
# Tests for unserialization:
should 'version.changeset should convert the attribute value back to its original, unserialized value' do
unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
assert_equal unserialized_value, @person.versions.last.changeset[:time_zone].last
end
should "record.changes (before save) returns the original, unserialized values" do
assert_equal [NilClass, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
end
should 'version.changeset should be the same as record.changes was before the save' do
assert_equal @changes_before_save, @person.versions.last.changeset.delete_if { |key, val| key.to_sym == :id }
assert_equal [NilClass, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
end
context 'when that attribute is updated' do
setup do
@attribute_value_before_change = @person.time_zone
@person.assign_attributes({ :time_zone => 'Pacific Time (US & Canada)' })
@changes_before_save = @person.changes.dup
@person.save!
end
# Tests for serialization:
# Before the serialized attributes fix, the object/object_changes value that was stored was ridiculously long (58723).
should 'version.object should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
assert @person.versions.last.object.length < 105, "object length was #{@person.versions.last.object.length}"
end
# Need an additional clause to detect what version of ActiveRecord is being used for this test because AR4 injects the `updated_at` column into the changeset for updates to models
should 'version.object_changes should not have stored the default, ridiculously long (to_yaml) serialization of the TimeZone object' do
assert @person.versions.last.object_changes.length < (ActiveRecord::VERSION::MAJOR < 4 ? 105 : 118), "object_changes length was #{@person.versions.last.object_changes.length}"
end
# But now it stores the short, serialized value.
should 'version.object attribute should have stored the value returned by the attribute serializer' do
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object)]
assert_equal 'Samoa', as_stored_in_version[:time_zone]
serialized_value = Person::TimeZoneSerializer.dump(@attribute_value_before_change)
assert_equal serialized_value, as_stored_in_version[:time_zone]
end
should 'version.object_changes attribute should have stored the value returned by the attribute serializer' do
as_stored_in_version = HashWithIndifferentAccess[YAML::load(@person.versions.last.object_changes)]
assert_equal ['Samoa', 'Pacific Time (US & Canada)'], as_stored_in_version[:time_zone]
serialized_value = Person::TimeZoneSerializer.dump(@person.time_zone)
assert_equal serialized_value, as_stored_in_version[:time_zone].last
end
# Tests for unserialization:
should 'version.reify should convert the attribute value back to its original, unserialized value' do
unserialized_value = Person::TimeZoneSerializer.load(@attribute_value_before_change)
assert_equal unserialized_value, @person.versions.last.reify.time_zone
end
should 'version.changeset should convert the attribute value back to its original, unserialized value' do
unserialized_value = Person::TimeZoneSerializer.load(@person.time_zone)
assert_equal unserialized_value, @person.versions.last.changeset[:time_zone].last
end
should "record.changes (before save) returns the original, unserialized values" do
assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone], @changes_before_save[:time_zone].map(&:class)
end
should 'version.changeset should be the same as record.changes was before the save' do
assert_equal @changes_before_save, @person.versions.last.changeset
assert_equal [ActiveSupport::TimeZone, ActiveSupport::TimeZone], @person.versions.last.changeset[:time_zone].map(&:class)
end
end
end
end
end
context 'A new model instance which uses a custom PaperTrail::Version class' do
setup { @post = Post.new }
2011-04-01 05:36:56 -04:00
context 'which is then saved' do
setup { @post.save }
2012-05-20 00:02:54 -04:00
should 'change the number of post versions' do assert_equal 1, PostVersion.count end
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
end
end
2011-04-01 05:36:56 -04:00
context 'An existing model instance which uses a custom PaperTrail::Version class' do
setup { @post = Post.create }
2012-05-20 00:02:54 -04:00
should 'have one post version' do assert_equal(1, PostVersion.count) end
2011-04-01 05:36:56 -04:00
context 'on the first version' do
setup { @version = @post.versions.first }
2011-04-01 05:36:56 -04:00
should 'have the correct index' do
assert_equal 0, @version.index
end
end
2011-04-01 05:36:56 -04:00
2011-08-31 04:18:45 -04:00
should 'have versions of the custom class' do
assert_equal "PostVersion", @post.versions.first.class.name
end
2011-04-01 05:36:56 -04:00
context 'which is modified' do
setup { @post.update_attributes({ :content => "Some new content" }) }
2012-05-20 00:02:54 -04:00
should 'change the number of post versions' do assert_equal(2, PostVersion.count) end
should 'not change the number of versions' do assert_equal(0, PaperTrail::Version.count) end
should "not have stored changes when object_changes column doesn't exist" do
assert_nil @post.versions.last.changeset
end
end
end
2011-04-01 05:36:56 -04:00
context 'An overwritten default accessor' do
setup do
@song = Song.create :length => 4
@song.update_attributes :length => 5
end
should 'return "overwritten" value on live instance' do
assert_equal 5, @song.length
end
should 'return "overwritten" value on reified instance' do
assert_equal 4, @song.versions.last.reify.length
end
end
2010-10-28 13:16:30 -04:00
context 'An unsaved record' do
setup do
@widget = Widget.new
@widget.destroy
end
should 'not have a version created on destroy' do
assert @widget.versions.empty?
end
end
context 'A model with a custom association' do
2011-07-18 11:07:46 -04:00
setup do
@doc = Document.create
@doc.update_attributes :name => 'Doc 1'
end
2011-07-23 11:58:46 -04:00
should 'not respond to versions method' do
2011-07-18 11:07:46 -04:00
assert !@doc.respond_to?(:versions)
end
2011-07-23 11:58:46 -04:00
should 'create a new version record' do
2011-07-18 11:07:46 -04:00
assert_equal 2, @doc.paper_trail_versions.length
end
should 'respond to `next_version` as normal' do
assert_equal @doc.paper_trail_versions.last.reify.next_version.name, @doc.name
end
should 'respond to `previous_version` as normal' do
2011-07-18 11:07:46 -04:00
@doc.update_attributes :name => 'Doc 2'
assert_equal 3, @doc.paper_trail_versions.length
assert_equal 'Doc 1', @doc.previous_version.name
end
end
context 'The `on` option' do
context 'on create' do
setup do
Fluxor.instance_eval <<-END
has_paper_trail :on => [:create]
END
@fluxor = Fluxor.create
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for the create event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'create', @fluxor.versions.last.event
end
end
context 'on update' do
setup do
Fluxor.reset_callbacks :create
Fluxor.reset_callbacks :update
Fluxor.reset_callbacks :destroy
Fluxor.instance_eval <<-END
has_paper_trail :on => [:update]
END
@fluxor = Fluxor.create
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for the update event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'update', @fluxor.versions.last.event
end
end
context 'on destroy' do
setup do
Fluxor.reset_callbacks :create
Fluxor.reset_callbacks :update
Fluxor.reset_callbacks :destroy
Fluxor.instance_eval <<-END
has_paper_trail :on => [:destroy]
END
@fluxor = Fluxor.create
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for the destroy event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'destroy', @fluxor.versions.last.event
end
end
context 'allows a symbol to be passed' do
setup do
Fluxor.reset_callbacks :create
Fluxor.reset_callbacks :update
Fluxor.reset_callbacks :destroy
Fluxor.instance_eval <<-END
has_paper_trail :on => :create
END
@fluxor = Fluxor.create
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for hte create event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'create', @fluxor.versions.last.event
end
end
2011-07-18 11:07:46 -04:00
end
2010-10-28 13:16:30 -04:00
context 'A model with column version and custom version_method' do
setup do
@legacy_widget = LegacyWidget.create(:name => "foo", :version => 2)
end
should 'set version on create' do
assert_equal 2, @legacy_widget.version
end
should 'allow version updates' do
@legacy_widget.update_attributes :version => 3
assert_equal 3, @legacy_widget.version
end
should 'create a new version record' do
assert_equal 1, @legacy_widget.versions.size
end
end
context 'A reified item with a column -version- and custom version_method' do
setup do
widget = LegacyWidget.create(:name => "foo", :version => 2)
%w( bar baz ).each { |name| widget.update_attributes :name => name }
@version = widget.versions.last
@widget = @version.reify
end
should 'know which version it came from' do
assert_equal @version, @widget.custom_version
end
should 'return its previous self' do
assert_equal @widget.versions[-2].reify, @widget.previous_version
end
end
context 'custom events' do
context 'on create' do
setup do
Fluxor.reset_callbacks :create
Fluxor.reset_callbacks :update
Fluxor.reset_callbacks :destroy
Fluxor.instance_eval <<-END
has_paper_trail :on => [:create]
END
@fluxor = Fluxor.new.tap { |model| model.paper_trail_event = 'created' }
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for the created event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'created', @fluxor.versions.last.event
end
end
context 'on update' do
setup do
Fluxor.reset_callbacks :create
Fluxor.reset_callbacks :update
Fluxor.reset_callbacks :destroy
Fluxor.instance_eval <<-END
has_paper_trail :on => [:update]
END
@fluxor = Fluxor.create.tap { |model| model.paper_trail_event = 'name_updated' }
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for the name_updated event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'name_updated', @fluxor.versions.last.event
end
end
context 'on destroy' do
setup do
Fluxor.reset_callbacks :create
Fluxor.reset_callbacks :update
Fluxor.reset_callbacks :destroy
Fluxor.instance_eval <<-END
has_paper_trail :on => [:destroy]
END
@fluxor = Fluxor.create.tap { |model| model.paper_trail_event = 'destroyed' }
@fluxor.update_attributes :name => 'blah'
@fluxor.destroy
end
should 'only have a version for the destroy event' do
assert_equal 1, @fluxor.versions.length
assert_equal 'destroyed', @fluxor.versions.last.event
end
end
end
context '`PaperTrail::Config.version_limit` set' do
setup do
PaperTrail.config.version_limit = 2
@widget = Widget.create! :name => 'Henry'
6.times { @widget.update_attribute(:name, Faker::Lorem.word) }
end
teardown { PaperTrail.config.version_limit = nil }
should "limit the number of versions to 3 (2 plus the created at event)" do
assert_equal 'create', @widget.versions.first.event
assert_equal 3, @widget.versions.size
end
end
end
class HasPaperTrailModelTransactionalTest < ActiveSupport::TestCase
# These would have been done in test_helper.rb if using_mysql? is true
unless using_mysql?
self.use_transactional_fixtures = false
setup { DatabaseCleaner.start }
end
teardown do
Timecop.return
# This would have been done in test_helper.rb if using_mysql? is true
DatabaseCleaner.clean unless using_mysql?
end
context 'A model with a has_one association' do
setup { @widget = Widget.create :name => 'widget_0' }
context 'before the associated was created' do
setup do
@widget.update_attributes :name => 'widget_1'
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
end
context 'when reified' do
setup { @widget_0 = @widget.versions.last.reify(:has_one => true) }
should 'see the associated as it was at the time' do
assert_nil @widget_0.wotsit
end
should 'not persist changes to the live association' do
assert_equal @wotsit, @widget.wotsit(true)
end
end
end
context 'where the association is created between model versions' do
setup do
@wotsit = @widget.create_wotsit :name => 'wotsit_0'
Timecop.travel 1.second.since
@widget.update_attributes :name => 'widget_1'
end
context 'when reified' do
setup { @widget_0 = @widget.versions.last.reify(:has_one => true) }
should 'see the associated as it was at the time' do
assert_equal 'wotsit_0', @widget_0.wotsit.name
end
should 'not persist changes to the live association' do
assert_equal @wotsit, @widget.wotsit(true)
end
end
context 'and then the associated is updated between model versions' do
setup do
@wotsit.update_attributes :name => 'wotsit_1'
@wotsit.update_attributes :name => 'wotsit_2'
Timecop.travel 1.second.since
@widget.update_attributes :name => 'widget_2'
@wotsit.update_attributes :name => 'wotsit_3'
end
context 'when reified' do
setup { @widget_1 = @widget.versions.last.reify(:has_one => true) }
should 'see the associated as it was at the time' do
assert_equal 'wotsit_2', @widget_1.wotsit.name
end
should 'not persist changes to the live association' do
assert_equal 'wotsit_3', @widget.wotsit(true).name
end
end
context 'when reified opting out of has_one reification' do
setup { @widget_1 = @widget.versions.last.reify(:has_one => false) }
should 'see the associated as it is live' do
assert_equal 'wotsit_3', @widget_1.wotsit.name
end
end
end
context 'and then the associated is destroyed' do
setup do
@wotsit.destroy
end
context 'when reify' do
setup { @widget_1 = @widget.versions.last.reify(:has_one => true) }
should 'see the associated as it was at the time' do
assert_equal @wotsit, @widget_1.wotsit
end
should 'not persist changes to the live association' do
assert_nil @widget.wotsit(true)
end
end
context 'and then the model is updated' do
setup do
Timecop.travel 1.second.since
@widget.update_attributes :name => 'widget_3'
end
context 'when reified' do
setup { @widget_2 = @widget.versions.last.reify(:has_one => true) }
should 'see the associated as it was at the time' do
assert_nil @widget_2.wotsit
end
end
end
end
end
end
context 'A model with a has_many association' do
setup { @customer = Customer.create :name => 'customer_0' }
context 'updated before the associated was created' do
setup do
@customer.update_attributes! :name => 'customer_1'
@customer.orders.create! :order_date => Date.today
end
context 'when reified' do
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [], @customer_0.orders
end
should 'not persist changes to the live association' do
assert_not_equal [], @customer.orders(true)
end
end
context 'when reified with option mark_for_destruction' do
setup { @customer_0 = @customer.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
should 'mark the associated for destruction' do
assert_equal [true], @customer_0.orders.map(&:marked_for_destruction?)
end
end
end
context 'where the association is created between model versions' do
setup do
@order = @customer.orders.create! :order_date => 'order_date_0'
Timecop.travel 1.second.since
@customer.update_attributes :name => 'customer_1'
end
context 'when reified' do
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal ['order_date_0'], @customer_0.orders.map(&:order_date)
end
end
context 'and then a nested has_many association is created' do
setup do
@order.line_items.create! :product => 'product_0'
end
context 'when reified' do
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
should 'see the live version of the nested association' do
assert_equal ['product_0'], @customer_0.orders.first.line_items.map(&:product)
end
end
end
context 'and then the associated is updated between model versions' do
setup do
@order.update_attributes :order_date => 'order_date_1'
@order.update_attributes :order_date => 'order_date_2'
Timecop.travel 1.second.since
@customer.update_attributes :name => 'customer_2'
@order.update_attributes :order_date => 'order_date_3'
end
context 'when reified' do
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal ['order_date_2'], @customer_1.orders.map(&:order_date)
end
should 'not persist changes to the live association' do
assert_equal ['order_date_3'], @customer.orders(true).map(&:order_date)
end
end
context 'when reified opting out of has_many reification' do
setup { @customer_1 = @customer.versions.last.reify(:has_many => false) }
should 'see the associated as it is live' do
assert_equal ['order_date_3'], @customer_1.orders.map(&:order_date)
end
end
2014-11-03 20:20:37 -05:00
context 'and then the associated is destroyed' do
setup do
@order.destroy
end
context 'when reified' do
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal ['order_date_2'], @customer_1.orders.map(&:order_date)
end
should 'not persist changes to the live association' do
assert_equal [], @customer.orders(true)
end
2014-11-03 20:20:37 -05:00
end
end
end
2014-11-09 21:06:29 -05:00
context 'and then the associated is destroyed' do
setup do
@order.destroy
end
context 'when reified' do
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [@order.order_date], @customer_1.orders.map(&:order_date)
end
should 'not persist changes to the live association' do
assert_equal [], @customer.orders(true)
end
2014-11-09 21:06:29 -05:00
end
end
context 'and then the associated is destroyed between model versions' do
setup do
@order.destroy
Timecop.travel 1.second.since
@customer.update_attributes :name => 'customer_2'
end
context 'when reified' do
setup { @customer_1 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [], @customer_1.orders
end
end
end
context 'and then another association is added' do
setup do
@customer.orders.create! :order_date => 'order_date_1'
end
context 'when reified' do
setup { @customer_0 = @customer.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal ['order_date_0'], @customer_0.orders.map(&:order_date)
end
should 'not persist changes to the live association' do
assert_equal ['order_date_0', 'order_date_1'], @customer.orders(true).map(&:order_date).sort
end
end
context 'when reified with option mark_for_destruction' do
setup { @customer_0 = @customer.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
should 'mark the newly associated for destruction' do
assert @customer_0.orders.detect { |o| o.order_date == 'order_date_1'}.marked_for_destruction?
end
end
end
end
end
context 'A model with a has_many through association' do
setup { @book = Book.create :title => 'book_0' }
context 'updated before the associated was created' do
setup do
@book.update_attributes! :title => 'book_1'
@book.authors.create! :name => 'author_0'
end
context 'when reified' do
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [], @book_0.authors
end
should 'not persist changes to the live association' do
assert_equal ['author_0'], @book.authors(true).map(&:name)
end
end
context 'when reified with option mark_for_destruction' do
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
should 'mark the associated for destruction' do
assert_equal [true], @book_0.authors.map(&:marked_for_destruction?)
end
should 'mark the associated-through for destruction' do
assert_equal [true], @book_0.authorships.map(&:marked_for_destruction?)
end
end
end
context 'updated before it is associated with an existing one' do
setup do
person_existing = Person.create(:name => 'person_existing')
Timecop.travel 1.second.since
@book.update_attributes! :title => 'book_1'
@book.authors << person_existing
end
context 'when reified' do
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [], @book_0.authors
end
end
context 'when reified with option mark_for_destruction' do
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
should 'not mark the associated for destruction' do
assert_equal [false], @book_0.authors.map(&:marked_for_destruction?)
end
should 'mark the associated-through for destruction' do
assert_equal [true], @book_0.authorships.map(&:marked_for_destruction?)
end
end
end
context 'where the association is created between model versions' do
setup do
@author = @book.authors.create! :name => 'author_0'
@person_existing = Person.create(:name => 'person_existing')
Timecop.travel 1.second.since
@book.update_attributes! :title => 'book_1'
end
context 'when reified' do
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal ['author_0'], @book_0.authors.map(&:name)
end
end
context 'and then the associated is updated between model versions' do
setup do
@author.update_attributes :name => 'author_1'
@author.update_attributes :name => 'author_2'
Timecop.travel 1.second.since
@book.update_attributes :title => 'book_2'
@author.update_attributes :name => 'author_3'
end
context 'when reified' do
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal ['author_2'], @book_1.authors.map(&:name)
end
should 'not persist changes to the live association' do
assert_equal ['author_3'], @book.authors(true).map(&:name)
end
end
context 'when reified opting out of has_many reification' do
setup { @book_1 = @book.versions.last.reify(:has_many => false) }
should 'see the associated as it is live' do
assert_equal ['author_3'], @book_1.authors.map(&:name)
end
end
end
2014-11-09 21:06:29 -05:00
context 'and then the associated is destroyed' do
setup do
@author.destroy
end
context 'when reified' do
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [@author.name], @book_1.authors.map(&:name)
end
should 'not persist changes to the live association' do
assert_equal [], @book.authors(true)
end
2014-11-09 21:06:29 -05:00
end
end
context 'and then the associated is destroyed between model versions' do
setup do
@author.destroy
Timecop.travel 1.second.since
@book.update_attributes :title => 'book_2'
end
context 'when reified' do
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [], @book_1.authors
end
end
end
context 'and then the associated is dissociated between model versions' do
setup do
@book.authors = []
Timecop.travel 1.second.since
@book.update_attributes :title => 'book_2'
end
context 'when reified' do
setup { @book_1 = @book.versions.last.reify(:has_many => true) }
should 'see the associated as it was at the time' do
assert_equal [], @book_1.authors
end
end
end
context 'and then another associated is created' do
setup do
@book.authors.create! :name => 'author_1'
end
context 'when reified' do
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
should 'only see the first associated' do
assert_equal ['author_0'], @book_0.authors.map(&:name)
end
should 'not persist changes to the live association' do
assert_equal ['author_0', 'author_1'], @book.authors(true).map(&:name)
end
end
context 'when reified with option mark_for_destruction' do
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
should 'mark the newly associated for destruction' do
assert @book_0.authors.detect { |a| a.name == 'author_1' }.marked_for_destruction?
end
should 'mark the newly associated-through for destruction' do
assert @book_0.authorships.detect { |as| as.person.name == 'author_1' }.marked_for_destruction?
end
end
end
context 'and then an existing one is associated' do
setup do
@book.authors << @person_existing
end
context 'when reified' do
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
should 'only see the first associated' do
assert_equal ['author_0'], @book_0.authors.map(&:name)
end
should 'not persist changes to the live association' do
assert_equal ['author_0', 'person_existing'], @book.authors(true).map(&:name).sort
end
end
context 'when reified with option mark_for_destruction' do
setup { @book_0 = @book.versions.last.reify(:has_many => true, :mark_for_destruction => true) }
should 'not mark the newly associated for destruction' do
assert !@book_0.authors.detect { |a| a.name == 'person_existing' }.marked_for_destruction?
end
should 'mark the newly associated-through for destruction' do
assert @book_0.authorships.detect { |as| as.person.name == 'person_existing' }.marked_for_destruction?
end
end
end
end
context 'updated before the associated without paper_trail was created' do
setup do
@book.update_attributes! :title => 'book_1'
@book.editors.create! :name => 'editor_0'
end
context 'when reified' do
setup { @book_0 = @book.versions.last.reify(:has_many => true) }
should 'see the live association' do
assert_equal ['editor_0'], @book_0.editors.map(&:name)
end
end
end
end
end