diff --git a/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb b/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb index f6985024..85c45365 100644 --- a/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb +++ b/spec/dummy_app/db/migrate/20110208155312_set_up_test_tables.rb @@ -124,7 +124,7 @@ class SetUpTestTables < ( end add_index :post_versions, %i[item_type item_id] - if ENV["DB"] == "postgres" && ::ActiveRecord::VERSION::MAJOR >= 4 + if ENV["DB"] == "postgres" create_table :json_versions, force: true do |t| t.string :item_type, null: false t.integer :item_id, null: false diff --git a/spec/models/json_version_spec.rb b/spec/models/json_version_spec.rb index d202f3fe..42453dde 100644 --- a/spec/models/json_version_spec.rb +++ b/spec/models/json_version_spec.rb @@ -3,99 +3,120 @@ require "spec_helper" # The `json_versions` table tests postgres' `json` data type. So, that -# table is only created when testing against postgres and ActiveRecord >= 4. +# table is only created when testing against postgres. if JsonVersion.table_exists? RSpec.describe JsonVersion, type: :model do it "includes the VersionConcern module" do expect(described_class).to include(PaperTrail::VersionConcern) end - describe "Methods" do - describe "Class" do - describe "#where_object" do - it { expect(described_class).to respond_to(:where_object) } + describe "#where_object" do + it { expect(described_class).to respond_to(:where_object) } - it "escapes values" do - f = Fruit.create(name: "Bobby") - expect( - f. - versions. - where_object(name: "Robert'; DROP TABLE Students;--"). - count - ).to eq(0) - end + it "escapes values" do + f = Fruit.create(name: "Bobby") + expect( + f. + versions. + where_object(name: "Robert'; DROP TABLE Students;--"). + count + ).to eq(0) + end - context "invalid arguments" do - it "raises an error" do - expect { described_class.where_object(:foo) }.to raise_error(ArgumentError) - expect { described_class.where_object([]) }.to raise_error(ArgumentError) - end - end + context "invalid arguments" do + it "raises an error" do + expect { described_class.where_object(:foo) }.to raise_error(ArgumentError) + expect { described_class.where_object([]) }.to raise_error(ArgumentError) + end + end - context "valid arguments", versioning: true do - let(:fruit_names) { %w[apple orange lemon banana lime coconut strawberry blueberry] } - let(:fruit) { Fruit.new } - let(:name) { "pomegranate" } - let(:color) { FFaker::Color.name } + context "valid arguments", versioning: true do + it "locates versions according to their `object` contents" do + fruit = Fruit.create!(name: "apple") + expect(fruit.versions.length).to eq(1) + fruit.update_attributes!(name: "banana", color: "aqua") + expect(fruit.versions.length).to eq(2) + fruit.update_attributes!(name: "coconut", color: "black") + expect(fruit.versions.length).to eq(3) + where_apple = described_class.where_object(name: "apple") + expect(where_apple.to_sql).to eq( + <<-SQL.squish + SELECT "json_versions".* + FROM "json_versions" + WHERE (object->>'name' = 'apple') + SQL + ) + expect(where_apple).to eq([fruit.versions[1]]) + expect( + described_class.where_object(color: "aqua") + ).to eq([fruit.versions[2]]) + end + end + end - before do - fruit.update_attributes!(name: name) - fruit.update_attributes!(name: fruit_names.sample, color: color) - fruit.update_attributes!(name: fruit_names.sample, color: FFaker::Color.name) - end + describe "#where_object_changes" do + it "escapes values" do + f = Fruit.create(name: "Bobby") + expect( + f. + versions. + where_object_changes(name: "Robert'; DROP TABLE Students;--"). + count + ).to eq(0) + end - it "locates versions according to their `object` contents" do - expect(described_class.where_object(name: name)).to eq([fruit.versions[1]]) - expect(described_class.where_object(color: color)).to eq([fruit.versions[2]]) - end - end + context "invalid arguments" do + it "raises an error" do + expect { described_class.where_object_changes(:foo) }.to raise_error(ArgumentError) + expect { described_class.where_object_changes([]) }.to raise_error(ArgumentError) + end + end + + context "valid arguments", versioning: true do + it "finds versions according to their `object_changes` contents" do + fruit = Fruit.create!(name: "apple") + fruit.update_attributes!(name: "banana", color: "red") + fruit.update_attributes!(name: "coconut", color: "green") + where_apple = fruit.versions.where_object_changes(name: "apple") + expect(where_apple.to_sql.squish).to eq( + <<-SQL.squish + SELECT "json_versions".* + FROM "json_versions" + WHERE "json_versions"."item_id" = #{fruit.id} + AND "json_versions"."item_type" = 'Fruit' + AND + (((object_changes->>'name' ILIKE '["apple",%') + OR (object_changes->>'name' ILIKE '[%,"apple"]%'))) + ORDER BY "json_versions"."created_at" ASC, + "json_versions"."id" ASC + SQL + ) + expect(where_apple).to match_array(fruit.versions[0..1]) + expect( + fruit.versions.where_object_changes(color: "red") + ).to match_array(fruit.versions[1..2]) end - describe "#where_object_changes" do - it { expect(described_class).to respond_to(:where_object_changes) } - - it "escapes values" do - f = Fruit.create(name: "Bobby") - expect( - f. - versions. - where_object_changes(name: "Robert'; DROP TABLE Students;--"). - count - ).to eq(0) - end - - context "invalid arguments" do - it "raises an error" do - expect { described_class.where_object_changes(:foo) }.to raise_error(ArgumentError) - expect { described_class.where_object_changes([]) }.to raise_error(ArgumentError) - end - end - - context "valid arguments", versioning: true do - let(:color) { %w[red green] } - let(:fruit) { Fruit.create!(name: name[0]) } - let(:name) { %w[banana kiwi mango] } - - before do - fruit.update_attributes!(name: name[1], color: color[0]) - fruit.update_attributes!(name: name[2], color: color[1]) - end - - it "finds versions according to their `object_changes` contents" do - expect( - fruit.versions.where_object_changes(name: name[0]) - ).to match_array(fruit.versions[0..1]) - expect( - fruit.versions.where_object_changes(color: color[0]) - ).to match_array(fruit.versions[1..2]) - end - - it "finds versions with multiple attributes changed" do - expect( - fruit.versions.where_object_changes(color: color[0], name: name[0]) - ).to match_array([fruit.versions[1]]) - end - end + it "finds versions with multiple attributes changed" do + fruit = Fruit.create!(name: "apple") + fruit.update_attributes!(name: "banana", color: "red") + fruit.update_attributes!(name: "coconut", color: "green") + where_red_apple = fruit.versions.where_object_changes(color: "red", name: "apple") + expect(where_red_apple.to_sql.squish).to eq( + <<-SQL.squish + SELECT "json_versions".* + FROM "json_versions" + WHERE "json_versions"."item_id" = #{fruit.id} + AND "json_versions"."item_type" = 'Fruit' + AND (((object_changes->>'color' ILIKE '["red",%') + OR (object_changes->>'color' ILIKE '[%,"red"]%')) + and ((object_changes->>'name' ILIKE '["apple",%') + OR (object_changes->>'name' ILIKE '[%,"apple"]%'))) + ORDER BY "json_versions"."created_at" ASC, + "json_versions"."id" ASC + SQL + ) + expect(where_red_apple).to match_array([fruit.versions[1]]) end end end