spec/integration/integration_spec.rb in mongoid-history-0.8.0 vs spec/integration/integration_spec.rb in mongoid-history-0.8.1

- old
+ new

@@ -1,9 +1,9 @@ require 'spec_helper' describe Mongoid::History do - before :all do + before :each do class Post include Mongoid::Document include Mongoid::Timestamps include Mongoid::History::Trackable @@ -27,11 +27,12 @@ include Mongoid::History::Trackable field :t, as: :title field :body embedded_in :commentable, polymorphic: true - track_history on: %i[title body], scope: :post, track_create: true, track_destroy: true + # BUG: see https://github.com/mongoid/mongoid-history/issues/223, modifier_field_optional should not be necessary + track_history on: %i[title body], scope: :post, track_create: true, track_destroy: true, modifier_field_optional: true end class Section include Mongoid::Document include Mongoid::Timestamps @@ -52,11 +53,11 @@ field :phone field :address field :city field :country field :aliases, type: Array - track_history except: %i[email updated_at] + track_history except: %i[email updated_at], modifier_field_optional: true end class Tag include Mongoid::Document # include Mongoid::Timestamps (see: https://github.com/mongoid/mongoid/issues/3078) @@ -68,20 +69,26 @@ track_history on: [:title], scope: :post, track_create: true, track_destroy: true, modifier_field: :updated_by end class Foo < Comment end + end - @persisted_history_options = Mongoid::History.trackable_class_options + after :each do + Object.send(:remove_const, :Post) + Object.send(:remove_const, :Comment) + Object.send(:remove_const, :Section) + Object.send(:remove_const, :User) + Object.send(:remove_const, :Tag) + Object.send(:remove_const, :Foo) end - before(:each) { Mongoid::History.trackable_class_options = @persisted_history_options } let(:user) { User.create!(name: 'Aaron', email: 'aaron@randomemail.com', aliases: ['bob'], country: 'Canada', city: 'Toronto', address: '21 Jump Street') } let(:another_user) { User.create!(name: 'Another Guy', email: 'anotherguy@randomemail.com') } let(:post) { Post.create!(title: 'Test', body: 'Post', modifier: user, views: 100) } let(:comment) { post.comments.create!(title: 'test', body: 'comment', modifier: user) } - let(:tag) { Tag.create!(title: 'test') } + let(:tag) { Tag.create!(title: 'test', updated_by: user) } describe 'track' do describe 'on creation' do it 'should have one history track in comment' do expect(comment.history_tracks.count).to eq(1) @@ -94,11 +101,11 @@ it 'should not assign title and body on original' do expect(comment.history_tracks.first.original).to eq({}) end it 'should assign modifier' do - expect(comment.history_tracks.first.modifier).to eq(user) + expect(comment.history_tracks.first.modifier.id).to eq(user.id) end it 'should assign version' do expect(comment.history_tracks.first.version).to eq(1) end @@ -148,101 +155,101 @@ describe 'on update non-embedded' do it 'should create a history track if changed attributes match tracked attributes' do post # This will create history track records for creation expect do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') end.to change(Tracker, :count).by(1) end it 'should not create a history track if changed attributes do not match tracked attributes' do post # This will create history track records for creation expect do - post.update_attributes(rating: 'untracked') + post.update_attributes!(rating: 'untracked') end.to change(Tracker, :count).by(0) end it 'should assign modified fields' do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.history_tracks.last.modified).to eq( 'title' => 'Another Test' ) end it 'should assign method field' do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.history_tracks.last.action).to eq('update') end it 'should assign original fields' do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.history_tracks.last.original).to eq( 'title' => 'Test' ) end it 'should assign modifier' do - post.update_attributes(title: 'Another Test') - expect(post.history_tracks.first.modifier).to eq(user) + post.update_attributes!(title: 'Another Test') + expect(post.history_tracks.first.modifier.id).to eq(user.id) end it 'should assign version on history tracks' do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.history_tracks.first.version).to eq(1) end it 'should assign version on post' do expect(post.version).to eq(1) # Created - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.version).to eq(2) # Updated end it 'should assign scope' do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.history_tracks.first.scope).to eq('post') end it 'should assign association_chain' do - post.update_attributes(title: 'Another Test') + post.update_attributes!(title: 'Another Test') expect(post.history_tracks.last.association_chain).to eq([{ 'id' => post.id, 'name' => 'Post' }]) end it 'should exclude defined options' do name = user.name - user.update_attributes(name: 'Aaron2', email: 'aaronsnewemail@randomemail.com') + user.update_attributes!(name: 'Aaron2', email: 'aaronsnewemail@randomemail.com') expect(user.history_tracks.last.original.keys).to eq(['n']) expect(user.history_tracks.last.original['n']).to eq(name) expect(user.history_tracks.last.modified.keys).to eq(['n']) expect(user.history_tracks.last.modified['n']).to eq(user.name) end it 'should undo field changes' do name = user.name - user.update_attributes(name: 'Aaron2', email: 'aaronsnewemail@randomemail.com') + user.update_attributes!(name: 'Aaron2', email: 'aaronsnewemail@randomemail.com') user.history_tracks.last.undo! nil expect(user.reload.name).to eq(name) end it 'should undo non-existing field changes' do post = Post.create!(modifier: user, views: 100) expect(post.reload.title).to be_nil - post.update_attributes(title: 'Aaron2') + post.update_attributes!(title: 'Aaron2') expect(post.reload.title).to eq('Aaron2') - post.history_tracks.last.undo! nil + post.history_tracks.last.undo! user expect(post.reload.title).to be_nil end it 'should track array changes' do aliases = user.aliases - user.update_attributes(aliases: %w[bob joe]) + user.update_attributes!(aliases: %w[bob joe]) expect(user.history_tracks.last.original['aliases']).to eq(aliases) expect(user.history_tracks.last.modified['aliases']).to eq(user.aliases) end it 'should undo array changes' do aliases = user.aliases - user.update_attributes(aliases: %w[bob joe]) + user.update_attributes!(aliases: %w[bob joe]) user.history_tracks.last.undo! nil expect(user.reload.aliases).to eq(aliases) end end @@ -263,11 +270,11 @@ end end context 'update action' do subject { user.history_tracks.last.tracked_changes } before do - user.update_attributes(name: 'Aaron2', email: nil, country: '', city: nil, phone: '867-5309', aliases: ['', 'bill', 'james']) + user.update_attributes!(name: 'Aaron2', email: nil, country: '', city: nil, phone: '867-5309', aliases: ['', 'bill', 'james']) end it { is_expected.to be_a HashWithIndifferentAccess } it 'should track changed field' do expect(subject[:n]).to eq({ from: 'Aaron', to: 'Aaron2' }.with_indifferent_access) end @@ -309,11 +316,11 @@ end end context 'update action' do subject { user.history_tracks.last.tracked_edits } before do - user.update_attributes(name: 'Aaron2', email: nil, country: '', city: nil, phone: '867-5309', aliases: ['', 'bill', 'james']) + user.update_attributes!(name: 'Aaron2', email: nil, country: '', city: nil, phone: '867-5309', aliases: ['', 'bill', 'james']) end it { is_expected.to be_a HashWithIndifferentAccess } it 'should track changed field' do expect(subject[:modify]).to eq({ n: { from: 'Aaron', to: 'Aaron2' } }.with_indifferent_access) end @@ -351,138 +358,139 @@ end describe 'on update non-embedded twice' do it 'should assign version on post' do expect(post.version).to eq(1) - post.update_attributes(title: 'Test2') - post.update_attributes(title: 'Test3') + post.update_attributes!(title: 'Test2') + post.update_attributes!(title: 'Test3') expect(post.version).to eq(3) end it 'should create a history track if changed attributes match tracked attributes' do post # Created expect do - post.update_attributes(title: 'Test2') - post.update_attributes(title: 'Test3') + post.update_attributes!(title: 'Test2') + post.update_attributes!(title: 'Test3') end.to change(Tracker, :count).by(2) end it 'should create a history track of version 2' do - post.update_attributes(title: 'Test2') - post.update_attributes(title: 'Test3') + post.update_attributes!(title: 'Test2') + post.update_attributes!(title: 'Test3') expect(post.history_tracks.where(version: 2).first).not_to be_nil end it 'should assign modified fields' do - post.update_attributes(title: 'Test2') - post.update_attributes(title: 'Test3') + post.update_attributes!(title: 'Test2') + post.update_attributes!(title: 'Test3') expect(post.history_tracks.where(version: 3).first.modified).to eq( 'title' => 'Test3' ) end it 'should assign original fields' do - post.update_attributes(title: 'Test2') - post.update_attributes(title: 'Test3') + post.update_attributes!(title: 'Test2') + post.update_attributes!(title: 'Test3') expect(post.history_tracks.where(version: 3).first.original).to eq( 'title' => 'Test2' ) end it 'should assign modifier' do - post.update_attributes(title: 'Another Test', modifier: another_user) - expect(post.history_tracks.last.modifier).to eq(another_user) + post.update_attributes!(title: 'Another Test', modifier: another_user) + expect(post.history_tracks.last.modifier.id).to eq(another_user.id) end end describe 'on update embedded 1..N (embeds_many)' do it 'should assign version on comment' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') expect(comment.version).to eq(2) # first track generated on creation end it 'should create a history track of version 2' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') expect(comment.history_tracks.where(version: 2).first).not_to be_nil end it 'should assign modified fields' do - comment.update_attributes(t: 'Test2') + comment.update_attributes!(t: 'Test2') expect(comment.history_tracks.where(version: 2).first.modified).to eq( 't' => 'Test2' ) end it 'should assign original fields' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') expect(comment.history_tracks.where(version: 2).first.original).to eq( 't' => 'test' ) end it 'should be possible to undo from parent' do - comment.update_attributes(title: 'Test 2') + comment.update_attributes!(title: 'Test 2') user post.history_tracks.last.undo!(user) comment.reload expect(comment.title).to eq('test') end it 'should assign modifier' do - post.update_attributes(title: 'Another Test', modifier: another_user) - expect(post.history_tracks.last.modifier).to eq(another_user) + post.update_attributes!(title: 'Another Test', modifier: another_user) + expect(post.history_tracks.last.modifier.id).to eq(another_user.id) end end describe 'on update embedded 1..1 (embeds_one)' do - let(:section) { Section.new(title: 'Technology') } + let(:section) { Section.new(title: 'Technology', modifier: user) } before(:each) do post.section = section + post.modifier = user post.save! post.reload post.section end it 'should assign version on create section' do expect(section.version).to eq(1) end it 'should assign version on section' do - section.update_attributes(title: 'Technology 2') + section.update_attributes!(title: 'Technology 2') expect(section.version).to eq(2) # first track generated on creation end it 'should create a history track of version 2' do - section.update_attributes(title: 'Technology 2') + section.update_attributes!(title: 'Technology 2') expect(section.history_tracks.where(version: 2).first).not_to be_nil end it 'should assign modified fields' do - section.update_attributes(title: 'Technology 2') + section.update_attributes!(title: 'Technology 2') expect(section.history_tracks.where(version: 2).first.modified).to eq( 't' => 'Technology 2' ) end it 'should assign original fields' do - section.update_attributes(title: 'Technology 2') + section.update_attributes!(title: 'Technology 2') expect(section.history_tracks.where(version: 2).first.original).to eq( 't' => 'Technology' ) end it 'should be possible to undo from parent' do - section.update_attributes(title: 'Technology 2') + section.update_attributes!(title: 'Technology 2') post.history_tracks.last.undo!(user) section.reload expect(section.title).to eq('Technology') end it 'should assign modifier' do - section.update_attributes(title: 'Business', modifier: another_user) - expect(post.history_tracks.last.modifier).to eq(another_user) + section.update_attributes!(title: 'Business', modifier: another_user) + expect(post.history_tracks.last.modifier.id).to eq(another_user.id) end end describe 'on destroy embedded' do it 'should be possible to re-create destroyed embedded' do @@ -507,59 +515,54 @@ expect(post.comments.count).to eq(0) end it 'should be possible to create with redo after undo create embedded from parent' do comment # initialize - post.comments.create!(title: 'The second one') + post.comments.create!(title: 'The second one', modifier: user) track = post.history_tracks[2] + expect(post.reload.comments.count).to eq 2 track.undo!(user) + expect(post.reload.comments.count).to eq 1 track.redo!(user) - post.reload - expect(post.comments.count).to eq(2) + expect(post.reload.comments.count).to eq 2 end end describe 'embedded with cascading callbacks' do let(:tag_foo) { post.tags.create!(title: 'foo', updated_by: user) } - let(:tag_bar) { post.tags.create!(title: 'bar') } + let(:tag_bar) { post.tags.create!(title: 'bar', updated_by: user) } - # it "should have cascaded the creation callbacks and set timestamps" do - # tag_foo; tag_bar # initialize - # tag_foo.created_at.should_not be_nil - # tag_foo.updated_at.should_not be_nil - # end - it 'should allow an update through the parent model' do update_hash = { 'post' => { 'tags_attributes' => { '1234' => { 'id' => tag_bar.id, 'title' => 'baz' } } } } - post.update_attributes(update_hash['post']) + post.update_attributes!(update_hash['post']) expect(post.tags.last.title).to eq('baz') end it 'should be possible to destroy through parent model using canoncial _destroy macro' do tag_foo tag_bar # initialize expect(post.tags.count).to eq(2) update_hash = { 'post' => { 'tags_attributes' => { '1234' => { 'id' => tag_bar.id, 'title' => 'baz', '_destroy' => 'true' } } } } - post.update_attributes(update_hash['post']) + post.update_attributes!(update_hash['post']) expect(post.tags.count).to eq(1) expect(post.history_tracks.to_a.last.action).to eq('destroy') end it 'should write relationship name for association_chain hiearchy instead of class name when using _destroy macro' do update_hash = { 'tags_attributes' => { '1234' => { 'id' => tag_foo.id, '_destroy' => '1' } } } - post.update_attributes(update_hash) + post.update_attributes!(update_hash) # historically this would have evaluated to 'Tags' and an error would be thrown # on any call that walked up the association_chain, e.g. 'trackable' expect(tag_foo.history_tracks.last.association_chain.last['name']).to eq('tags') expect { tag_foo.history_tracks.last.trackable }.not_to raise_error end end describe 'non-embedded' do it 'should undo changes' do - post.update_attributes(title: 'Test2') + post.update_attributes!(title: 'Test2') post.history_tracks.where(version: 2).last.undo!(user) post.reload expect(post.title).to eq('Test') end @@ -569,25 +572,25 @@ expect(Post.find(post.id).title).to eq('Test') end it 'should create a new history track after undo' do comment # initialize - post.update_attributes(title: 'Test2') + post.update_attributes!(title: 'Test2') post.history_tracks.last.undo!(user) post.reload expect(post.history_tracks.count).to eq(4) end it 'should assign user as the modifier of the newly created history track' do - post.update_attributes(title: 'Test2') + post.update_attributes!(title: 'Test2') post.history_tracks.where(version: 2).last.undo!(user) post.reload - expect(post.history_tracks.where(version: 2).last.modifier).to eq(user) + expect(post.history_tracks.where(version: 2).last.modifier.id).to eq(user.id) end it 'should stay the same after undo and redo' do - post.update_attributes(title: 'Test2') + post.update_attributes!(title: 'Test2') track = post.history_tracks.last track.undo!(user) track.redo!(user) post2 = Post.where(_id: post.id).first @@ -603,32 +606,32 @@ end end describe 'embedded' do it 'should undo changes' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') comment.history_tracks.where(version: 2).first.undo!(user) comment.reload expect(comment.title).to eq('test') end it 'should create a new history track after undo' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') comment.history_tracks.where(version: 2).first.undo!(user) comment.reload expect(comment.history_tracks.count).to eq(3) end it 'should assign user as the modifier of the newly created history track' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') comment.history_tracks.where(version: 2).first.undo!(user) comment.reload - expect(comment.history_tracks.where(version: 3).first.modifier).to eq(user) + expect(comment.history_tracks.where(version: 3).first.modifier.id).to eq(user.id) end it 'should stay the same after undo and redo' do - comment.update_attributes(title: 'Test2') + comment.update_attributes!(title: 'Test2') track = comment.history_tracks.where(version: 2).first track.undo!(user) track.redo!(user) comment.reload expect(comment.title).to eq('Test2') @@ -700,11 +703,11 @@ describe 'redo' do [nil, :reload].each do |method| context (method || 'instance').to_s do before :each do - comment.update_attributes(title: 'Test5') + comment.update_attributes!(title: 'Test5') end it 'should recognize :from, :to options' do comment.redo! user, from: 2, to: 4 comment.send(method) if method @@ -755,43 +758,12 @@ end end end end - describe 'localized fields' do - before :each do - class Sausage - include Mongoid::Document - include Mongoid::History::Trackable - - field :flavour, localize: true - track_history on: [:flavour], track_destroy: true - end - end - it 'should correctly undo and redo' do - if Sausage.respond_to?(:localized_fields) - sausage = Sausage.create!(flavour_translations: { 'en' => 'Apple', 'nl' => 'Appel' }) - sausage.update_attributes(flavour: 'Guinness') - - track = sausage.history_tracks.last - - track.undo! user - expect(sausage.reload.flavour).to eq('Apple') - - track.redo! user - expect(sausage.reload.flavour).to eq('Guinness') - - sausage.destroy - expect(sausage.history_tracks.last.action).to eq('destroy') - sausage.history_tracks.last.undo! user - expect(sausage.reload.flavour).to eq('Guinness') - end - end - end - describe 'embedded with a polymorphic trackable' do - let(:foo) { Foo.new(title: 'a title', body: 'a body') } + let(:foo) { Foo.new(title: 'a title', body: 'a body', modifier: user) } before :each do post.comments << foo post.save! end it 'should assign interface name in association chain' do @@ -813,11 +785,11 @@ expect(tracker.trackable_parent_class).to eq(Tag) end end context 'an embedded model' do it 'should return the trackable parent class' do - comment.update_attributes(title: 'Foo') + comment.update_attributes!(title: 'Foo') expect(comment.history_tracks.first.trackable_parent_class).to eq(Post) end it 'should return the parent class even if the trackable is deleted' do tracker = comment.history_tracks.first comment.destroy @@ -825,11 +797,11 @@ end end end describe 'when default scope is present' do - before do + before :each do class Post default_scope -> { where(title: nil) } end class Comment default_scope -> { where(title: nil) } @@ -842,74 +814,74 @@ end end describe 'post' do it 'should correctly undo and redo' do - post.update_attributes(title: 'a new title') + post.update_attributes!(title: 'a new title') track = post.history_tracks.last track.undo! user expect(post.reload.title).to eq('Test') track.redo! user expect(post.reload.title).to eq('a new title') end it 'should stay the same after undo and redo' do - post.update_attributes(title: 'testing') + post.update_attributes!(title: 'testing') track = post.history_tracks.last track.undo! user track.redo! user expect(post.reload.title).to eq('testing') end end describe 'comment' do it 'should correctly undo and redo' do - comment.update_attributes(title: 'a new title') + comment.update_attributes!(title: 'a new title') track = comment.history_tracks.last track.undo! user expect(comment.reload.title).to eq('test') track.redo! user expect(comment.reload.title).to eq('a new title') end it 'should stay the same after undo and redo' do - comment.update_attributes(title: 'testing') + comment.update_attributes!(title: 'testing') track = comment.history_tracks.last track.undo! user track.redo! user expect(comment.reload.title).to eq('testing') end end describe 'user' do it 'should correctly undo and redo' do - user.update_attributes(name: 'a new name') + user.update_attributes!(name: 'a new name') track = user.history_tracks.last track.undo! user expect(user.reload.name).to eq('Aaron') track.redo! user expect(user.reload.name).to eq('a new name') end it 'should stay the same after undo and redo' do - user.update_attributes(name: 'testing') + user.update_attributes!(name: 'testing') track = user.history_tracks.last track.undo! user track.redo! user expect(user.reload.name).to eq('testing') end end describe 'tag' do it 'should correctly undo and redo' do - tag.update_attributes(title: 'a new title') + tag.update_attributes!(title: 'a new title') track = tag.history_tracks.last track.undo! user expect(tag.reload.title).to eq('test') track.redo! user expect(tag.reload.title).to eq('a new title') end it 'should stay the same after undo and redo' do - tag.update_attributes(title: 'testing') + tag.update_attributes!(title: 'testing') track = tag.history_tracks.last track.undo! user track.redo! user expect(tag.reload.title).to eq('testing') end @@ -928,15 +900,55 @@ { foo: %w[bar baz] } end end end + after :each do + Object.send(:remove_const, :OverriddenChangesMethod) + end + it 'should add foo to the changes history' do - o = OverriddenChangesMethod.create + o = OverriddenChangesMethod.create(modifier: user) o.save! track = o.history_tracks.last expect(track.modified).to eq('foo' => 'baz') expect(track.original).to eq('foo' => 'bar') + end + end + + describe 'localized fields' do + before :each do + class Sausage + include Mongoid::Document + include Mongoid::History::Trackable + + field :flavour, localize: true + track_history on: [:flavour], track_destroy: true, modifier_field_optional: true + end + end + + after :each do + Object.send(:remove_const, :Sausage) + end + + it 'should correctly undo and redo' do + pending unless Sausage.respond_to?(:localized_fields) + + sausage = Sausage.create!(flavour_translations: { 'en' => 'Apple', 'nl' => 'Appel' }, modifier: user) + sausage.update_attributes!(flavour: 'Guinness') + + track = sausage.history_tracks.last + + track.undo! user + expect(sausage.reload.flavour).to eq('Apple') + + track.redo! user + expect(sausage.reload.flavour).to eq('Guinness') + + sausage.destroy + expect(sausage.history_tracks.last.action).to eq('destroy') + sausage.history_tracks.last.undo! user + expect(sausage.reload.flavour).to eq('Guinness') end end end end