Skip to content

Commit

Permalink
Merge pull request #32 from stevehodges/unscoped_queries
Browse files Browse the repository at this point in the history
Allow SELECT queries without a WHERE or LIMIT to be tested with the unscoped option
  • Loading branch information
sds authored Sep 25, 2019
2 parents 3cb1bf5 + 84bf222 commit d46fa8a
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 3 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ describe 'MyCode' do
end
end

context 'when we only care about unscoped queries (SELECT without a WHERE or LIMIT clause))' do
it 'makes an unscoped database query' do
expect { subject.make_one_query }.to make_database_queries(unscoped: true)
end
end

context 'when we only care about queries matching a certain pattern' do
it 'makes a destructive database query' do
expect { subject.make_special_queries }.to make_database_queries(matching: 'DELETE * FROM')
Expand Down
15 changes: 15 additions & 0 deletions lib/db_query_matchers/make_database_queries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
# @example
# expect { subject }.to make_database_queries(manipulative: true)
#
# @example
# expect { subject }.to make_database_queries(unscoped: true)
#
# @see DBQueryMatchers::QueryCounter
RSpec::Matchers.define :make_database_queries do |options = {}|
if RSpec::Core::Version::STRING =~ /^2/
Expand Down Expand Up @@ -46,6 +49,18 @@ def pluralize(count, singular, plural = nil)
if options[:manipulative]
counter_options[:matches] = [/^\ *(INSERT|UPDATE|DELETE\ FROM)/]
end
if options[:unscoped]
counter_options[:matches] = [
%r{
(?: # Any of these appear
SELECT(?!\sCOUNT).*FROM| # SELECT ... FROM (not SELECT ... COUNT)
DELETE\sFROM| # DELETE ... FROM
UPDATE.*SET # UPDATE ... SET
)
(?!.*(WHERE|LIMIT)) # Followed by WHERE and/or LIMIT
}mx # Ignore whitespace and newlines
]
end
if options[:matching]
counter_options[:matches] ||= []
case options[:matching]
Expand Down
172 changes: 172 additions & 0 deletions spec/db_query_matchers/make_database_queries_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,178 @@
end
end

context 'when a `unscoped` option is true' do
shared_examples 'it raises an error' do
it 'raises an error' do
expect do
expect { subject }.to make_database_queries(unscoped: true)
end.to raise_error(RSpec::Expectations::ExpectationNotMetError,
/expected queries, but none were made/)
end

it 'does not raise with `to_not`' do
expect { subject }.to_not make_database_queries(unscoped: true)
end
end

before do
Cat.create if Cat.count == 0
end

context 'and there is a query without a WHERE or LIMIT clause' do
context 'SELECT' do
subject { Cat.all.to_a }

it 'matches true' do
expect { subject }.to make_database_queries(unscoped: true)
end

it 'raises an error with `to_not`' do
expect do
expect { subject }.to_not make_database_queries(unscoped: true)
end.to raise_error(RSpec::Expectations::ExpectationNotMetError,
/expected no queries, but 1 were made/)
end
end

context 'DELETE' do
subject { Cat.delete_all }

it 'matches true' do
expect { subject }.to make_database_queries(unscoped: true)
end

it 'raises an error with `to_not`' do
expect do
expect { subject }.to_not make_database_queries(unscoped: true)
end.to raise_error(RSpec::Expectations::ExpectationNotMetError,
/expected no queries, but 1 were made/)
end
end

context 'UPDATE' do
subject { Cat.update_all(name: 'Nombre') }

it 'matches true' do
expect { subject }.to make_database_queries(unscoped: true)
end

it 'raises an error with `to_not`' do
expect do
expect { subject }.to_not make_database_queries(unscoped: true)
end.to raise_error(RSpec::Expectations::ExpectationNotMetError,
/expected no queries, but 1 were made/)
end
end

context 'INSERT' do
context 'without INTO SELECT' do
subject { Cat.create name: 'Joe' }

it 'matches false' do
expect { subject }.to_not make_database_queries(unscoped: true)
end
end

context 'with INTO SELECT' do
subject do
Cat.connection.execute <<-SQL
INSERT INTO "cats" SELECT * FROM "dogs";
SQL
end
it 'matches true' do
expect { subject }.to make_database_queries(unscoped: true)
end

it 'raises an error with `to`' do
expect do
expect { subject }.to_not make_database_queries(unscoped: true)
end.to raise_error(RSpec::Expectations::ExpectationNotMetError,
/expected no queries, but 1 were made/)
end
end
end
end

context 'there is a limit clause' do
context 'SELECT' do
subject { Cat.all.limit(100).to_a }
include_examples 'it raises an error'
end

context 'UPDATE' do
subject { Cat.limit(10).update_all(name: 'Nombre') }
include_examples 'it raises an error'
end

context 'DELETE' do
subject { Cat.limit(100).delete_all }
include_examples 'it raises an error'
end

context 'INTO SELECT' do
subject do
Cat.connection.execute <<-SQL
INSERT INTO "cats" SELECT * FROM "dogs" LIMIT 100;
SQL
end
include_examples 'it raises an error'
end
end

context 'there is a where clause' do
context 'SELECT' do
subject { Cat.where(name: 'Bob').to_a }
include_examples 'it raises an error'
end

context 'UPDATE' do
subject { Cat.where(name: 'Bob').update_all(name: 'Nombre') }
include_examples 'it raises an error'
end

context 'DELETE' do
subject { Cat.where(name: 'Bob').delete_all }
include_examples 'it raises an error'
end

context 'INTO SELECT' do
subject do
Cat.connection.execute <<-SQL
INSERT INTO "cats" SELECT * FROM "dogs" WHERE "dogs"."name" = 'Fido';
SQL
end
include_examples 'it raises an error'
end
end

context 'there is a where and limit clause' do
context 'SELECT' do
subject { Cat.where(name: 'Bob').limit(10).to_a }
include_examples 'it raises an error'
end

context 'UPDATE' do
subject { Cat.where(name: 'Bob').limit(10).update_all(name: 'Nombre') }
include_examples 'it raises an error'
end

context 'DELETE' do
subject { Cat.where(name: 'Bob').limit(10).delete_all }
include_examples 'it raises an error'
end

context 'INTO SELECT' do
subject do
Cat.connection.execute <<-SQL
INSERT INTO "cats" SELECT * FROM "dogs" WHERE "dogs"."name" = 'Fido' LIMIT 10;
SQL
end
include_examples 'it raises an error'
end
end
end

context 'when a `matching` option is specified' do
context 'with a string matcher' do
context 'and there is a query matching the matcher specified' do
Expand Down
4 changes: 4 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
create_table :cats, :force => true do |t|
t.column :name, :string
end

create_table :dogs, :force => true do |t|
t.column :name, :string
end
end
end
6 changes: 3 additions & 3 deletions spec/support/models/cat.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Test model
class Cat < ActiveRecord::Base
# Test models
class Cat < ActiveRecord::Base; end
class Dog < ActiveRecord::Base; end

end

0 comments on commit d46fa8a

Please sign in to comment.