require 'rails_helper' describe PaperTrail::Version, :type => :model do it "should include the `VersionConcern` module to get base functionality" do expect(PaperTrail::Version).to include(PaperTrail::VersionConcern) end describe "Attributes" do it { is_expected.to have_db_column(:item_type).of_type(:string) } it { is_expected.to have_db_column(:item_id).of_type(:integer) } it { is_expected.to have_db_column(:event).of_type(:string) } it { is_expected.to have_db_column(:whodunnit).of_type(:string) } it { is_expected.to have_db_column(:object).of_type(:text) } it { is_expected.to have_db_column(:created_at).of_type(:datetime) } describe "object_changes column", :versioning => true do let(:widget) { Widget.create!(:name => 'Dashboard') } let(:value) { widget.versions.last.object_changes } context "serializer is YAML" do specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML } it "should store out as a plain hash" do expect(value =~ /HashWithIndifferentAccess/).to be_nil end end context "serializer is JSON" do before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON } it "should store out as a plain hash" do expect(value =~ /HashWithIndifferentAccess/).to be_nil end after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML } end end end describe "Indexes" do it { is_expected.to have_db_index([:item_type, :item_id]) } end describe "Methods" do describe "Instance" do subject { PaperTrail::Version.new(attributes) rescue PaperTrail::Version.new } describe '#paper_trail_originator' do it { is_expected.to respond_to(:paper_trail_originator) } context "No previous versions" do specify { expect(subject.previous).to be_nil } it "should return nil" do expect(subject.paper_trail_originator).to be_nil end end context "Has previous version", :versioning => true do let(:name) { FFaker::Name.name } let(:widget) { Widget.create!(:name => FFaker::Name.name) } before do widget.versions.first.update_attributes!(:whodunnit => name) widget.update_attributes!(:name => FFaker::Name.first_name) end subject { widget.versions.last } specify { expect(subject.previous).to be_instance_of(PaperTrail::Version) } it "should return nil" do expect(subject.paper_trail_originator).to eq(name) end end end describe "#originator" do it { is_expected.to respond_to(:originator) } it 'should set the invoke `paper_trail_originator`' do allow(ActiveSupport::Deprecation).to receive(:warn) is_expected.to receive(:paper_trail_originator) subject.originator end it 'should display a deprecation warning' do expect(ActiveSupport::Deprecation).to receive(:warn). with(/Use paper_trail_originator instead of originator/) subject.originator end end describe '#terminator' do it { is_expected.to respond_to(:terminator) } let(:attributes) { {:whodunnit => FFaker::Name.first_name} } it "is an alias for the `whodunnit` attribute" do expect(subject.terminator).to eq(attributes[:whodunnit]) end end describe '#version_author' do it { is_expected.to respond_to(:version_author) } it "should be an alias for the `terminator` method" do expect(subject.method(:version_author)).to eq(subject.method(:terminator)) end end end describe "Class" do column_overrides = [false] if ENV['DB'] == 'postgres' && ::ActiveRecord::VERSION::MAJOR >= 4 column_overrides << 'json' # 'jsonb' column types are only supported for ActiveRecord 4.2+ column_overrides << 'jsonb' if ::ActiveRecord::VERSION::STRING >= '4.2' end column_overrides.shuffle.each do |override| context "with a #{override || 'text'} column" do before do if override ActiveRecord::Base.connection.execute("SAVEPOINT pgtest;") %w[object object_changes].each do |column| ActiveRecord::Base.connection.execute("ALTER TABLE versions DROP COLUMN #{column};") ActiveRecord::Base.connection.execute("ALTER TABLE versions ADD COLUMN #{column} #{override};") end PaperTrail::Version.reset_column_information end end after do if override ActiveRecord::Base.connection.execute("ROLLBACK TO SAVEPOINT pgtest;") PaperTrail::Version.reset_column_information end end describe '#where_object' do it { expect(PaperTrail::Version).to respond_to(:where_object) } context "invalid arguments" do it "should raise an error" do expect { PaperTrail::Version.where_object(:foo) }.to raise_error(ArgumentError) expect { PaperTrail::Version.where_object([]) }.to raise_error(ArgumentError) end end context "valid arguments", :versioning => true do let(:widget) { Widget.new } let(:name) { FFaker::Name.first_name } let(:int) { rand(10) + 1 } before do widget.update_attributes!(:name => name, :an_integer => int) widget.update_attributes!(:name => 'foobar', :an_integer => 100) widget.update_attributes!(:name => FFaker::Name.last_name, :an_integer => 15) end context "`serializer == YAML`" do specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML } it "should be able to locate versions according to their `object` contents" do expect(PaperTrail::Version.where_object(:name => name)).to eq([widget.versions[1]]) expect(PaperTrail::Version.where_object(:an_integer => 100)).to eq([widget.versions[2]]) end end context "`serializer == JSON`" do before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON } specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON } it "should be able to locate versions according to their `object` contents" do expect(PaperTrail::Version.where_object(:name => name)).to eq([widget.versions[1]]) expect(PaperTrail::Version.where_object(:an_integer => 100)).to eq([widget.versions[2]]) end after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML } end end end describe '#where_object_changes' do it { expect(PaperTrail::Version).to respond_to(:where_object_changes) } context "invalid arguments" do it "should raise an error" do expect { PaperTrail::Version.where_object_changes(:foo) }.to raise_error(ArgumentError) expect { PaperTrail::Version.where_object_changes([]) }.to raise_error(ArgumentError) end end context "valid arguments", :versioning => true do let(:widget) { Widget.new } let(:name) { FFaker::Name.first_name } let(:int) { rand(5) + 2 } before do widget.update_attributes!(:name => name, :an_integer => 0) widget.update_attributes!(:name => 'foobar', :an_integer => 77) widget.update_attributes!(:name => FFaker::Name.last_name, :an_integer => int) end context "`serializer == YAML`" do specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::YAML } it "should be able to locate versions according to their `object_changes` contents" do expect(widget.versions.where_object_changes(:name => name)).to eq(widget.versions[0..1]) expect(widget.versions.where_object_changes(:an_integer => 77)).to eq(widget.versions[1..2]) expect(widget.versions.where_object_changes(:an_integer => int)).to eq([widget.versions.last]) end it "should be able to handle queries for multiple attributes" do expect(widget.versions.where_object_changes(:an_integer => 77, :name => 'foobar')).to eq(widget.versions[1..2]) end end context "`serializer == JSON`" do before(:all) { PaperTrail.serializer = PaperTrail::Serializers::JSON } specify { expect(PaperTrail.serializer).to be PaperTrail::Serializers::JSON } it "should be able to locate versions according to their `object_changes` contents" do expect(widget.versions.where_object_changes(:name => name)).to eq(widget.versions[0..1]) expect(widget.versions.where_object_changes(:an_integer => 77)).to eq(widget.versions[1..2]) expect(widget.versions.where_object_changes(:an_integer => int)).to eq([widget.versions.last]) end it "should be able to handle queries for multiple attributes" do expect(widget.versions.where_object_changes(:an_integer => 77, :name => 'foobar')).to eq(widget.versions[1..2]) end after(:all) { PaperTrail.serializer = PaperTrail::Serializers::YAML } end end end end end end end end