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 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: