evil-client 0.2.1
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 +7 -0
- data/.codeclimate.yml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.rubocop.yml +98 -0
- data/.travis.yml +17 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +144 -0
- data/Rakefile +6 -0
- data/docs/base_url.md +38 -0
- data/docs/documentation.md +9 -0
- data/docs/headers.md +59 -0
- data/docs/http_method.md +31 -0
- data/docs/index.md +127 -0
- data/docs/license.md +19 -0
- data/docs/model.md +173 -0
- data/docs/operation.md +0 -0
- data/docs/overview.md +0 -0
- data/docs/path.md +48 -0
- data/docs/query.md +99 -0
- data/docs/responses.md +66 -0
- data/docs/security.md +102 -0
- data/docs/settings.md +32 -0
- data/evil-client.gemspec +25 -0
- data/lib/evil/client.rb +97 -0
- data/lib/evil/client/connection.rb +35 -0
- data/lib/evil/client/connection/net_http.rb +57 -0
- data/lib/evil/client/dsl.rb +110 -0
- data/lib/evil/client/dsl/files.rb +37 -0
- data/lib/evil/client/dsl/operation.rb +102 -0
- data/lib/evil/client/dsl/operations.rb +41 -0
- data/lib/evil/client/dsl/scope.rb +34 -0
- data/lib/evil/client/dsl/security.rb +57 -0
- data/lib/evil/client/middleware.rb +81 -0
- data/lib/evil/client/middleware/base.rb +15 -0
- data/lib/evil/client/middleware/merge_security.rb +16 -0
- data/lib/evil/client/middleware/normalize_headers.rb +13 -0
- data/lib/evil/client/middleware/stringify_form.rb +36 -0
- data/lib/evil/client/middleware/stringify_json.rb +15 -0
- data/lib/evil/client/middleware/stringify_multipart.rb +32 -0
- data/lib/evil/client/middleware/stringify_multipart/part.rb +36 -0
- data/lib/evil/client/middleware/stringify_query.rb +31 -0
- data/lib/evil/client/model.rb +65 -0
- data/lib/evil/client/operation.rb +34 -0
- data/lib/evil/client/operation/request.rb +42 -0
- data/lib/evil/client/operation/response.rb +40 -0
- data/lib/evil/client/operation/response_error.rb +12 -0
- data/lib/evil/client/operation/unexpected_response_error.rb +16 -0
- data/mkdocs.yml +21 -0
- data/spec/features/instantiation_spec.rb +68 -0
- data/spec/features/middleware_spec.rb +75 -0
- data/spec/features/operation_with_documentation_spec.rb +41 -0
- data/spec/features/operation_with_files_spec.rb +40 -0
- data/spec/features/operation_with_form_body_spec.rb +158 -0
- data/spec/features/operation_with_headers_spec.rb +99 -0
- data/spec/features/operation_with_http_method_spec.rb +45 -0
- data/spec/features/operation_with_json_body_spec.rb +156 -0
- data/spec/features/operation_with_path_spec.rb +47 -0
- data/spec/features/operation_with_query_spec.rb +84 -0
- data/spec/features/operation_with_response_spec.rb +109 -0
- data/spec/features/operation_with_security_spec.rb +228 -0
- data/spec/features/scoping_spec.rb +48 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/test_client.rb +15 -0
- data/spec/unit/evil/client/connection/net_http_spec.rb +38 -0
- data/spec/unit/evil/client/dsl/files_spec.rb +37 -0
- data/spec/unit/evil/client/dsl/operation_spec.rb +233 -0
- data/spec/unit/evil/client/dsl/operations_spec.rb +27 -0
- data/spec/unit/evil/client/dsl/scope_spec.rb +30 -0
- data/spec/unit/evil/client/dsl/security_spec.rb +135 -0
- data/spec/unit/evil/client/dsl_spec.rb +57 -0
- data/spec/unit/evil/client/middleware/merge_security_spec.rb +32 -0
- data/spec/unit/evil/client/middleware/normalize_headers_spec.rb +17 -0
- data/spec/unit/evil/client/middleware/stringify_form_spec.rb +63 -0
- data/spec/unit/evil/client/middleware/stringify_json_spec.rb +61 -0
- data/spec/unit/evil/client/middleware/stringify_multipart/part_spec.rb +59 -0
- data/spec/unit/evil/client/middleware/stringify_multipart_spec.rb +62 -0
- data/spec/unit/evil/client/middleware/stringify_query_spec.rb +40 -0
- data/spec/unit/evil/client/middleware_spec.rb +46 -0
- data/spec/unit/evil/client/model_spec.rb +100 -0
- data/spec/unit/evil/client/operation/request_spec.rb +49 -0
- data/spec/unit/evil/client/operation/response_spec.rb +61 -0
- metadata +271 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
RSpec.describe "operation with documentation" do
|
2
|
+
# see Test::Client definition in `/spec/support/test_client.rb`
|
3
|
+
before do
|
4
|
+
class Test::Client < Evil::Client
|
5
|
+
operation do |settings|
|
6
|
+
documentation "https://docs.example.com/v#{settings.version}/index.html"
|
7
|
+
http_method :get
|
8
|
+
path { "data" }
|
9
|
+
end
|
10
|
+
|
11
|
+
operation :clear_data
|
12
|
+
operation :find_data do |settings|
|
13
|
+
documentation "https://docs.example.com/v#{settings.version}/findData"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
stub_request(:any, //)
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:client) { Test::Client.new("foo", version: 3, user: "bar") }
|
21
|
+
|
22
|
+
it "displays default documentation in exception messages" do
|
23
|
+
begin
|
24
|
+
client.operations[:clear_data].call
|
25
|
+
rescue => error
|
26
|
+
expect(error.message).to include "https://docs.example.com/v3/index.html"
|
27
|
+
else
|
28
|
+
fail
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "reloads default value with operation-specific one" do
|
33
|
+
begin
|
34
|
+
client.operations[:find_data].call
|
35
|
+
rescue => error
|
36
|
+
expect(error.message).to include "https://docs.example.com/v3/findData"
|
37
|
+
else
|
38
|
+
fail
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
RSpec.describe "operation with files" do
|
2
|
+
# see Test::Client definition in `/spec/support/test_client.rb`
|
3
|
+
before do
|
4
|
+
class Test::Client < Evil::Client
|
5
|
+
operation :example do
|
6
|
+
http_method :get
|
7
|
+
path { "users" }
|
8
|
+
response 200
|
9
|
+
|
10
|
+
files do |file:, **|
|
11
|
+
add file, type: "text/xml", charset: "utf-16", filename: "foo.xml"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
stub_request(:get, //)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:path) { "https://foo.example.com/api/v3/users" }
|
20
|
+
let(:client) { Test::Client.new "foo", user: "bar", version: 3, token: "baz" }
|
21
|
+
|
22
|
+
subject { client.operations[:example].call file: "Hi!" }
|
23
|
+
|
24
|
+
it "builds a multipart body" do
|
25
|
+
request = a_request(:get, path).with do |req|
|
26
|
+
expect(req.body).to include "Content-Disposition: form-data;" \
|
27
|
+
' name="AttachedFile1"; filename="foo.xml"'
|
28
|
+
|
29
|
+
expect(req.body).to include "Content-Type: text/xml; charset=utf-16"
|
30
|
+
|
31
|
+
expect(req.body).to include "Hi!"
|
32
|
+
|
33
|
+
expect(req.headers["Content-Type"]).to include "multipart/form-data"
|
34
|
+
end
|
35
|
+
|
36
|
+
subject
|
37
|
+
|
38
|
+
expect(request).to have_been_made
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
RSpec.describe "operation with form body" do
|
2
|
+
# see Test::Client definition in `/spec/support/test_client.rb`
|
3
|
+
before do
|
4
|
+
class Test::User < Evil::Client::Model
|
5
|
+
attribute :name
|
6
|
+
end
|
7
|
+
|
8
|
+
class Test::Client < Evil::Client
|
9
|
+
operation do
|
10
|
+
http_method :get
|
11
|
+
path { "users" }
|
12
|
+
response 200
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
stub_request(:get, //)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:path) { "https://foo.example.com/api/v3/users" }
|
20
|
+
let(:client) { Test::Client.new "foo", user: "bar", version: 3, token: "baz" }
|
21
|
+
let(:operation) { client.operations[:example] }
|
22
|
+
|
23
|
+
context "without operation-specific definition" do
|
24
|
+
before do
|
25
|
+
class Test::Client < Evil::Client
|
26
|
+
operation do
|
27
|
+
http_method :get
|
28
|
+
path { "users" }
|
29
|
+
response 200
|
30
|
+
|
31
|
+
body format: "form" do
|
32
|
+
attribute :foo
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
operation :example do
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "uses the default one" do
|
42
|
+
request = a_request(:get, path).with do |req|
|
43
|
+
expect(req.body).to eq "foo[][bar][]=BAZ"
|
44
|
+
expect(req.headers)
|
45
|
+
.to include "Content-Type" => "application/x-www-form-urlencoded"
|
46
|
+
end
|
47
|
+
|
48
|
+
operation.call foo: [{ bar: [:BAZ] }], baz: :QUX
|
49
|
+
|
50
|
+
expect(request).to have_been_made
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with operation-specific definition" do
|
55
|
+
before do
|
56
|
+
class Test::Client < Evil::Client
|
57
|
+
operation do
|
58
|
+
http_method :get
|
59
|
+
path { "users" }
|
60
|
+
response 200
|
61
|
+
|
62
|
+
body format: "form" do
|
63
|
+
attribute :foo
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
operation :example do
|
68
|
+
body format: "form" do
|
69
|
+
attribute :baz
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "uses the specific one" do
|
76
|
+
request = a_request(:get, path).with body: "baz=QUX"
|
77
|
+
|
78
|
+
operation.call foo: [{ bar: [:BAZ] }], baz: :QUX
|
79
|
+
|
80
|
+
expect(request).to have_been_made
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when appended with files" do
|
85
|
+
before do
|
86
|
+
class Test::Client < Evil::Client
|
87
|
+
operation :example do
|
88
|
+
body format: "form" do
|
89
|
+
attribute :baz
|
90
|
+
end
|
91
|
+
|
92
|
+
files do
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "skips body definition" do
|
99
|
+
request = a_request(:get, path).with do |req|
|
100
|
+
expect(req.body).to be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
operation.call foo: [{ bar: [:BAZ] }], baz: :QUX
|
104
|
+
|
105
|
+
expect(request).to have_been_made
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "with a model" do
|
110
|
+
before do
|
111
|
+
class Test::Client < Evil::Client
|
112
|
+
operation :example do
|
113
|
+
body format: "form", model: Test::User do
|
114
|
+
attribute :foo
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "extends the model" do
|
121
|
+
request = a_request(:get, path).with body: "name=Andy&foo=BAR"
|
122
|
+
|
123
|
+
operation.call foo: :BAR, name: "Andy"
|
124
|
+
|
125
|
+
expect(request).to have_been_made
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "with a model" do
|
130
|
+
before do
|
131
|
+
class Test::Client < Evil::Client
|
132
|
+
operation :example do
|
133
|
+
body format: "form" do
|
134
|
+
attribute :foo, type: Dry::Types["strict.string"]
|
135
|
+
attribute :bar, default: proc { 1 }
|
136
|
+
attribute :baz, optional: true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "requires mandatory arguments" do
|
143
|
+
expect { operation.call bar: 2, baz: 3 }.to raise_error(ArgumentError)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "applies type restrictions" do
|
147
|
+
expect { operation.call foo: :FOO }.to raise_error(TypeError)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "uses default values and skips undefined optional attributes" do
|
151
|
+
request = a_request(:get, path).with body: "foo=BAR&bar=1"
|
152
|
+
|
153
|
+
operation.call foo: "BAR"
|
154
|
+
|
155
|
+
expect(request).to have_been_made
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
RSpec.describe "operation with headers" do
|
2
|
+
# see Test::Client definition in `/spec/support/test_client.rb`
|
3
|
+
before do
|
4
|
+
class Test::Client < Evil::Client
|
5
|
+
operation do
|
6
|
+
http_method :get
|
7
|
+
path { "data" }
|
8
|
+
|
9
|
+
headers do
|
10
|
+
attribute :foo
|
11
|
+
attribute :bar, type: Dry::Types["strict.string"]
|
12
|
+
attribute :baz, optional: true
|
13
|
+
end
|
14
|
+
|
15
|
+
response 200
|
16
|
+
end
|
17
|
+
|
18
|
+
operation :clear_data do
|
19
|
+
end
|
20
|
+
|
21
|
+
operation :get_data do
|
22
|
+
headers
|
23
|
+
end
|
24
|
+
|
25
|
+
operation :find_data do |settings|
|
26
|
+
headers do
|
27
|
+
attribute :foo if settings.version < 2
|
28
|
+
attribute :bar, type: Dry::Types["coercible.int"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
stub_request(:any, //)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:path) { "https://foo.example.com/api/v3/data" }
|
37
|
+
let(:client) { Test::Client.new("foo", version: 3, user: "bar") }
|
38
|
+
|
39
|
+
it "uses default value" do
|
40
|
+
client.operations[:clear_data].call foo: "FOO", bar: "BAR", baz: "BAZ"
|
41
|
+
|
42
|
+
request = a_request(:get, path) do |req|
|
43
|
+
expect(req.headers).to include "Foo" => "FOO",
|
44
|
+
"Bar" => "BAR",
|
45
|
+
"Baz" => "BAZ"
|
46
|
+
end
|
47
|
+
|
48
|
+
expect(request).to have_been_made
|
49
|
+
end
|
50
|
+
|
51
|
+
it "ingnores unknown attributes" do
|
52
|
+
client.operations[:clear_data]
|
53
|
+
.call foo: "FOO", bar: "BAR", baz: "BAZ", qux: "QUX"
|
54
|
+
|
55
|
+
request = a_request(:get, path).with do |req|
|
56
|
+
expect(req.headers.keys).not_to include "Qux"
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(request).to have_been_made
|
60
|
+
end
|
61
|
+
|
62
|
+
it "requires mandatory headers" do
|
63
|
+
expect { client.operations[:clear_data].call bar: "BAR", baz: "BAZ" }
|
64
|
+
.to raise_error(ArgumentError)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "applies type constraints" do
|
68
|
+
expect { client.operations[:clear_data].call foo: "FOO", bar: :BAR }
|
69
|
+
.to raise_error(TypeError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "skips optional headers" do
|
73
|
+
client.operations[:clear_data].call foo: "FOO", bar: "BAR"
|
74
|
+
|
75
|
+
request = a_request(:get, path).with do |req|
|
76
|
+
expect(req.headers.keys).not_to include "Baz"
|
77
|
+
end
|
78
|
+
|
79
|
+
expect(request).to have_been_made
|
80
|
+
end
|
81
|
+
|
82
|
+
it "can undefine default headers" do
|
83
|
+
client.operations[:get_data].call
|
84
|
+
|
85
|
+
expect(a_request(:get, path)).to have_been_made
|
86
|
+
end
|
87
|
+
|
88
|
+
it "can reload default headers for a specific operation" do
|
89
|
+
client.operations[:find_data].call foo: "FOO", bar: "01", baz: "BAZ"
|
90
|
+
|
91
|
+
request = a_request(:get, path).with do |req|
|
92
|
+
expect(req.headers).to include "Bar" => "1"
|
93
|
+
expect(req.headers.keys).not_to include "Foo"
|
94
|
+
expect(req.headers.keys).not_to include "Baz"
|
95
|
+
end
|
96
|
+
|
97
|
+
expect(request).to have_been_made
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
RSpec.describe "operation with http_method" do
|
2
|
+
# see Test::Client definition in `/spec/support/test_client.rb`
|
3
|
+
before do
|
4
|
+
class Test::Client < Evil::Client
|
5
|
+
operation do |settings|
|
6
|
+
http_method settings.version > 1 ? :post : :get
|
7
|
+
path { "data" }
|
8
|
+
response 200
|
9
|
+
end
|
10
|
+
|
11
|
+
operation :clear_data do
|
12
|
+
http_method :delete
|
13
|
+
end
|
14
|
+
|
15
|
+
operation :find_data
|
16
|
+
|
17
|
+
operation :reset_data do |settings|
|
18
|
+
http_method settings.version > 2 ? :patch : :put
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
stub_request(:any, //)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:path) { "https://foo.example.com/api/v3/data" }
|
26
|
+
let(:client) { Test::Client.new("foo", version: 3, user: "bar") }
|
27
|
+
|
28
|
+
it "uses default value" do
|
29
|
+
client.operations[:find_data].call
|
30
|
+
|
31
|
+
expect(a_request(:post, path)).to have_been_made
|
32
|
+
end
|
33
|
+
|
34
|
+
it "reloads default value with operation-specific one" do
|
35
|
+
client.operations[:clear_data].call
|
36
|
+
|
37
|
+
expect(a_request(:delete, path)).to have_been_made
|
38
|
+
end
|
39
|
+
|
40
|
+
it "is customizeable by settings" do
|
41
|
+
client.operations[:reset_data].call
|
42
|
+
|
43
|
+
expect(a_request(:patch, path)).to have_been_made
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
RSpec.describe "operation with json body" do
|
2
|
+
# see Test::Client definition in `/spec/support/test_client.rb`
|
3
|
+
before do
|
4
|
+
class Test::User < Evil::Client::Model
|
5
|
+
attribute :name
|
6
|
+
end
|
7
|
+
|
8
|
+
class Test::Client < Evil::Client
|
9
|
+
operation do
|
10
|
+
http_method :get
|
11
|
+
path { "users" }
|
12
|
+
response 200
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
stub_request(:get, //)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:path) { "https://foo.example.com/api/v3/users" }
|
20
|
+
let(:client) { Test::Client.new "foo", user: "bar", version: 3, token: "baz" }
|
21
|
+
let(:operation) { client.operations[:example] }
|
22
|
+
|
23
|
+
context "without operation-specific definition" do
|
24
|
+
before do
|
25
|
+
class Test::Client < Evil::Client
|
26
|
+
operation do
|
27
|
+
http_method :get
|
28
|
+
path { "users" }
|
29
|
+
response 200
|
30
|
+
|
31
|
+
body do
|
32
|
+
attribute :foo
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
operation :example
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "uses the default one" do
|
41
|
+
request = a_request(:get, path).with do |req|
|
42
|
+
expect(req.body).to eq '{"foo":[{"bar":["BAZ"]}]}'
|
43
|
+
expect(req.headers).to include "Content-Type" => "application/json"
|
44
|
+
end
|
45
|
+
|
46
|
+
operation.call foo: [{ bar: [:BAZ] }], baz: :QUX
|
47
|
+
|
48
|
+
expect(request).to have_been_made
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with operation-specific definition" do
|
53
|
+
before do
|
54
|
+
class Test::Client < Evil::Client
|
55
|
+
operation do
|
56
|
+
http_method :get
|
57
|
+
path { "users" }
|
58
|
+
response 200
|
59
|
+
|
60
|
+
body do
|
61
|
+
attribute :foo
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
operation :example do
|
66
|
+
body do
|
67
|
+
attribute :baz
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "uses the specific one" do
|
74
|
+
request = a_request(:get, path).with body: '{"baz":"QUX"}'
|
75
|
+
|
76
|
+
operation.call foo: [{ bar: [:BAZ] }], baz: :QUX
|
77
|
+
|
78
|
+
expect(request).to have_been_made
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when appended with files" do
|
83
|
+
before do
|
84
|
+
class Test::Client < Evil::Client
|
85
|
+
operation :example do
|
86
|
+
body do
|
87
|
+
attribute :baz
|
88
|
+
end
|
89
|
+
|
90
|
+
files do
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "skips body definition" do
|
97
|
+
request = a_request(:get, path).with do |req|
|
98
|
+
expect(req.body).to be_nil
|
99
|
+
end
|
100
|
+
|
101
|
+
operation.call foo: [{ bar: [:BAZ] }], baz: :QUX
|
102
|
+
|
103
|
+
expect(request).to have_been_made
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "with a model" do
|
108
|
+
before do
|
109
|
+
class Test::Client < Evil::Client
|
110
|
+
operation :example do
|
111
|
+
body model: Test::User do
|
112
|
+
attribute :foo
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "extends the model" do
|
119
|
+
request = a_request(:get, path).with body: '{"name":"Andy","foo":"BAR"}'
|
120
|
+
|
121
|
+
operation.call foo: :BAR, name: "Andy"
|
122
|
+
|
123
|
+
expect(request).to have_been_made
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "with a model" do
|
128
|
+
before do
|
129
|
+
class Test::Client < Evil::Client
|
130
|
+
operation :example do
|
131
|
+
body do
|
132
|
+
attribute :foo, type: Dry::Types["strict.string"]
|
133
|
+
attribute :bar, default: proc { 1 }
|
134
|
+
attribute :baz, optional: true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "requires mandatory arguments" do
|
141
|
+
expect { operation.call bar: 2, baz: 3 }.to raise_error(ArgumentError)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "applies type restrictions" do
|
145
|
+
expect { operation.call foo: :FOO }.to raise_error(TypeError)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "uses default values and skips undefined optional attributes" do
|
149
|
+
request = a_request(:get, path).with body: '{"foo":"BAR","bar":1}'
|
150
|
+
|
151
|
+
operation.call foo: "BAR"
|
152
|
+
|
153
|
+
expect(request).to have_been_made
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|