rspec-goodies 0.0.1
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 +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +218 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/Rakefile +4 -0
- data/lib/rspec/goodies/helpers/sidekiq.rb +156 -0
- data/lib/rspec/goodies/helpers/stubs.rb +76 -0
- data/lib/rspec/goodies/matchers/collection.rb +50 -0
- data/lib/rspec/goodies/matchers/date_time.rb +24 -0
- data/lib/rspec/goodies/matchers/hash.rb +8 -0
- data/lib/rspec/goodies/matchers/sidekiq.rb +214 -0
- data/lib/rspec/goodies/matchers/string.rb +16 -0
- data/lib/rspec-goodies.rb +15 -0
- data/rspec-goodies.gemspec +39 -0
- metadata +143 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 688595f20fe784a45182edf3ebdcb7f19a411ad3dd975259b0022631d925f788
|
|
4
|
+
data.tar.gz: f9a6bd4961893ceab3d91de8592d6ce8ca883d2c68a1706212144e0d74972dcd
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 430633d6935b63b479bc46e9fc1e02dcf65f73ddd5ab8aa38db6efe3adc4c0c85681c2ad0eccc00fd720eefaf958c0566b19e7ae7dc9f2132c223d940a33e5bc
|
|
7
|
+
data.tar.gz: 14841a867c03923ec9af50fa0eda9d02b5d88f9c64ba7dc9783ec4be283ab3fc4e865bdaf3c04d52dc11ee1202b23c6c8bf36277355944978915bceac8131191
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
rspec-goodies (0.0.1)
|
|
5
|
+
activesupport
|
|
6
|
+
byebug
|
|
7
|
+
rails
|
|
8
|
+
|
|
9
|
+
GEM
|
|
10
|
+
remote: https://rubygems.org/
|
|
11
|
+
specs:
|
|
12
|
+
actioncable (7.1.2)
|
|
13
|
+
actionpack (= 7.1.2)
|
|
14
|
+
activesupport (= 7.1.2)
|
|
15
|
+
nio4r (~> 2.0)
|
|
16
|
+
websocket-driver (>= 0.6.1)
|
|
17
|
+
zeitwerk (~> 2.6)
|
|
18
|
+
actionmailbox (7.1.2)
|
|
19
|
+
actionpack (= 7.1.2)
|
|
20
|
+
activejob (= 7.1.2)
|
|
21
|
+
activerecord (= 7.1.2)
|
|
22
|
+
activestorage (= 7.1.2)
|
|
23
|
+
activesupport (= 7.1.2)
|
|
24
|
+
mail (>= 2.7.1)
|
|
25
|
+
net-imap
|
|
26
|
+
net-pop
|
|
27
|
+
net-smtp
|
|
28
|
+
actionmailer (7.1.2)
|
|
29
|
+
actionpack (= 7.1.2)
|
|
30
|
+
actionview (= 7.1.2)
|
|
31
|
+
activejob (= 7.1.2)
|
|
32
|
+
activesupport (= 7.1.2)
|
|
33
|
+
mail (~> 2.5, >= 2.5.4)
|
|
34
|
+
net-imap
|
|
35
|
+
net-pop
|
|
36
|
+
net-smtp
|
|
37
|
+
rails-dom-testing (~> 2.2)
|
|
38
|
+
actionpack (7.1.2)
|
|
39
|
+
actionview (= 7.1.2)
|
|
40
|
+
activesupport (= 7.1.2)
|
|
41
|
+
nokogiri (>= 1.8.5)
|
|
42
|
+
racc
|
|
43
|
+
rack (>= 2.2.4)
|
|
44
|
+
rack-session (>= 1.0.1)
|
|
45
|
+
rack-test (>= 0.6.3)
|
|
46
|
+
rails-dom-testing (~> 2.2)
|
|
47
|
+
rails-html-sanitizer (~> 1.6)
|
|
48
|
+
actiontext (7.1.2)
|
|
49
|
+
actionpack (= 7.1.2)
|
|
50
|
+
activerecord (= 7.1.2)
|
|
51
|
+
activestorage (= 7.1.2)
|
|
52
|
+
activesupport (= 7.1.2)
|
|
53
|
+
globalid (>= 0.6.0)
|
|
54
|
+
nokogiri (>= 1.8.5)
|
|
55
|
+
actionview (7.1.2)
|
|
56
|
+
activesupport (= 7.1.2)
|
|
57
|
+
builder (~> 3.1)
|
|
58
|
+
erubi (~> 1.11)
|
|
59
|
+
rails-dom-testing (~> 2.2)
|
|
60
|
+
rails-html-sanitizer (~> 1.6)
|
|
61
|
+
activejob (7.1.2)
|
|
62
|
+
activesupport (= 7.1.2)
|
|
63
|
+
globalid (>= 0.3.6)
|
|
64
|
+
activemodel (7.1.2)
|
|
65
|
+
activesupport (= 7.1.2)
|
|
66
|
+
activerecord (7.1.2)
|
|
67
|
+
activemodel (= 7.1.2)
|
|
68
|
+
activesupport (= 7.1.2)
|
|
69
|
+
timeout (>= 0.4.0)
|
|
70
|
+
activestorage (7.1.2)
|
|
71
|
+
actionpack (= 7.1.2)
|
|
72
|
+
activejob (= 7.1.2)
|
|
73
|
+
activerecord (= 7.1.2)
|
|
74
|
+
activesupport (= 7.1.2)
|
|
75
|
+
marcel (~> 1.0)
|
|
76
|
+
activesupport (7.1.2)
|
|
77
|
+
base64
|
|
78
|
+
bigdecimal
|
|
79
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
80
|
+
connection_pool (>= 2.2.5)
|
|
81
|
+
drb
|
|
82
|
+
i18n (>= 1.6, < 2)
|
|
83
|
+
minitest (>= 5.1)
|
|
84
|
+
mutex_m
|
|
85
|
+
tzinfo (~> 2.0)
|
|
86
|
+
base64 (0.1.1)
|
|
87
|
+
bigdecimal (3.1.4)
|
|
88
|
+
builder (3.2.4)
|
|
89
|
+
byebug (11.1.3)
|
|
90
|
+
concurrent-ruby (1.2.2)
|
|
91
|
+
connection_pool (2.4.1)
|
|
92
|
+
crass (1.0.6)
|
|
93
|
+
date (3.3.4)
|
|
94
|
+
diff-lcs (1.5.0)
|
|
95
|
+
drb (2.1.1)
|
|
96
|
+
ruby2_keywords
|
|
97
|
+
erubi (1.12.0)
|
|
98
|
+
globalid (1.2.1)
|
|
99
|
+
activesupport (>= 6.1)
|
|
100
|
+
i18n (1.14.1)
|
|
101
|
+
concurrent-ruby (~> 1.0)
|
|
102
|
+
io-console (0.7.1)
|
|
103
|
+
irb (1.11.0)
|
|
104
|
+
rdoc
|
|
105
|
+
reline (>= 0.3.8)
|
|
106
|
+
loofah (2.22.0)
|
|
107
|
+
crass (~> 1.0.2)
|
|
108
|
+
nokogiri (>= 1.12.0)
|
|
109
|
+
mail (2.8.1)
|
|
110
|
+
mini_mime (>= 0.1.1)
|
|
111
|
+
net-imap
|
|
112
|
+
net-pop
|
|
113
|
+
net-smtp
|
|
114
|
+
marcel (1.0.2)
|
|
115
|
+
mini_mime (1.1.5)
|
|
116
|
+
minitest (5.20.0)
|
|
117
|
+
mutex_m (0.1.2)
|
|
118
|
+
net-imap (0.4.9)
|
|
119
|
+
date
|
|
120
|
+
net-protocol
|
|
121
|
+
net-pop (0.1.2)
|
|
122
|
+
net-protocol
|
|
123
|
+
net-protocol (0.2.2)
|
|
124
|
+
timeout
|
|
125
|
+
net-smtp (0.4.0)
|
|
126
|
+
net-protocol
|
|
127
|
+
nio4r (2.7.0)
|
|
128
|
+
nokogiri (1.15.5-arm64-darwin)
|
|
129
|
+
racc (~> 1.4)
|
|
130
|
+
psych (5.1.2)
|
|
131
|
+
stringio
|
|
132
|
+
racc (1.7.3)
|
|
133
|
+
rack (3.0.8)
|
|
134
|
+
rack-session (2.0.0)
|
|
135
|
+
rack (>= 3.0.0)
|
|
136
|
+
rack-test (2.1.0)
|
|
137
|
+
rack (>= 1.3)
|
|
138
|
+
rackup (2.1.0)
|
|
139
|
+
rack (>= 3)
|
|
140
|
+
webrick (~> 1.8)
|
|
141
|
+
rails (7.1.2)
|
|
142
|
+
actioncable (= 7.1.2)
|
|
143
|
+
actionmailbox (= 7.1.2)
|
|
144
|
+
actionmailer (= 7.1.2)
|
|
145
|
+
actionpack (= 7.1.2)
|
|
146
|
+
actiontext (= 7.1.2)
|
|
147
|
+
actionview (= 7.1.2)
|
|
148
|
+
activejob (= 7.1.2)
|
|
149
|
+
activemodel (= 7.1.2)
|
|
150
|
+
activerecord (= 7.1.2)
|
|
151
|
+
activestorage (= 7.1.2)
|
|
152
|
+
activesupport (= 7.1.2)
|
|
153
|
+
bundler (>= 1.15.0)
|
|
154
|
+
railties (= 7.1.2)
|
|
155
|
+
rails-dom-testing (2.2.0)
|
|
156
|
+
activesupport (>= 5.0.0)
|
|
157
|
+
minitest
|
|
158
|
+
nokogiri (>= 1.6)
|
|
159
|
+
rails-html-sanitizer (1.6.0)
|
|
160
|
+
loofah (~> 2.21)
|
|
161
|
+
nokogiri (~> 1.14)
|
|
162
|
+
railties (7.1.2)
|
|
163
|
+
actionpack (= 7.1.2)
|
|
164
|
+
activesupport (= 7.1.2)
|
|
165
|
+
irb
|
|
166
|
+
rackup (>= 1.0.0)
|
|
167
|
+
rake (>= 12.2)
|
|
168
|
+
thor (~> 1.0, >= 1.2.2)
|
|
169
|
+
zeitwerk (~> 2.6)
|
|
170
|
+
rake (13.1.0)
|
|
171
|
+
rdoc (6.6.2)
|
|
172
|
+
psych (>= 4.0.0)
|
|
173
|
+
redis-client (0.19.1)
|
|
174
|
+
connection_pool
|
|
175
|
+
reline (0.4.1)
|
|
176
|
+
io-console (~> 0.5)
|
|
177
|
+
rspec (3.12.0)
|
|
178
|
+
rspec-core (~> 3.12.0)
|
|
179
|
+
rspec-expectations (~> 3.12.0)
|
|
180
|
+
rspec-mocks (~> 3.12.0)
|
|
181
|
+
rspec-core (3.12.2)
|
|
182
|
+
rspec-support (~> 3.12.0)
|
|
183
|
+
rspec-expectations (3.12.3)
|
|
184
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
185
|
+
rspec-support (~> 3.12.0)
|
|
186
|
+
rspec-mocks (3.12.6)
|
|
187
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
188
|
+
rspec-support (~> 3.12.0)
|
|
189
|
+
rspec-support (3.12.1)
|
|
190
|
+
ruby2_keywords (0.0.5)
|
|
191
|
+
sidekiq (7.2.0)
|
|
192
|
+
concurrent-ruby (< 2)
|
|
193
|
+
connection_pool (>= 2.3.0)
|
|
194
|
+
rack (>= 2.2.4)
|
|
195
|
+
redis-client (>= 0.14.0)
|
|
196
|
+
stringio (3.1.0)
|
|
197
|
+
thor (1.3.0)
|
|
198
|
+
timecop (0.9.8)
|
|
199
|
+
timeout (0.4.1)
|
|
200
|
+
tzinfo (2.0.6)
|
|
201
|
+
concurrent-ruby (~> 1.0)
|
|
202
|
+
webrick (1.8.1)
|
|
203
|
+
websocket-driver (0.7.6)
|
|
204
|
+
websocket-extensions (>= 0.1.0)
|
|
205
|
+
websocket-extensions (0.1.5)
|
|
206
|
+
zeitwerk (2.6.12)
|
|
207
|
+
|
|
208
|
+
PLATFORMS
|
|
209
|
+
arm64-darwin-21
|
|
210
|
+
|
|
211
|
+
DEPENDENCIES
|
|
212
|
+
rspec
|
|
213
|
+
rspec-goodies!
|
|
214
|
+
sidekiq
|
|
215
|
+
timecop
|
|
216
|
+
|
|
217
|
+
BUNDLED WITH
|
|
218
|
+
2.4.20
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 James Hu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# rspec-goodies
|
|
2
|
+
|
|
3
|
+
Provides RSpec helpers and matchers for stubs, Sidekiq, and more.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem "rspec-goodies"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
```shell
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install it yourself as:
|
|
20
|
+
|
|
21
|
+
```shell
|
|
22
|
+
gem install rspec-goodies
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Within `spec_helper.rb` or `rails_helper.rb`
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
require "rspec-goodies"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Development
|
|
34
|
+
|
|
35
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
36
|
+
|
|
37
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
38
|
+
|
|
39
|
+
## Contributing
|
|
40
|
+
|
|
41
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/axsuul/rspec-goodies.
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
require "active_support"
|
|
2
|
+
require "active_support/core_ext/hash"
|
|
3
|
+
|
|
4
|
+
module RSpec
|
|
5
|
+
module Goodies
|
|
6
|
+
module Helpers
|
|
7
|
+
module Sidekiq
|
|
8
|
+
# Helper to briefly enable unique jobs functionality Only use this if you're explicitly testing unique job
|
|
9
|
+
# behavior otherwise it is very unpredictable and it's best to disable it for normal tests
|
|
10
|
+
def within_sidekiq_unique
|
|
11
|
+
::Sidekiq::Enterprise.unique!
|
|
12
|
+
|
|
13
|
+
yield
|
|
14
|
+
ensure
|
|
15
|
+
disable_sidekiq_unique!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def disable_sidekiq_unique!
|
|
19
|
+
::Sidekiq.configure_server do |config|
|
|
20
|
+
config.server_middleware do |chain|
|
|
21
|
+
chain.remove(::Sidekiq::Enterprise::Unique::Server)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
::Sidekiq.configure_client do |config|
|
|
26
|
+
config.client_middleware do |chain|
|
|
27
|
+
chain.remove(::Sidekiq::Enterprise::Unique::Client)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Simulate job being performed within the entire middleware stack
|
|
33
|
+
def perform_sidekiq_job(worker_class, *args)
|
|
34
|
+
worker_class.perform_async(*args)
|
|
35
|
+
|
|
36
|
+
actual_worker_class =
|
|
37
|
+
if worker_class.is_a?(::Sidekiq::Worker::Setter)
|
|
38
|
+
worker_class.instance_variable_get("@klass")
|
|
39
|
+
else
|
|
40
|
+
worker_class
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
actual_worker_class.process_job(actual_worker_class.jobs.last)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def process_sidekiq_payloads(payloads)
|
|
47
|
+
payloads.each do |payload|
|
|
48
|
+
payload_hash = ::Sidekiq.load_json(payload)
|
|
49
|
+
worker_class = ::Sidekiq::Testing.constantize(payload_hash["class"])
|
|
50
|
+
|
|
51
|
+
worker_class.process_job(payload_hash)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def add_enqueued_sidekiq_job(worker_class:, args:, metadata: {}, jid: SecureRandom.hex, queue: :default)
|
|
56
|
+
payload = ::Sidekiq.dump_json(
|
|
57
|
+
metadata.merge(
|
|
58
|
+
"jid" => jid,
|
|
59
|
+
"class" => worker_class.to_s,
|
|
60
|
+
"args" => args,
|
|
61
|
+
"queue" => queue.to_s,
|
|
62
|
+
"enqueued_at" => Time.current.to_f,
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
::Sidekiq.redis do |redis|
|
|
67
|
+
redis.sadd("queues", queue.to_s)
|
|
68
|
+
redis.lpush("queue:#{queue}", payload)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
payload
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Simulate job being added to retry queue (based off Sidekiq source code)
|
|
75
|
+
def add_retry_sidekiq_job(
|
|
76
|
+
worker_class:,
|
|
77
|
+
args:,
|
|
78
|
+
metadata: {},
|
|
79
|
+
jid: SecureRandom.hex,
|
|
80
|
+
retry_count: 2,
|
|
81
|
+
retry_at: 1.hour.from_now
|
|
82
|
+
)
|
|
83
|
+
payload = ::Sidekiq.dump_json(
|
|
84
|
+
metadata.merge(
|
|
85
|
+
"jid" => jid,
|
|
86
|
+
"class" => worker_class.to_s,
|
|
87
|
+
"args" => args,
|
|
88
|
+
"queue" => "default",
|
|
89
|
+
"failed_at" => Time.now.to_f,
|
|
90
|
+
"retry_count" => retry_count,
|
|
91
|
+
"error_backtrace" => ["line1", "line2"],
|
|
92
|
+
),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
::Sidekiq.redis do |redis|
|
|
96
|
+
redis.zadd("retry", retry_at.to_f.to_s, payload)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
payload
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def add_scheduled_sidekiq_job(worker_class:, args:, metadata: {}, jid: SecureRandom.hex)
|
|
103
|
+
payload = ::Sidekiq.dump_json(
|
|
104
|
+
metadata.merge(
|
|
105
|
+
"jid" => jid,
|
|
106
|
+
"class" => worker_class.to_s,
|
|
107
|
+
"args" => args,
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
score = Time.now.to_f
|
|
111
|
+
|
|
112
|
+
::Sidekiq.redis do |redis|
|
|
113
|
+
redis.zadd("schedule", score, payload)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
payload
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Simulate job being worked on a worker so that's available via the Sidekiq::Workers.new API (based off Sidekiq
|
|
120
|
+
# source code)
|
|
121
|
+
def add_in_progress_sidekiq_job(worker_class:, args:, jid: SecureRandom.hex)
|
|
122
|
+
@sidekiq_job_in_progress_thread_id ||= 1000
|
|
123
|
+
@sidekiq_job_in_progress_count ||= 0
|
|
124
|
+
|
|
125
|
+
process_id = "foo:#{SecureRandom.hex}"
|
|
126
|
+
job_data = ::Sidekiq.dump_json(
|
|
127
|
+
"queue" => "default",
|
|
128
|
+
"payload" => {
|
|
129
|
+
"jid" => jid,
|
|
130
|
+
"class" => worker_class.to_s,
|
|
131
|
+
"args" => args,
|
|
132
|
+
},
|
|
133
|
+
"run_at" => Time.current.to_i,
|
|
134
|
+
)
|
|
135
|
+
process_data = ::Sidekiq.dump_json(
|
|
136
|
+
"hostname" => "foo",
|
|
137
|
+
"started_at" => Time.now.to_f,
|
|
138
|
+
"queues" => ["default"],
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
::Sidekiq.redis do |redis|
|
|
142
|
+
redis.incr("busy")
|
|
143
|
+
redis.sadd("processes", process_id)
|
|
144
|
+
redis.hmset(
|
|
145
|
+
process_id, "info",
|
|
146
|
+
process_data, "at",
|
|
147
|
+
Time.current.to_f, "busy",
|
|
148
|
+
@sidekiq_job_in_progress_count += 1,
|
|
149
|
+
)
|
|
150
|
+
redis.hmset("#{process_id}:work", @sidekiq_job_in_progress_thread_id += 1, job_data)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module RSpec
|
|
2
|
+
module Goodies
|
|
3
|
+
module Helpers
|
|
4
|
+
module Stubs
|
|
5
|
+
def stub_service_as_spy(klass)
|
|
6
|
+
service_stub = class_spy(klass)
|
|
7
|
+
stub_const(klass.to_s, service_stub)
|
|
8
|
+
|
|
9
|
+
service_stub
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def stub_rails_logger_as_spy
|
|
13
|
+
instance_spy("ActiveSupport::Logger").tap do |stub|
|
|
14
|
+
allow(::Rails).to receive(:logger).and_return(stub)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def stub_class_const(klass, const_string, value)
|
|
19
|
+
raise ArgumentError, "a Class or Module must be passed in" if !klass.is_a?(Class) && !klass.is_a?(Module)
|
|
20
|
+
|
|
21
|
+
# Check that constant actually exists. Also by calling klass here we ensure it's loaded before stubbing
|
|
22
|
+
# constant
|
|
23
|
+
unless klass.const_defined?(const_string)
|
|
24
|
+
raise Exception, "Tried to stub #{klass}::#{const_string} but it doesn't exist!"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
stub_const("#{klass.name}::#{const_string}", value)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Stubs existing constant with resulting hash deep merged with existing hash and hash passed in
|
|
31
|
+
def stub_class_const_and_merge(klass, const_string, hash)
|
|
32
|
+
raise ArgumentError, "must pass in hash" unless hash.is_a?(Hash)
|
|
33
|
+
|
|
34
|
+
existing_hash = klass.const_get(const_string)
|
|
35
|
+
|
|
36
|
+
stub_class_const(klass, const_string, existing_hash.deep_merge(hash))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Stub environment variable so that it doesn't leak out of tests
|
|
40
|
+
def stub_env(name, value)
|
|
41
|
+
allow(ENV).to receive(:[]).with(name).and_return(value)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Only works with nested credentials for now
|
|
45
|
+
def stub_rails_credentials(stubbed_config)
|
|
46
|
+
raise NotImplementedError, "Rails not found" unless Object.const_defined?(:Rails)
|
|
47
|
+
|
|
48
|
+
credentials = ::Rails.application.credentials
|
|
49
|
+
credentials_config = credentials.config
|
|
50
|
+
stubbed_credentials_config = credentials_config
|
|
51
|
+
|
|
52
|
+
stubbed_config.each do |key, key_stubbed_config|
|
|
53
|
+
key = key.to_sym
|
|
54
|
+
|
|
55
|
+
case key_stubbed_config
|
|
56
|
+
when Hash
|
|
57
|
+
key_stubbed_config.each do |child_key, _|
|
|
58
|
+
if !credentials_config.key?(key) || !credentials_config[key].key?(child_key)
|
|
59
|
+
raise ArgumentError, "Tried to stub Rails credential #{key}: #{child_key} but it doesn't exist!"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Merge in existing credentials so we don't break accessing other credentials
|
|
64
|
+
stubbed_credentials_config[key].merge!(key_stubbed_config)
|
|
65
|
+
when String
|
|
66
|
+
# Not nested so set it directly
|
|
67
|
+
stubbed_credentials_config[key] = key_stubbed_config
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
allow(::Rails.application).to receive(:credentials).and_return(OpenStruct.new(stubbed_credentials_config))
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Used to match if array includes elements and when order matters
|
|
2
|
+
RSpec::Matchers.define :include_in_order do |*args|
|
|
3
|
+
match do |collection|
|
|
4
|
+
expect_to_match = lambda do |value, matcher|
|
|
5
|
+
# If composable matcher (e.g. a_string_matching(...) then we need to use a different expectation)
|
|
6
|
+
if matcher.class.name.match?(/Matcher/)
|
|
7
|
+
expect(value).to match(matcher)
|
|
8
|
+
else
|
|
9
|
+
expect(value).to eq(matcher)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
is_matched_in_order = false
|
|
14
|
+
|
|
15
|
+
# Go through collection and try to find the first match
|
|
16
|
+
collection.each_with_index do |element, collection_index|
|
|
17
|
+
initial_matcher = args.first
|
|
18
|
+
|
|
19
|
+
expect_to_match.call(element, initial_matcher)
|
|
20
|
+
|
|
21
|
+
# If reaches here, that means we found the first match so let's see if the remaining also match in order
|
|
22
|
+
is_matched_in_order = args[1..-1].each_with_index.all? do |pending_matcher, pending_matcher_index|
|
|
23
|
+
expect_to_match.call(collection[collection_index + pending_matcher_index + 1], pending_matcher)
|
|
24
|
+
|
|
25
|
+
true
|
|
26
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# No need to search anymore once we found it
|
|
31
|
+
break if is_matched_in_order
|
|
32
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
|
33
|
+
# Keep trying to find the first match
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
is_matched_in_order
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
RSpec::Matchers.alias_matcher :a_collection_including_in_order, :include_in_order
|
|
41
|
+
|
|
42
|
+
# So we can do:
|
|
43
|
+
#
|
|
44
|
+
# expect(collection).to not_any(...)
|
|
45
|
+
#
|
|
46
|
+
# since we can't do:
|
|
47
|
+
#
|
|
48
|
+
# expect(collection).not_to all(...)
|
|
49
|
+
#
|
|
50
|
+
RSpec::Matchers.define_negated_matcher :not_any, :include
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
RSpec::Matchers.define :match_timestamp do |expected, decimal_places = nil|
|
|
2
|
+
normalize = lambda do |value|
|
|
3
|
+
value = DateTime.parse(value) if value.is_a?(String)
|
|
4
|
+
|
|
5
|
+
# Compare floats since there can be sub-seconds
|
|
6
|
+
normalized = value.to_f
|
|
7
|
+
|
|
8
|
+
# Can be limited to decimals places (e.g. 0.002 can match 0.0019 if "3" is provided)
|
|
9
|
+
normalized = normalized.round(decimal_places) if decimal_places
|
|
10
|
+
|
|
11
|
+
normalized
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
match do |actual|
|
|
15
|
+
normalize.call(actual) == normalize.call(expected)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
failure_message do |actual|
|
|
19
|
+
<<~MESSAGE
|
|
20
|
+
expected that #{actual} (#{normalize.call(actual)}) would match timestamp #{expected}
|
|
21
|
+
(#{normalize.call(expected)})"
|
|
22
|
+
MESSAGE
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
require "rspec/matchers"
|
|
2
|
+
require "sidekiq/testing"
|
|
3
|
+
|
|
4
|
+
class SidekiqJobsEnqueuedMatcher
|
|
5
|
+
include RSpec::Matchers
|
|
6
|
+
|
|
7
|
+
attr_reader :new_jobs, :new_jobs_matching_properties
|
|
8
|
+
|
|
9
|
+
def initialize(actual, worker_class, expected_size = nil, expected_properties = {})
|
|
10
|
+
@actual = actual
|
|
11
|
+
@worker_class = worker_class
|
|
12
|
+
|
|
13
|
+
# Size is optional to be passed in
|
|
14
|
+
if expected_size.is_a?(Hash)
|
|
15
|
+
@expected_properties = expected_size
|
|
16
|
+
@expected_size = nil
|
|
17
|
+
else
|
|
18
|
+
@expected_properties = expected_properties
|
|
19
|
+
@expected_size = expected_size
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Normalize
|
|
23
|
+
@expected_properties.stringify_keys!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def matches?
|
|
27
|
+
# Get jobs before so that we only perform new jobs
|
|
28
|
+
jobs_before = @worker_class.jobs.clone
|
|
29
|
+
|
|
30
|
+
# Calls the actual block of the matcher
|
|
31
|
+
@actual.call
|
|
32
|
+
|
|
33
|
+
jobs_after = @worker_class.jobs
|
|
34
|
+
@new_jobs = jobs_after - jobs_before
|
|
35
|
+
|
|
36
|
+
@new_jobs_matching_properties = @new_jobs.select do |job|
|
|
37
|
+
matched_count = 0
|
|
38
|
+
|
|
39
|
+
@expected_properties.each do |key, value|
|
|
40
|
+
case key
|
|
41
|
+
when "args"
|
|
42
|
+
# Coerce to an array unless it's a matcher
|
|
43
|
+
value = Array(value) unless value.respond_to?(:base_matcher)
|
|
44
|
+
|
|
45
|
+
expect(Array(job[key])).to match(value)
|
|
46
|
+
when "at"
|
|
47
|
+
if (scheduled_at = job[key])
|
|
48
|
+
# It's a float in job
|
|
49
|
+
expect(scheduled_at).to eq(value.to_f)
|
|
50
|
+
|
|
51
|
+
# Otherwise if nil, then we are looking for jobs that aren't scheduled
|
|
52
|
+
else
|
|
53
|
+
expect(job.key?("at")).to eq false
|
|
54
|
+
end
|
|
55
|
+
when "bid"
|
|
56
|
+
expect(job[key]).to match(value)
|
|
57
|
+
when "queue", "unique_for", "unique_until"
|
|
58
|
+
expect(job[key].to_s).to eq(value.to_s)
|
|
59
|
+
when "metadata"
|
|
60
|
+
# Even though we call this metadata, it's actually just keys in the job hash. We iterate through each key so
|
|
61
|
+
# that it also works on checking for nil values
|
|
62
|
+
value.each do |metadata_key, metadata_value|
|
|
63
|
+
expect(job[metadata_key]).to eq metadata_value
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
matched_count += 1
|
|
68
|
+
rescue RSpec::Expectations::ExpectationNotMetError
|
|
69
|
+
# Doesn't contribute to matched_count if any expectations fail
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
matched_count == @expected_properties.count
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Check expected number of new jobs enqueued to match size if specified
|
|
76
|
+
if @expected_size
|
|
77
|
+
@new_jobs_matching_properties.size == @expected_size
|
|
78
|
+
else
|
|
79
|
+
@new_jobs_matching_properties.any?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def new_jobs_sanitized
|
|
84
|
+
(@new_jobs || []).map { |j| j.except("jid", "backtrace", "retry", "created_at", "enqueued_at") }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
RSpec::Matchers.define :have_sidekiq_jobs_enqueued do |*args|
|
|
89
|
+
match do |actual|
|
|
90
|
+
@matcher = SidekiqJobsEnqueuedMatcher.new(actual, *args)
|
|
91
|
+
|
|
92
|
+
@matcher.matches?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
failure_message do |_|
|
|
96
|
+
"expected Sidekiq jobs to be enqueued with #{args.join(', ')} but instead found:\n\n#{@matcher.new_jobs_sanitized.pretty_inspect}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def supports_block_expectations?
|
|
100
|
+
true
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
RSpec::Matchers.alias_matcher :have_sidekiq_job_enqueued, :have_sidekiq_jobs_enqueued
|
|
105
|
+
RSpec::Matchers.define_negated_matcher :not_have_sidekiq_jobs_enqueued, :have_sidekiq_jobs_enqueued
|
|
106
|
+
|
|
107
|
+
RSpec::Matchers.define :have_sidekiq_jobs_enqueued_and_performed do |*args|
|
|
108
|
+
match do |actual|
|
|
109
|
+
# Add the first arguments to the end since we are processing the chain from
|
|
110
|
+
# last to first
|
|
111
|
+
@worker_chain ||= []
|
|
112
|
+
@worker_chain << args
|
|
113
|
+
|
|
114
|
+
# Alias
|
|
115
|
+
perform_jobs = actual
|
|
116
|
+
|
|
117
|
+
while @worker_chain.any?
|
|
118
|
+
args = @worker_chain.pop
|
|
119
|
+
worker_class = args.first
|
|
120
|
+
|
|
121
|
+
@matcher = SidekiqJobsEnqueuedMatcher.new(perform_jobs, *args)
|
|
122
|
+
|
|
123
|
+
expect(@matcher.matches?).to eq true
|
|
124
|
+
|
|
125
|
+
new_jobs_matching_properties = @matcher.new_jobs_matching_properties
|
|
126
|
+
|
|
127
|
+
perform_jobs = lambda do
|
|
128
|
+
new_jobs_matching_properties.each do |job|
|
|
129
|
+
worker_class = job["class"]
|
|
130
|
+
|
|
131
|
+
# Remove from queue after performing it
|
|
132
|
+
Sidekiq::Queues.delete_for(job["jid"], job["queue"], worker_class)
|
|
133
|
+
|
|
134
|
+
worker_class.constantize.process_job(job)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Make sure we call perform jobs one last time if there's no more in chain
|
|
140
|
+
perform_jobs.call
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
chain :thereafter do |*next_args|
|
|
144
|
+
@worker_chain ||= []
|
|
145
|
+
@worker_chain.prepend(next_args)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
failure_message do |_|
|
|
149
|
+
"expected Sidekiq jobs to be enqueued and performed with #{args.join(', ')} but instead found:\n\n#{@matcher.new_jobs_sanitized.pretty_inspect}"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def supports_block_expectations?
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
RSpec::Matchers.define :have_sidekiq_batch_with_callback_triggered do |event, callback_class|
|
|
158
|
+
match do |actual|
|
|
159
|
+
received_callbacks = Hash.new do |hash, key|
|
|
160
|
+
hash[key] = {
|
|
161
|
+
death: [],
|
|
162
|
+
complete: [],
|
|
163
|
+
success: [],
|
|
164
|
+
}
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
sidekiq_batch_stub = ::Sidekiq::Batch.new.tap do |stub|
|
|
168
|
+
# This can be called multiple times so track each time
|
|
169
|
+
allow(stub).to receive(:on) do |actual_event, actual_callback_class, actual_callback_options|
|
|
170
|
+
received_callbacks[actual_callback_class.name][actual_event].push(actual_callback_options)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
allow(::Sidekiq::Batch).to receive(:new).and_return(stub)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Ensure stuff happens within batch
|
|
177
|
+
allow(sidekiq_batch_stub).to receive(:jobs).and_call_original
|
|
178
|
+
|
|
179
|
+
actual.call
|
|
180
|
+
|
|
181
|
+
# Only care about those received for event and callback
|
|
182
|
+
relevant_received_callbacks = received_callbacks[callback_class.to_s][event]
|
|
183
|
+
|
|
184
|
+
expect(relevant_received_callbacks).to be_any
|
|
185
|
+
|
|
186
|
+
# Once block is complete, simulate Sidekiq batch callback is called by manually calling it ourselves since it
|
|
187
|
+
# doesn't automatically get called in tests
|
|
188
|
+
::Sidekiq::Batch.new.tap do |batch|
|
|
189
|
+
# Ensure created in redis so that we can access it
|
|
190
|
+
batch.jobs {}
|
|
191
|
+
|
|
192
|
+
# Simulate that callback is called for each time callback options were received
|
|
193
|
+
relevant_received_callbacks.each do |callback_options|
|
|
194
|
+
# Also simulate that callback options are converted to JSON and back to hash which is what happens in production
|
|
195
|
+
callback_class.new.send(
|
|
196
|
+
:"on_#{event}",
|
|
197
|
+
::Sidekiq::Batch::Status.new(batch.bid),
|
|
198
|
+
JSON.parse(callback_options.to_json),
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
failure_message do |_|
|
|
205
|
+
"expected jobs to perform within Sidekiq batch with #{callback_class} callback to be triggered by #{event}"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def supports_block_expectations?
|
|
209
|
+
true
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
RSpec::Matchers.define_negated_matcher :not_have_sidekiq_batch_with_callback_triggered,
|
|
214
|
+
:have_sidekiq_batch_with_callback_triggered
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
RSpec::Matchers.define_negated_matcher :a_string_not_matching, :a_string_matching
|
|
2
|
+
|
|
3
|
+
RSpec::Matchers.define :match_url do |matched_url|
|
|
4
|
+
match do |url|
|
|
5
|
+
uri = Addressable::URI.parse(url)
|
|
6
|
+
matched_uri = Addressable::URI.parse(matched_url)
|
|
7
|
+
|
|
8
|
+
uri.host == matched_uri.host &&
|
|
9
|
+
uri.path == matched_uri.path &&
|
|
10
|
+
uri.query_values == matched_uri.query_values
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
failure_message do |url|
|
|
14
|
+
"expected the url: #{url} to match the url: #{matched_url}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rspec/core"
|
|
4
|
+
require "rspec/goodies/helpers/sidekiq"
|
|
5
|
+
require "rspec/goodies/helpers/stubs"
|
|
6
|
+
require "rspec/goodies/matchers/collection"
|
|
7
|
+
require "rspec/goodies/matchers/date_time"
|
|
8
|
+
require "rspec/goodies/matchers/hash"
|
|
9
|
+
require "rspec/goodies/matchers/sidekiq"
|
|
10
|
+
require "rspec/goodies/matchers/string"
|
|
11
|
+
|
|
12
|
+
RSpec.configure do |config|
|
|
13
|
+
config.include(RSpec::Goodies::Helpers::Sidekiq)
|
|
14
|
+
config.include(RSpec::Goodies::Helpers::Stubs)
|
|
15
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "rspec-goodies"
|
|
5
|
+
spec.version = "0.0.1"
|
|
6
|
+
spec.authors = ["James Hu"]
|
|
7
|
+
|
|
8
|
+
spec.summary = "RSpec goodies full of helpers, matchers, and more"
|
|
9
|
+
spec.description = "RSpec goodies full of helpers, matchers, and more"
|
|
10
|
+
spec.homepage = "https://github.com/axsuul/rspec-goodies"
|
|
11
|
+
spec.license = "MIT"
|
|
12
|
+
|
|
13
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
15
|
+
spec.metadata["source_code_uri"] = "https://github.com/axsuul/rspec-goodies"
|
|
16
|
+
|
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
19
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
20
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
21
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
spec.bindir = "exe"
|
|
25
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
26
|
+
spec.require_paths = ["lib"]
|
|
27
|
+
|
|
28
|
+
# Uncomment to register a new dependency of your gem
|
|
29
|
+
spec.add_dependency "activesupport"
|
|
30
|
+
spec.add_dependency "byebug"
|
|
31
|
+
spec.add_dependency "rails"
|
|
32
|
+
|
|
33
|
+
spec.add_development_dependency "rspec"
|
|
34
|
+
spec.add_development_dependency "sidekiq"
|
|
35
|
+
spec.add_development_dependency "timecop"
|
|
36
|
+
|
|
37
|
+
# For more information and examples about making a new gem, check out our
|
|
38
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
|
39
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rspec-goodies
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- James Hu
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2024-01-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: byebug
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rails
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: sidekiq
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: timecop
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
description: RSpec goodies full of helpers, matchers, and more
|
|
98
|
+
email:
|
|
99
|
+
executables: []
|
|
100
|
+
extensions: []
|
|
101
|
+
extra_rdoc_files: []
|
|
102
|
+
files:
|
|
103
|
+
- Gemfile
|
|
104
|
+
- Gemfile.lock
|
|
105
|
+
- LICENSE.txt
|
|
106
|
+
- README.md
|
|
107
|
+
- Rakefile
|
|
108
|
+
- lib/rspec-goodies.rb
|
|
109
|
+
- lib/rspec/goodies/helpers/sidekiq.rb
|
|
110
|
+
- lib/rspec/goodies/helpers/stubs.rb
|
|
111
|
+
- lib/rspec/goodies/matchers/collection.rb
|
|
112
|
+
- lib/rspec/goodies/matchers/date_time.rb
|
|
113
|
+
- lib/rspec/goodies/matchers/hash.rb
|
|
114
|
+
- lib/rspec/goodies/matchers/sidekiq.rb
|
|
115
|
+
- lib/rspec/goodies/matchers/string.rb
|
|
116
|
+
- rspec-goodies.gemspec
|
|
117
|
+
homepage: https://github.com/axsuul/rspec-goodies
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
120
|
+
metadata:
|
|
121
|
+
allowed_push_host: https://rubygems.org
|
|
122
|
+
homepage_uri: https://github.com/axsuul/rspec-goodies
|
|
123
|
+
source_code_uri: https://github.com/axsuul/rspec-goodies
|
|
124
|
+
post_install_message:
|
|
125
|
+
rdoc_options: []
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
requirements: []
|
|
139
|
+
rubygems_version: 3.1.6
|
|
140
|
+
signing_key:
|
|
141
|
+
specification_version: 4
|
|
142
|
+
summary: RSpec goodies full of helpers, matchers, and more
|
|
143
|
+
test_files: []
|