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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edf7cd6ae62013f70d49d21f5e633f90b07cb4331b7f3573d1614d0e74e26ec2
4
- data.tar.gz: df206c8c7f5c749dfda9ed95bf8b04ee7fdf04d215a356760a936a0069be3660
3
+ metadata.gz: 96a249015799017da0f82dd2d826cd06e743c2a03170506b6f64a6ca4cf4ad81
4
+ data.tar.gz: 6f5ed354dc863d91645a0a32de557f178cb972b7016caae6bca5413a7c4dd810
5
5
  SHA512:
6
- metadata.gz: 320944994296eca28470899960df56a72aefea1ef1137e46ce8de30a519eeb505dc531103fdea416b8c67badb9a6843d12240a6dc337a1132797f33a075e356c
7
- data.tar.gz: 2ab0182c6862f3e7745fadaf136be9f397dc2bda626c29fbff879830585d1e545dd28df4fd7be93eebdec23e934a216198ab36b988c610807ea77d2fedfdf8b0
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- esse-rspec (0.0.4)
4
+ esse-rspec (0.0.6)
5
5
  esse (>= 0.2.4)
6
6
  rspec (>= 3)
7
7
 
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
- cluster = if Esse.config.cluster_ids.include?(cluster_and_indexes.first)
22
- Esse.cluster(cluster_and_indexes.shift)
23
- elsif cluster_and_indexes.first.is_a?(Esse::Index)
24
- cluster_and_indexes.first.cluster
25
- else
26
- Esse.cluster
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
- allow(cluster).to receive(:api).and_return(transport)
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Esse
4
4
  module RSpec
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.6"
6
6
  end
7
7
  end
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
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-15 00:00:00.000000000 Z
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: