esse-rspec 0.0.4 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/Gemfile.lock +1 -1
- data/README.md +49 -22
- data/lib/esse/rspec/class_methods.rb +9 -12
- data/lib/esse/rspec/matchers.rb +213 -0
- data/lib/esse/rspec/version.rb +1 -1
- data/lib/esse/rspec.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96a249015799017da0f82dd2d826cd06e743c2a03170506b6f64a6ca4cf4ad81
|
4
|
+
data.tar.gz: 6f5ed354dc863d91645a0a32de557f178cb972b7016caae6bca5413a7c4dd810
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0aac5e90d3eeb43f5b99a32c4316a6cbe243ef50e92ae69d3654889bb3352a81d25b8338c1a17be4b1693f2e3731aeeb64e20864011e384e6f8827ddd871644c
|
7
|
+
data.tar.gz: 014b753e7e9ab4b60adb0d37143f2d789cf0062f4afa29316f06a7716245f8c6eb9216eb1e5f0c2b88f91bc9ab80659fdd2bb1061b43add533712124f27b2c67
|
data/CHANGELOG.md
CHANGED
@@ -4,10 +4,14 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## 0.0.6 - 2023-11-17
|
8
|
+
* Fixing the `with` chain method of `esse_expect_search` matcher to accept hash instead of keyword arguments.
|
9
|
+
## 0.0.5 - 2023-11-17
|
10
|
+
* Replacing the `stub_esse_search` by a new `esse_expect_search` matcher that cover all transport actions
|
11
|
+
|
7
12
|
## 0.0.4 - 2023-11-15
|
8
13
|
* The `cluster_id` argument of `stub_esse_search` is useless when the given index is a Esse::Index. Cluster id is now optional.
|
9
14
|
|
10
|
-
|
11
15
|
## 0.0.3 - 2023-11-15
|
12
16
|
It should not automatically require `esse/rspec` anymore. You should require it manually in your `spec_helper.rb` file.
|
13
17
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -26,6 +26,55 @@ Require the `esse/rspec` file in your `spec_helper.rb` file:
|
|
26
26
|
require 'esse/rspec'
|
27
27
|
```
|
28
28
|
|
29
|
+
## Mock Requests
|
30
|
+
|
31
|
+
Stub a search request to specific index and return a response
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
expect(ProductsIndex).to esse_receive_request(:search)
|
35
|
+
.with(body: {query: {match_all: {}}, size: 10})
|
36
|
+
.and_return('hits' => { 'total' => 0, 'hits' => [] })
|
37
|
+
|
38
|
+
query = ProductsIndex.search(query: {match_all: {}}, size: 10)
|
39
|
+
query.response.total # => 0
|
40
|
+
```
|
41
|
+
|
42
|
+
Stub a search request to an index with a non 200 response
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
expect(ProductsIndex).to esse_receive_request(:search)
|
46
|
+
.with(body: {query: {match_all: {}}, size: 10})
|
47
|
+
.and_raise_http_status(500, {"error" => 'Something went wrong'})
|
48
|
+
|
49
|
+
begin
|
50
|
+
ProductsIndex.search(query: {match_all: {}}, size: 10).response
|
51
|
+
rescue Esse::Transport::InternalServerError => e
|
52
|
+
puts e.message # => {"error" => 'Something went wrong'}
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
Stub a cluster search request
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
|
60
|
+
expect(Esse.cluster(:default)).to esse_receive_request(:search)
|
61
|
+
.with(index: 'geos_*', body: {query: {match_all: {}}, size: 10})
|
62
|
+
.and_return('hits' => { 'total' => 0, 'hits' => [] })
|
63
|
+
|
64
|
+
query = Esse.cluster(:default).search('geos_*', body: {query: {match_all: {}}, size: 10})
|
65
|
+
query.response.total # => 0
|
66
|
+
```
|
67
|
+
|
68
|
+
Stub a api/transport request
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
expect(Esse.cluster).to esse_receive_request(:get)
|
72
|
+
.with(id: '1', index: 'products')
|
73
|
+
.and_return('_id' => '1', '_source' => {title: 'Product 1'})
|
74
|
+
|
75
|
+
Esse.cluster.api.get('1', index: 'products') # => { '_id' => '1', '_source' => {title: 'Product 1'} }
|
76
|
+
```
|
77
|
+
|
29
78
|
### Stubbing Esse::Index classes
|
30
79
|
|
31
80
|
```ruby
|
@@ -42,25 +91,3 @@ it 'defines the ProductsIndex class' do
|
|
42
91
|
expect(ProductsIndex::Product).to be < Esse::Index::Repository
|
43
92
|
end
|
44
93
|
```
|
45
|
-
|
46
|
-
### Stubbing search requests
|
47
|
-
|
48
|
-
```ruby
|
49
|
-
before do
|
50
|
-
stub_esse_search(:default, "geos_*", body: {query: {match_all: {}}, size: 10}) do
|
51
|
-
{
|
52
|
-
"hits" => {
|
53
|
-
"total" => {
|
54
|
-
"value" => 1000
|
55
|
-
},
|
56
|
-
"hits" => [{}] * 20
|
57
|
-
}
|
58
|
-
}
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'returns a Query using the stubbed response' do
|
63
|
-
query = ProductsIndex.search('geos_*', body: {query: {match_all: {}}, size: 10})
|
64
|
-
expect(query.responsee.total).to eq(1000)
|
65
|
-
end
|
66
|
-
```
|
@@ -17,21 +17,18 @@ module Esse
|
|
17
17
|
stub_const(Esse::Hstring.new(name).camelize.to_s, klass)
|
18
18
|
end
|
19
19
|
|
20
|
-
def stub_esse_search(*cluster_and_indexes, **definition)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
cluster_and_indexes
|
25
|
-
|
26
|
-
Esse.
|
20
|
+
def stub_esse_search(*cluster_and_indexes, **definition) # Let's deprecated this method
|
21
|
+
target = cluster_and_indexes.shift
|
22
|
+
if target.is_a?(Symbol) || target.is_a?(String) && Esse.config.cluster_ids.include?(target.to_sym)
|
23
|
+
target = Esse.cluster(target)
|
24
|
+
definition[:index] ||= Esse::Search::Query.normalize_indices(*cluster_and_indexes)
|
25
|
+
elsif target.is_a?(String) || target.is_a?(Symbol)
|
26
|
+
definition[:index] ||= Esse::Search::Query.normalize_indices(target.to_s)
|
27
|
+
target = Esse.cluster
|
27
28
|
end
|
28
29
|
|
29
|
-
indexes = cluster_and_indexes
|
30
|
-
transport = cluster.api
|
31
|
-
definition[:index] ||= Esse::Search::Query.normalize_indices(*indexes)
|
32
30
|
response = yield
|
33
|
-
|
34
|
-
allow(transport).to receive(:search).with(**definition).and_return(response)
|
31
|
+
expect(target).to esse_receive_request(:search).with(**definition).and_return(response)
|
35
32
|
end
|
36
33
|
end
|
37
34
|
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Esse
|
3
|
+
module RSpec
|
4
|
+
module Matchers
|
5
|
+
|
6
|
+
class EsseReceiveRequest
|
7
|
+
include ::RSpec::Matchers::Composable
|
8
|
+
|
9
|
+
STATUS_ERRORS = {
|
10
|
+
300 => Esse::Transport::MultipleChoicesError,
|
11
|
+
301 => Esse::Transport::MovedPermanentlyError,
|
12
|
+
302 => Esse::Transport::FoundError,
|
13
|
+
303 => Esse::Transport::SeeOtherError,
|
14
|
+
304 => Esse::Transport::NotModifiedError,
|
15
|
+
305 => Esse::Transport::UseProxyError,
|
16
|
+
307 => Esse::Transport::TemporaryRedirectError,
|
17
|
+
308 => Esse::Transport::PermanentRedirectError,
|
18
|
+
400 => Esse::Transport::BadRequestError,
|
19
|
+
401 => Esse::Transport::UnauthorizedError,
|
20
|
+
402 => Esse::Transport::PaymentRequiredError,
|
21
|
+
403 => Esse::Transport::ForbiddenError,
|
22
|
+
404 => Esse::Transport::NotFoundError,
|
23
|
+
405 => Esse::Transport::MethodNotAllowedError,
|
24
|
+
406 => Esse::Transport::NotAcceptableError,
|
25
|
+
407 => Esse::Transport::ProxyAuthenticationRequiredError,
|
26
|
+
408 => Esse::Transport::RequestTimeoutError,
|
27
|
+
409 => Esse::Transport::ConflictError,
|
28
|
+
410 => Esse::Transport::GoneError,
|
29
|
+
411 => Esse::Transport::LengthRequiredError,
|
30
|
+
412 => Esse::Transport::PreconditionFailedError,
|
31
|
+
413 => Esse::Transport::RequestEntityTooLargeError,
|
32
|
+
414 => Esse::Transport::RequestURITooLongError,
|
33
|
+
415 => Esse::Transport::UnsupportedMediaTypeError,
|
34
|
+
416 => Esse::Transport::RequestedRangeNotSatisfiableError,
|
35
|
+
417 => Esse::Transport::ExpectationFailedError,
|
36
|
+
418 => Esse::Transport::ImATeapotError,
|
37
|
+
421 => Esse::Transport::TooManyConnectionsFromThisIPError,
|
38
|
+
426 => Esse::Transport::UpgradeRequiredError,
|
39
|
+
450 => Esse::Transport::BlockedByWindowsParentalControlsError,
|
40
|
+
494 => Esse::Transport::RequestHeaderTooLargeError,
|
41
|
+
497 => Esse::Transport::HTTPToHTTPSError,
|
42
|
+
499 => Esse::Transport::ClientClosedRequestError,
|
43
|
+
500 => Esse::Transport::InternalServerError,
|
44
|
+
501 => Esse::Transport::NotImplementedError,
|
45
|
+
502 => Esse::Transport::BadGatewayError,
|
46
|
+
503 => Esse::Transport::ServiceUnavailableError,
|
47
|
+
504 => Esse::Transport::GatewayTimeoutError,
|
48
|
+
505 => Esse::Transport::HTTPVersionNotSupportedError,
|
49
|
+
506 => Esse::Transport::VariantAlsoNegotiatesError,
|
50
|
+
510 => Esse::Transport::NotExtendedError
|
51
|
+
}
|
52
|
+
|
53
|
+
def initialize(*args)
|
54
|
+
@transport_method = args.shift
|
55
|
+
unless Esse::Transport.instance_methods.include?(@transport_method)
|
56
|
+
raise ArgumentError, "expected #{@transport_method.inspect} to be a Esse::Transport method"
|
57
|
+
end
|
58
|
+
|
59
|
+
@definition = {}
|
60
|
+
if (hash = args.last).is_a?(Hash)
|
61
|
+
@definition.merge!(hash.transform_keys(&:to_sym))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def description
|
66
|
+
"esse receive request"
|
67
|
+
end
|
68
|
+
|
69
|
+
def matches?(index_or_cluster)
|
70
|
+
normalize_actual_args!(index_or_cluster)
|
71
|
+
allow(@cluster).to receive(:api).and_return(transport)
|
72
|
+
allow(transport).to receive_expected
|
73
|
+
receive_expected.matches?(transport)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param definition [Hash]
|
77
|
+
def with(definition)
|
78
|
+
@definition.update(definition.transform_keys(&:to_sym))
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param status [Integer] HTTP status code
|
83
|
+
# @param response [Hash, String, nil] response body
|
84
|
+
def and_raise_http_status(status, response = nil)
|
85
|
+
@error_class = STATUS_ERRORS[status] || Esse::Transport::ServerError
|
86
|
+
@response = response if response
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param response [Hash, String, nil] response body
|
91
|
+
def and_return(response)
|
92
|
+
@response = response
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param error_class [Class] error class
|
97
|
+
def and_raise(error_class, response = nil)
|
98
|
+
@error_class = error_class
|
99
|
+
@response = response if response
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def and_call_original
|
104
|
+
@and_call_original = true
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
def exactly(times)
|
109
|
+
@times = times
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def once
|
114
|
+
exactly(1)
|
115
|
+
end
|
116
|
+
|
117
|
+
def twice
|
118
|
+
exactly(2)
|
119
|
+
end
|
120
|
+
|
121
|
+
# @param times [Integer]
|
122
|
+
def at_least(times)
|
123
|
+
@at_least = times
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# @param times [Integer]
|
128
|
+
def at_most(times)
|
129
|
+
@at_most = times
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
def failure_message
|
134
|
+
"expected that #{@cluster.id} cluster would receive `#{@transport_method}` with #{@definition}".tap do |str|
|
135
|
+
if @error_class
|
136
|
+
str << " and raise #{@error_class}"
|
137
|
+
str << " with #{@response}" if @response
|
138
|
+
elsif @response
|
139
|
+
str << " and return #{@response}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def failure_message_when_negated
|
145
|
+
"expected that #{@cluster.id} cluster would not receive `#{@transport_method}` with #{@definition}".tap do |str|
|
146
|
+
if @error_class
|
147
|
+
str << " and raise #{@error_class}"
|
148
|
+
str << " with #{@response}" if @response
|
149
|
+
elsif @response
|
150
|
+
str << " and return #{@response}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def transport
|
158
|
+
@cluster.api
|
159
|
+
end
|
160
|
+
|
161
|
+
def normalize_actual_args!(index_or_cluster)
|
162
|
+
if index_or_cluster.is_a?(Esse::Cluster)
|
163
|
+
@cluster = index_or_cluster
|
164
|
+
elsif index_or_cluster.is_a?(Class) && index_or_cluster < Esse::Index
|
165
|
+
@definition[:index] ||= Esse::Search::Query.normalize_indices(index_or_cluster)
|
166
|
+
@cluster ||= index_or_cluster.cluster
|
167
|
+
elsif index_or_cluster.is_a?(Symbol) || index_or_cluster.is_a?(String)
|
168
|
+
if Esse.config.cluster_ids.include?(index_or_cluster.to_sym)
|
169
|
+
@cluster = Esse.cluster(index_or_cluster)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
raise ArgumentError, "expected #{index_or_cluster.inspect} to be an Esse::Index or Esse::Cluster" unless @cluster
|
173
|
+
end
|
174
|
+
|
175
|
+
def supports_block_expectations?
|
176
|
+
false
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def allow(target)
|
182
|
+
::RSpec::Mocks::AllowanceTarget.new(target)
|
183
|
+
end
|
184
|
+
|
185
|
+
def receive(method_name)
|
186
|
+
::RSpec::Mocks::Matchers::Receive.new(method_name, nil)
|
187
|
+
end
|
188
|
+
|
189
|
+
def receive_expected
|
190
|
+
@receive_expected ||= begin
|
191
|
+
matcher = receive(@transport_method).with(**@definition)
|
192
|
+
matcher = matcher.and_return(@response) if defined?(@response)
|
193
|
+
matcher = matcher.and_raise(*[@error_class, @response].compact) if @error_class
|
194
|
+
matcher = matcher.and_call_original if defined?(@and_call_original)
|
195
|
+
if @times
|
196
|
+
matcher = matcher.exactly(@times).times
|
197
|
+
elsif @at_least
|
198
|
+
matcher = matcher.at_least(@at_least).times
|
199
|
+
elsif @at_most
|
200
|
+
matcher = matcher.at_most(@at_most).times
|
201
|
+
end
|
202
|
+
matcher
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def esse_receive_request(*args)
|
208
|
+
EsseReceiveRequest.new(*args)
|
209
|
+
end
|
210
|
+
alias_method :receive_esse_request, :esse_receive_request
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
data/lib/esse/rspec/version.rb
CHANGED
data/lib/esse/rspec.rb
CHANGED
@@ -4,6 +4,7 @@ require "esse"
|
|
4
4
|
|
5
5
|
require_relative "rspec/version"
|
6
6
|
require_relative "rspec/class_methods"
|
7
|
+
require_relative "rspec/matchers"
|
7
8
|
|
8
9
|
module Esse
|
9
10
|
module RSpec
|
@@ -13,5 +14,6 @@ end
|
|
13
14
|
if defined?(::RSpec)
|
14
15
|
::RSpec.configure do |config|
|
15
16
|
config.include Esse::RSpec::ClassMethods
|
17
|
+
config.include Esse::RSpec::Matchers
|
16
18
|
end
|
17
19
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: esse-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcos G. Zimmermann
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: esse
|
@@ -130,6 +130,7 @@ files:
|
|
130
130
|
- lib/esse-rspec.rb
|
131
131
|
- lib/esse/rspec.rb
|
132
132
|
- lib/esse/rspec/class_methods.rb
|
133
|
+
- lib/esse/rspec/matchers.rb
|
133
134
|
- lib/esse/rspec/version.rb
|
134
135
|
homepage: https://github.com/marcosgz/esse-rspec
|
135
136
|
licenses:
|