esse-rspec 0.0.4 → 0.0.6
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 +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:
|