rspec-activerecord-expectations 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6e24b218adcab0f1e6fb117d66463a2cdf60996fe5c16f60d48f8efd37c3259
4
- data.tar.gz: 4f0acb5f4c489f4b2ded11b7d27a1bec613d231b04d836f651a26daf1bdb5bb1
3
+ metadata.gz: 3205b050d5f2164b5bc72de9fe29a0f6d988cd869ecd6703a09b83b2550bb02e
4
+ data.tar.gz: d43d363888f5c8f85dc551fae7d1381a945ac304c597192544ffd1506d1f7495
5
5
  SHA512:
6
- metadata.gz: d077a6aa43e9f243a4be0d5b4db338a2aa3272e49974b8cc9b987499571de846e6f5cbc4c9d1f4d3d58d50b1d91ce3d361f2719ee3073ae8eeeee589a62d3e8c
7
- data.tar.gz: 4473b4f016d10ca617e4ef5b3b1550c44903a170e9fde1291a88097a264028a0e21383fa37324f063c9529412c52e70e6fb24fab9e7a6df2382568f6a763fbc5
6
+ metadata.gz: a98cfac5ba127c3e5f7bd2b42ef48fa56e6750e75d0ab841988b076a2a39c0ca13e5d67e534020fd81af3b7885cba3d192c23a2fbb0b4ccd0d8e725e3cafb147
7
+ data.tar.gz: 65347df27d406531b1ea594bfe9a3d9b6405c75ad5f8be5c3f52f668e3d976e0e73c5e1560caeb308cb203b73547ab18c0ffd758aff0e913a9f010665a0a6595
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.0] - 2022-01-14
4
+ - Adds transaction matcher to verify that code was executed within a
5
+ transaction at minimum
6
+ - Transaction matcher also allows for commits and rollbacks
7
+ - Update README for accompanying functionality
8
+
3
9
  ## [2.1.1] - 2022-01-12
4
10
  - Gemspec is really generated using the version of ruby that's locally in use.
5
11
  Update that build artifact to use non-java dependencies.
data/README.md CHANGED
@@ -181,7 +181,57 @@ expect {}.to execute.exactly(20).transaction_queries
181
181
  categories, nor are queries that load the DB schema.
182
182
 
183
183
  **Note:** Destroy and delete queries are both condensed into the matcher for
184
- `destroy_queries`.
184
+ the errorthe error `destroy_queries`.
185
+
186
+ ## Transaction Management
187
+
188
+ Sometimes, it makes sense to monitor whether database transactions were
189
+ successful or not. This is very similar to using `expect{}.to change(SomeModel,
190
+ :count)` in a spec, but nonetheless it can be useful to assert transactions
191
+ themselves. Some assertions are available for this purpose.
192
+
193
+ ```ruby
194
+ expect {}.to execute_a_transaction
195
+ expect {}.to rollback_a_transaction
196
+ expect {}.to roll_back_a_transaction
197
+ expect {}.to commit_a_transaction
198
+ ```
199
+
200
+ A complication to this scheme is that Rails tries not to make unnecessary database
201
+ calls, which means that attempting to save a model that has failing validations
202
+ won't actually attempt to save to the database.
203
+
204
+ ```ruby
205
+ expect {
206
+ MyClass.create!(required_field: nil)
207
+ }.to rollback_a_transaction
208
+ ```
209
+
210
+ This assertion will fail, as `create!` will never make it as far as the
211
+ database. That said, if you manually create a transaction, _and you select
212
+ data within that transaction_, you may assert a rollback.
213
+
214
+ ```ruby
215
+ expect {
216
+ MyClass.first # triggers the transaction
217
+ MyClass.create!(required_field: nil)
218
+ }.to rollback_a_transaction
219
+ ```
220
+
221
+ It you need to make transaction-related assertions of this sort, your best bet
222
+ may be to assert that a commit statement was _not_ issued.
223
+
224
+ ```ruby
225
+ expect do
226
+ MyClass.create!(required_field: nil)
227
+ rescue
228
+ # NOOP
229
+ end.not_to rollback_a_transaction
230
+ ```
231
+
232
+ Note that ActiveRecord will not only roll back the transaction, but also
233
+ re-raise errors. As such, it's necessary in this example to rescue that
234
+ error in order for the test to fail.
185
235
 
186
236
  ## Future Planned Functionality
187
237
 
@@ -194,14 +244,13 @@ expect {}.to execute.at_least(2).load_queries("Audited::Audit")
194
244
  expect {}.to execute.at_least(2).activerecord_queries
195
245
  expect {}.to execute.at_least(2).hand_rolled_queries
196
246
 
197
- expect {}.not_to rollback_transaction.exactly(5).times
198
- expect {}.not_to commit_transaction.once
199
- expect {}.to run_a_transaction
200
-
201
247
  expect {}.to create.exactly(5).of_type(User)
202
248
  expect {}.to insert.exactly(5).subscription_changes
203
249
  expect {}.to update.exactly(2).of_any_type
204
250
  expect {}.to delete.exactly(2).of_any_type
251
+
252
+ expect {}.to commit_a_transaction.once
253
+ expect {}.to rollback_a_transaction.exactly(5).times
205
254
  ```
206
255
 
207
256
  - warn if we smite any built in methods (or methods from other libs)
@@ -0,0 +1,70 @@
1
+ module RSpec::ActiveRecord::Expectations
2
+ module Matchers
3
+ class TransactionMatcher
4
+ def initialize(transaction_type)
5
+ @collector = Collector.new
6
+ @transaction = transaction_type
7
+ end
8
+
9
+ def supports_block_expectations?
10
+ true
11
+ end
12
+
13
+ def matches?(block)
14
+ block.call
15
+
16
+ @count = @collector.queries_of_type(@transaction)
17
+ @count > 0
18
+ end
19
+
20
+ def failure_message
21
+ type_msg = case @transaction
22
+ when :transaction_queries
23
+ "execute a transaction"
24
+ when :rollback_queries
25
+ "roll back a transaction"
26
+ when :commit_queries
27
+ "commit a transaction"
28
+ end
29
+
30
+ "expected block to #{type_msg}, but it didn't do so"
31
+ end
32
+
33
+ def failure_message_when_negated
34
+ if @count == 1
35
+ negated_message_singular
36
+ else
37
+ negated_message_plural
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def negated_message_singular
44
+ pre_msg, post_msg = case @transaction
45
+ when :transaction_queries
46
+ ["execute a transaction", "executed one"]
47
+ when :rollback_queries
48
+ ["roll back a transaction", "rolled one back"]
49
+ when :commit_queries
50
+ ["commit a transaction", "committed one"]
51
+ end
52
+
53
+ "expected block not to #{pre_msg}, but it #{post_msg}"
54
+ end
55
+
56
+ def negated_message_plural
57
+ pre_msg, post_msg = case @transaction
58
+ when :transaction_queries
59
+ ["execute a transaction", "executed #{@count} transactions"]
60
+ when :rollback_queries
61
+ ["roll back a transaction", "rolled back #{@count} transactions"]
62
+ when :commit_queries
63
+ ["commit a transaction", "committed #{@count} transactions"]
64
+ end
65
+
66
+ "expected block not to #{pre_msg}, but it #{post_msg}"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -1,14 +1,19 @@
1
1
  module RSpec::ActiveRecord::Expectations
2
2
  class QueryInspector
3
3
  def self.valid_query_types
4
- [:queries, :schema_queries, :transaction_queries, :insert_queries,
5
- :load_queries, :destroy_queries, :exists_queries]
4
+ [:queries, :schema_queries, :insert_queries, :load_queries,
5
+ :destroy_queries, :exists_queries,
6
+ :transaction_queries, :commit_queries, :rollback_queries]
6
7
  end
7
8
 
8
9
  def categorize(query)
9
10
  if query[:name] == "SCHEMA"
10
11
  [:schema_queries]
11
- elsif query[:name] == "TRANSACTION" || query[:sql] =~ /^begin/i || query[:sql] =~ /^commit/i
12
+ elsif query[:sql] =~ /^commit/i
13
+ [:commit_queries]
14
+ elsif query[:sql] =~ /^rollback/i
15
+ [:rollback_queries]
16
+ elsif query[:name] == "TRANSACTION"
12
17
  [:transaction_queries]
13
18
  elsif query[:name] =~ /Create$/
14
19
  [:queries, :insert_queries]
@@ -8,6 +8,22 @@ module RSpec
8
8
  def repeatedly_load(klass)
9
9
  Matchers::LoadMatcher.new(klass)
10
10
  end
11
+
12
+ def execute_a_transaction
13
+ Matchers::TransactionMatcher.new(:transaction_queries)
14
+ end
15
+
16
+ def rollback_a_transaction
17
+ Matchers::TransactionMatcher.new(:rollback_queries)
18
+ end
19
+
20
+ def roll_back_a_transaction
21
+ Matchers::TransactionMatcher.new(:rollback_queries)
22
+ end
23
+
24
+ def commit_a_transaction
25
+ Matchers::TransactionMatcher.new(:commit_queries)
26
+ end
11
27
  end
12
28
  end
13
29
  end
@@ -17,3 +33,4 @@ require_relative 'expectations/query_inspector'
17
33
  require_relative 'expectations/collector'
18
34
  require_relative 'expectations/matchers/query_count_matcher'
19
35
  require_relative 'expectations/matchers/load_matcher'
36
+ require_relative 'expectations/matchers/transaction_matcher'
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "rspec-activerecord-expectations"
3
- spec.version = '2.1.1'
3
+ spec.version = '2.2.0'
4
4
  spec.authors = ["Joseph Mastey"]
5
5
  spec.email = ["hello@joemastey.com"]
6
6
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-activerecord-expectations
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joseph Mastey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-13 00:00:00.000000000 Z
11
+ date: 2022-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -104,6 +104,7 @@ files:
104
104
  - lib/rspec/activerecord/expectations/errors.rb
105
105
  - lib/rspec/activerecord/expectations/matchers/load_matcher.rb
106
106
  - lib/rspec/activerecord/expectations/matchers/query_count_matcher.rb
107
+ - lib/rspec/activerecord/expectations/matchers/transaction_matcher.rb
107
108
  - lib/rspec/activerecord/expectations/query_inspector.rb
108
109
  - rspec-activerecord-expectations.gemspec
109
110
  homepage: https://github.com/jmmastey/rspec-activerecord-expectations