evil-client 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +1 -1
- data/docs/index.md +1 -1
- data/docs/rspec.md +60 -30
- data/evil-client.gemspec +2 -2
- data/lib/evil/client.rb +5 -0
- data/lib/evil/client/builder.rb +2 -0
- data/lib/evil/client/chaining.rb +2 -0
- data/lib/evil/client/container.rb +16 -2
- data/lib/evil/client/exceptions/name_error.rb +5 -7
- data/lib/evil/client/names.rb +54 -0
- data/lib/evil/client/options.rb +27 -0
- data/lib/evil/client/resolver.rb +2 -0
- data/lib/evil/client/rspec.rb +18 -120
- data/lib/evil/client/rspec/allow_stub.rb +33 -0
- data/lib/evil/client/rspec/base_stub.rb +26 -0
- data/lib/evil/client/rspec/evil_client_schema_matching.rb +21 -0
- data/lib/evil/client/rspec/expect_stub.rb +15 -0
- data/lib/evil/client/schema.rb +3 -1
- data/lib/evil/client/schema/scope.rb +2 -5
- data/lib/evil/client/settings.rb +5 -9
- data/lib/evil/client/settings/validator.rb +1 -1
- data/spec/unit/container_spec.rb +18 -0
- data/spec/unit/exceptions/name_error_spec.rb +5 -10
- data/spec/unit/options_spec.rb +20 -0
- data/spec/unit/rspec/evil_client_shema_matching_spec.rb +37 -0
- data/spec/unit/rspec/expect_client_operation_spec.rb +143 -0
- data/spec/unit/rspec/stub_client_operation_spec.rb +168 -0
- data/spec/unit/settings_spec.rb +5 -0
- metadata +18 -6
- data/spec/unit/rspec_spec.rb +0 -342
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 237e89873909ecf0ad225c9a714e68702c96b5c6
|
4
|
+
data.tar.gz: 5a030e4b7629338ebe24359311e7dac5b4d74fe8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ae5ee8377ef7ca78252356a4cad2726ef854a676b4b0715c4e6a881d7c8d381896307b48d7d8e6cbe7920362369f4886dc8f654a39bab10e2cf160cf8c6fed8
|
7
|
+
data.tar.gz: 54acb1b3f1d7e94d879240eaa6eba66576c719b5833707ef68393dcded7c971b7a1ed248e840f514d1a377a3daa6f33892caaf93a4acecffd485459e9c9f177f
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,35 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog], and this project adheres
|
5
5
|
to [Semantic Versioning].
|
6
6
|
|
7
|
+
## [1.1.0] [2017-08-10]
|
8
|
+
|
9
|
+
Some syntax sugar has been added to both the client and its RSpec helpers.
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Assigned options are wrapped into simple delegator with rails-like methods
|
14
|
+
`#slice` and `#except`. This helps when you need to select part of assigned
|
15
|
+
options for some part of a request (nepalez)
|
16
|
+
|
17
|
+
Remember the options are collected from the very root of the client,
|
18
|
+
so at the endpoint operation there could be a lot of options
|
19
|
+
related to other endpoints, or to a different part of the request.
|
20
|
+
|
21
|
+
- Every container has reference to its `#client` along the standalone `#name`
|
22
|
+
of its schema. This allows to select operation containers by
|
23
|
+
`#client`, `#name`, `#options` to stub their methods `#call` (nepalez)
|
24
|
+
|
25
|
+
- RSpec stubs and expectations for operations (nepalez, palkan)
|
26
|
+
|
27
|
+
### Removed
|
28
|
+
|
29
|
+
- RSpec matcher `perform_operation` has been dropped in favor of
|
30
|
+
`stub_client_operation` and `expect_client_operation` (nepalez)
|
31
|
+
|
32
|
+
- Unnecessary instance methods inherited from [Object] are removed
|
33
|
+
from various classes to avoid name conflicts with user-provided
|
34
|
+
scopes and operations (nepalez)
|
35
|
+
|
7
36
|
## [1.0.0] [2017-08-06]
|
8
37
|
|
9
38
|
This is a total new reincarnation of the gem. I've changed its
|
@@ -326,6 +355,7 @@ formats will be added.
|
|
326
355
|
response :not_found, 404, format: "json", raise: true
|
327
356
|
```
|
328
357
|
|
358
|
+
[1.1.0]: https://github.com/evilmartians/evil-client/compare/v1.0.0...v1.1.0
|
329
359
|
[1.0.0]: https://github.com/evilmartians/evil-client/compare/v0.3.3...v1.0.0
|
330
360
|
[0.3.3]: https://github.com/evilmartians/evil-client/compare/v0.3.2...v0.3.3
|
331
361
|
[0.3.2]: https://github.com/evilmartians/evil-client/compare/v0.3.1...v0.3.2
|
data/README.md
CHANGED
@@ -70,7 +70,7 @@ class CatsClient < Evil::Client
|
|
70
70
|
path { "cats/#{id}" } # added to root path
|
71
71
|
http_method :patch # you can use plain syntax instead of a block
|
72
72
|
format "json"
|
73
|
-
body { options.
|
73
|
+
body { options.except(:id, :version) } # [#slice] is available too
|
74
74
|
|
75
75
|
# Parses json response and wraps it into Cat instance with additional
|
76
76
|
# parameter
|
data/docs/index.md
CHANGED
@@ -68,7 +68,7 @@ class CatsClient < Evil::Client
|
|
68
68
|
path { "cats/#{id}" } # added to root path
|
69
69
|
http_method :patch # you can use plain syntax instead of a block
|
70
70
|
format "json"
|
71
|
-
body { options.
|
71
|
+
body { options.except(:id, :version) } # [#slice] is available too
|
72
72
|
|
73
73
|
# Parses json response and wraps it into Cat instance with additional
|
74
74
|
# parameter
|
data/docs/rspec.md
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
When you provide a client to remote API, you would provide some means for its users to test their operations.
|
2
2
|
|
3
|
-
Surely, they could use [webmock] to check the ultimate requests that are sent to the server. But doing this, they would inadvertedly specify not their own code, but your client's code too. What do they actually need is a means to check
|
3
|
+
Surely, they could use [webmock] to check the ultimate requests that are sent to the server. But doing this, they would inadvertedly specify not their own code, but your client's code too. What do they actually need is a means to stub and check invocations of your client's operations. This way they would back on correctness of your client, and take its interface as an endpoint.
|
4
4
|
|
5
|
-
For this reason, we support a special RSpec
|
6
|
-
|
7
|
-
The matcher isn't loaded by default, so you must require it first:
|
5
|
+
For this reason, we support a special RSpec stubs and expectations. sThey are not loaded by default, so you must require it first, and then include the module:
|
8
6
|
|
9
7
|
```ruby
|
10
8
|
require "evil/client/rspec"
|
9
|
+
|
10
|
+
RSpec.describe CatsClient, "cats.fetch" do
|
11
|
+
include Evil::Client::RSpec
|
12
|
+
# ...
|
13
|
+
end
|
11
14
|
```
|
12
15
|
|
13
16
|
Providing that you defined some client...
|
@@ -37,60 +40,87 @@ RSpec.describe CatsClient, "cats.fetch" do
|
|
37
40
|
let(:scope) { client.cats(version: 1) }
|
38
41
|
|
39
42
|
it "fetches a cat by id" do
|
40
|
-
|
41
|
-
.
|
43
|
+
stub_client_operation(CatsClient, "cats.fetch")
|
44
|
+
.with(token: "foo", version: 1, id: 8) # full hash of collected options
|
45
|
+
.to_return 8 # returned value by operation
|
46
|
+
|
47
|
+
expect(scope.fetch(id: 8)).to eq 8
|
48
|
+
expect_client_operation(CatsClient, "cats.fetch")
|
49
|
+
.to_have_been_performed
|
42
50
|
end
|
43
51
|
end
|
44
52
|
```
|
45
53
|
|
46
|
-
|
54
|
+
## Selection
|
55
|
+
|
56
|
+
To select stubbed operations you can specify client class:
|
47
57
|
|
48
|
-
|
58
|
+
```ruby
|
59
|
+
stub_client_operation(CatsClient)
|
60
|
+
```
|
49
61
|
|
50
|
-
|
62
|
+
or its superclass
|
51
63
|
|
52
64
|
```ruby
|
53
|
-
|
54
|
-
.to perform_operation("CatsClient.client.fetch")
|
55
|
-
.with token: "foo"
|
65
|
+
stub_client_operation(Evil::Client)
|
56
66
|
```
|
57
67
|
|
58
|
-
|
68
|
+
or leave it for default `Evil::Client`:
|
59
69
|
|
60
|
-
|
70
|
+
```ruby
|
71
|
+
stub_client_operation()
|
72
|
+
```
|
73
|
+
|
74
|
+
or add a fully qualified name of the operation (for **exact** matching):
|
61
75
|
|
62
76
|
```ruby
|
63
|
-
|
64
|
-
.to perform_operation("CatsClient.client.fetch")
|
65
|
-
.with_exactly token: "foo", version: 1, id: 8
|
77
|
+
stub_client_operation(CatsClient, "cats.fetch")
|
66
78
|
```
|
67
79
|
|
68
|
-
|
80
|
+
or regexp for partial matching:
|
69
81
|
|
70
|
-
|
82
|
+
```ruby
|
83
|
+
stub_client_operation(CatsClient, /fetch/)
|
84
|
+
```
|
85
|
+
|
86
|
+
or use method `with` to check options exactly:
|
71
87
|
|
72
88
|
```ruby
|
73
|
-
|
74
|
-
.to perform_operation("CatsClient.client.fetch")
|
75
|
-
.without :name, :email
|
89
|
+
stub_client_operation(CatsClient, "cats.fetch").with(token: "foo", version: 1, id: 8)
|
76
90
|
```
|
77
91
|
|
78
|
-
|
92
|
+
or partially:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
stub_client_operation(CatsClient, "cats.fetch").with(hash_including(id: 8))
|
96
|
+
```
|
79
97
|
|
80
|
-
|
98
|
+
or via block:
|
81
99
|
|
82
100
|
```ruby
|
83
|
-
|
84
|
-
.not_to perform_operation("CatsClient.client.fetch")
|
85
|
-
.with token: "foo"
|
101
|
+
stub_client_operation(CatsClient, "cats.fetch").with { |opts| opts[:id] == 8 }
|
86
102
|
```
|
87
103
|
|
88
|
-
|
104
|
+
## Return value
|
105
|
+
|
106
|
+
You **must** define some value returned by a stub:
|
89
107
|
|
90
108
|
```ruby
|
91
|
-
|
109
|
+
stub_client_operation(CatsClient, "cats.fetch").to_return(8)
|
110
|
+
```
|
111
|
+
|
112
|
+
or fall back to original implementation:
|
92
113
|
|
93
|
-
|
114
|
+
```ruby
|
115
|
+
stub_client_operation(CatsClient, "cats.fetch").to_call_original
|
116
|
+
```
|
117
|
+
|
118
|
+
or raise an exception:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
stub_client_operation(CatsClient, "cats.fetch").to_raise StandardError, "Wrong id"
|
94
122
|
```
|
95
123
|
|
124
|
+
## Some Hint
|
125
|
+
|
96
126
|
[webmock]: https://github.com/bblimke/webmock
|
data/evil-client.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = "evil-client"
|
3
|
-
gem.version = "1.
|
3
|
+
gem.version = "1.1.0"
|
4
4
|
gem.author = ["Andrew Kozin (nepalez)", "Ravil Bairamgalin (brainopia)"]
|
5
5
|
gem.email = ["andrew.kozin@gmail.com", "nepalez@evilmartians.com"]
|
6
6
|
gem.homepage = "https://github.com/evilmartians/evil-client"
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.add_runtime_dependency "dry-initializer", "~> 1.4"
|
17
17
|
gem.add_runtime_dependency "i18n", "~> 0.8.6"
|
18
18
|
gem.add_runtime_dependency "mime-types", "~> 3.1"
|
19
|
-
gem.add_runtime_dependency "rack", "~>
|
19
|
+
gem.add_runtime_dependency "rack", "~> 2"
|
20
20
|
|
21
21
|
gem.add_development_dependency "rake", ">= 10"
|
22
22
|
gem.add_development_dependency "rspec", "~> 3.0"
|
data/lib/evil/client.rb
CHANGED
@@ -7,6 +7,7 @@ require "yaml"
|
|
7
7
|
require "i18n"
|
8
8
|
require "mime-types"
|
9
9
|
require "securerandom"
|
10
|
+
require "delegate"
|
10
11
|
require "dry-initializer"
|
11
12
|
require "net/http"
|
12
13
|
require "net/https"
|
@@ -18,6 +19,9 @@ module Evil
|
|
18
19
|
# Absctract base class for clients to remote APIs
|
19
20
|
#
|
20
21
|
class Client
|
22
|
+
require_relative "client/names"
|
23
|
+
Names.clean(self) # Remove unnecessary methods from the instance
|
24
|
+
|
21
25
|
require_relative "client/exceptions/definition_error"
|
22
26
|
require_relative "client/exceptions/name_error"
|
23
27
|
require_relative "client/exceptions/response_error"
|
@@ -25,6 +29,7 @@ module Evil
|
|
25
29
|
require_relative "client/exceptions/validation_error"
|
26
30
|
|
27
31
|
require_relative "client/chaining"
|
32
|
+
require_relative "client/options"
|
28
33
|
require_relative "client/settings"
|
29
34
|
require_relative "client/schema"
|
30
35
|
require_relative "client/container"
|
data/lib/evil/client/builder.rb
CHANGED
@@ -9,6 +9,8 @@ class Evil::Client
|
|
9
9
|
# for scope/operation instance whose options reload the [#parent]'s ones.
|
10
10
|
#
|
11
11
|
class Builder
|
12
|
+
Names.clean(self) # Remove unnecessary methods from the instance
|
13
|
+
|
12
14
|
# Load concrete implementations for the abstact builder
|
13
15
|
require_relative "builder/scope"
|
14
16
|
require_relative "builder/operation"
|
data/lib/evil/client/chaining.rb
CHANGED
@@ -5,6 +5,8 @@ class Evil::Client
|
|
5
5
|
# and methods to build sub-scope/operation or perform the current operation.
|
6
6
|
#
|
7
7
|
class Container
|
8
|
+
Names.clean(self) # Remove unnecessary methods from the instance
|
9
|
+
|
8
10
|
# Loads concrete implementations of the abstract container
|
9
11
|
require_relative "container/scope"
|
10
12
|
require_relative "container/operation"
|
@@ -17,6 +19,18 @@ class Evil::Client
|
|
17
19
|
# @return [Evil::Client::Settings]
|
18
20
|
attr_reader :settings
|
19
21
|
|
22
|
+
# The client of the [#schema]
|
23
|
+
# @return [Class]
|
24
|
+
def client
|
25
|
+
schema.client
|
26
|
+
end
|
27
|
+
|
28
|
+
# The name of the current schema
|
29
|
+
# @return [String]
|
30
|
+
def name
|
31
|
+
schema.to_s
|
32
|
+
end
|
33
|
+
|
20
34
|
# Options assigned to the [#settings]
|
21
35
|
#
|
22
36
|
# These are opts given to the [#initializer],
|
@@ -24,7 +38,7 @@ class Evil::Client
|
|
24
38
|
#
|
25
39
|
# @return [Hash<Symbol, Object>]
|
26
40
|
def options
|
27
|
-
|
41
|
+
settings.options
|
28
42
|
end
|
29
43
|
|
30
44
|
# The human-friendly representation of the scope instance
|
@@ -34,7 +48,7 @@ class Evil::Client
|
|
34
48
|
#
|
35
49
|
# @return [String]
|
36
50
|
def to_s
|
37
|
-
"#<#{
|
51
|
+
"#<#{name} #{options.map { |key, val| "@#{key}=#{val}" }.join(', ')}>"
|
38
52
|
end
|
39
53
|
alias_method :to_str, :to_s
|
40
54
|
alias_method :inspect, :to_s
|
@@ -10,23 +10,21 @@ class Evil::Client
|
|
10
10
|
# @return [Symbol] if name is valid
|
11
11
|
# @raise [self] if name isn't valid
|
12
12
|
#
|
13
|
-
def self.check!(name
|
13
|
+
def self.check!(name)
|
14
14
|
name = name.to_sym
|
15
|
-
return name if name[FORMAT] && !
|
16
|
-
raise new(name
|
15
|
+
return name if name[Names::FORMAT] && !Names::FORBIDDEN.include?(name)
|
16
|
+
raise new(name)
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def initialize(name
|
21
|
+
def initialize(name)
|
22
22
|
super "Invalid name :#{name}." \
|
23
23
|
" It should contain latin letters in the lower case, digits," \
|
24
24
|
" and underscores only; have minimum 2 chars;" \
|
25
25
|
" start from a letter; end with either letter or digit." \
|
26
|
-
" The following names: '#{
|
26
|
+
" The following names: '#{Names::FORBIDDEN.join("', '")}'" \
|
27
27
|
" are already used by Evil::Client."
|
28
28
|
end
|
29
|
-
|
30
|
-
FORMAT = /^[a-z]([a-z\d_])*[a-z\d]$/
|
31
29
|
end
|
32
30
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Evil::Client
|
2
|
+
#
|
3
|
+
# Utility to remove unnecessary methods from instances
|
4
|
+
# to clear a namespace
|
5
|
+
# @private
|
6
|
+
#
|
7
|
+
module Names
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# Removes unused instance methods inherited from [Object] from given class
|
11
|
+
#
|
12
|
+
# @param [Class] klass
|
13
|
+
# @return [nil]
|
14
|
+
#
|
15
|
+
def clean(klass)
|
16
|
+
(klass.instance_methods - BasicObject.instance_methods - FORBIDDEN)
|
17
|
+
.each { |m| klass.send(:undef_method, m) if m[FORMAT] } && nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# List of preserved methods.
|
21
|
+
# They also couldn't be used as names of operations/scopes/options
|
22
|
+
# to avoid name conflicts.
|
23
|
+
FORBIDDEN = %i[
|
24
|
+
basic_auth
|
25
|
+
class
|
26
|
+
datetime
|
27
|
+
hash
|
28
|
+
inspect
|
29
|
+
instance_exec
|
30
|
+
instance_variable_get
|
31
|
+
instance_variable_set
|
32
|
+
key_auth
|
33
|
+
logger
|
34
|
+
logger
|
35
|
+
object_id
|
36
|
+
operation
|
37
|
+
operations
|
38
|
+
options
|
39
|
+
schema
|
40
|
+
scope
|
41
|
+
scopes
|
42
|
+
self
|
43
|
+
send
|
44
|
+
settings
|
45
|
+
singleton_class
|
46
|
+
to_s
|
47
|
+
to_str
|
48
|
+
token_auth
|
49
|
+
].freeze
|
50
|
+
|
51
|
+
# Matches whether a name can be used in operations/scopes/options
|
52
|
+
FORMAT = /^[a-z]([a-z\d_])*[a-z\d]$/
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Evil::Client
|
2
|
+
# Wraps hash of options with railsy methods [#slice] and [#except]
|
3
|
+
#
|
4
|
+
# Both methods works on the root level only.
|
5
|
+
# Nevertheless, this is sufficient to select/reject a part of the whole
|
6
|
+
# options collected from the very root of the client.
|
7
|
+
#
|
8
|
+
class Options < SimpleDelegator
|
9
|
+
# Returns a new hash which include only selected keys
|
10
|
+
#
|
11
|
+
# @param [Object, Array<Object>] keys
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
def slice(*keys)
|
15
|
+
select { |key| keys.flatten.include? key }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a new hash where some keys are excluded from
|
19
|
+
#
|
20
|
+
# @param [Object, Array<Object>] keys
|
21
|
+
# @return [Hash]
|
22
|
+
#
|
23
|
+
def except(*keys)
|
24
|
+
reject { |key| keys.flatten.include? key }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/evil/client/resolver.rb
CHANGED
@@ -7,6 +7,8 @@ class Evil::Client
|
|
7
7
|
# (request, middleware or response).
|
8
8
|
#
|
9
9
|
class Resolver
|
10
|
+
Names.clean(self) # Remove unnecessary methods from the instance
|
11
|
+
|
10
12
|
# Loads concrete implementation of the abstract resolver
|
11
13
|
require_relative "resolver/request"
|
12
14
|
require_relative "resolver/middleware"
|
data/lib/evil/client/rspec.rb
CHANGED
@@ -1,127 +1,25 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# expect { subject }
|
14
|
-
# .to perform_operation("MyClient.users.fetch")
|
15
|
-
# .with_exactly(token: "bar", version: 3, id: 42)
|
16
|
-
#
|
17
|
-
# # partial matcher
|
18
|
-
# expect { subject }
|
19
|
-
# .to perform_operation("MyClient.users.fetch")
|
20
|
-
# .with(token: "bar", version: 3)
|
21
|
-
#
|
22
|
-
# # absence matcher
|
23
|
-
# expect { subject }
|
24
|
-
# .to perform_operation("MyClient.users.fetch")
|
25
|
-
# .without(:user, :password)
|
26
|
-
#
|
27
|
-
# # block syntax
|
28
|
-
# expect { subject }
|
29
|
-
# .to perform_operation("MyClient.users.fetch")
|
30
|
-
# .with { |token:, **| expect(token).to eq "bar" }
|
31
|
-
#
|
32
|
-
# @param [String] name The full name of the operation
|
33
|
-
#
|
34
|
-
RSpec::Matchers.define :perform_operation do |name|
|
35
|
-
supports_block_expectations
|
36
|
-
|
37
|
-
description { "perform operation #{name} " }
|
38
|
-
|
39
|
-
chain :with do |**options|
|
40
|
-
@some_options = options
|
41
|
-
end
|
42
|
-
|
43
|
-
chain :with_exactly do |**options|
|
44
|
-
@exact_options = options
|
45
|
-
end
|
46
|
-
|
47
|
-
chain :without do |*options|
|
48
|
-
@no_options = options.flatten.map(&:to_sym)
|
49
|
-
end
|
50
|
-
|
51
|
-
def full_signature(name)
|
52
|
-
name.dup.tap do |text|
|
53
|
-
text << " with options #{@exact_options}" if @exact_options
|
54
|
-
text << " with options including #{@some_options}" if @some_options
|
55
|
-
text << " without options :#{@no_options.join(', :')}" if @no_options
|
1
|
+
class Evil::Client
|
2
|
+
#
|
3
|
+
# Collection of RSpec-related definitions
|
4
|
+
#
|
5
|
+
module RSpec
|
6
|
+
require_relative "rspec/evil_client_schema_matching"
|
7
|
+
require_relative "rspec/base_stub"
|
8
|
+
require_relative "rspec/allow_stub"
|
9
|
+
require_relative "rspec/expect_stub"
|
10
|
+
|
11
|
+
def stub_client_operation(klass = Evil::Client, name = nil)
|
12
|
+
AllowStub.new(klass, name)
|
56
13
|
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
60
|
-
# rubocop: disable Style/InverseMethods
|
61
|
-
def expected_options?(options, check)
|
62
|
-
return if @exact_options && options != @exact_options
|
63
|
-
return if @some_options && !(options >= @some_options)
|
64
|
-
return if (options.keys & @no_options.to_a).any?
|
65
|
-
check.nil? || check.call(options)
|
66
|
-
end
|
67
|
-
# rubocop: enable Metrics/CyclomaticComplexity
|
68
|
-
# rubocop: enable Style/InverseMethods
|
69
|
-
|
70
|
-
def stub_resolver
|
71
|
-
resolver = Evil::Client::Resolver::Request
|
72
14
|
|
73
|
-
|
74
|
-
|
75
|
-
resolver.new(schema, settings).send(:__call__)
|
15
|
+
def expect_client_operation(klass, name = nil)
|
16
|
+
ExpectStub.new(klass, name)
|
76
17
|
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def actual_operations
|
80
|
-
@actual_operations ||= []
|
81
|
-
end
|
82
|
-
|
83
|
-
def register(schema, settings)
|
84
|
-
actual_operations << [schema.to_s, settings.options]
|
85
|
-
end
|
86
|
-
|
87
|
-
def performed(name, check)
|
88
|
-
@performed ||= actual_operations.find do |(key, options)|
|
89
|
-
(key == name) && expected_options?(options, check)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
match do |block|
|
94
|
-
stub_resolver
|
95
|
-
block.call
|
96
|
-
!performed(name, block_arg).nil?
|
97
|
-
end
|
98
|
-
|
99
|
-
match_when_negated do |block|
|
100
|
-
stub_resolver
|
101
|
-
block.call
|
102
|
-
performed(name, block_arg).nil?
|
103
|
-
end
|
104
|
-
|
105
|
-
def describe_expectations(name, perform)
|
106
|
-
"It was expected the operation #{full_signature(name)}" \
|
107
|
-
" #{'NOT ' unless perform}to be performed.\n" \
|
108
|
-
"The following operations has been actually performed:"
|
109
|
-
end
|
110
|
-
|
111
|
-
failure_message do
|
112
|
-
text = describe_expectations(name, true)
|
113
|
-
actual_operations.each.with_index(1) do |(key, opts), index|
|
114
|
-
text << format("\n %02d) #{key} with #{opts}", index)
|
115
|
-
end
|
116
|
-
text
|
117
|
-
end
|
118
18
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
text << format("\n#{marker} % 2d) #{key} with #{opts}", index)
|
19
|
+
def unstub_all
|
20
|
+
allow(Evil::Client::Container::Operation)
|
21
|
+
.to receive(:new)
|
22
|
+
.and_call_original
|
124
23
|
end
|
125
|
-
text
|
126
24
|
end
|
127
25
|
end
|