drillbit 1.1.0 → 2.0.0
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/drillbit.rb +3 -1
- data/lib/drillbit/authorizable_resource.rb +6 -0
- data/lib/drillbit/authorizers/parameters/filtering.rb +1 -0
- data/lib/drillbit/authorizers/parameters/resource.rb +59 -0
- data/lib/drillbit/errors/invalid_request_body.rb +29 -0
- data/lib/drillbit/middleware/api_request_validator.rb +40 -0
- data/lib/drillbit/middleware/parameter_parser.rb +61 -0
- data/lib/drillbit/middleware/token_processor.rb +26 -0
- data/lib/drillbit/requests/base.rb +8 -5
- data/lib/drillbit/responses/invalid_request_body.rb +18 -0
- data/lib/drillbit/tokens/json_web_token.rb +9 -1
- data/lib/drillbit/version.rb +1 -1
- data/spec/drillbit/accept_header_spec.rb +2 -2
- data/spec/drillbit/authorizers/parameters/filtering_spec.rb +4 -4
- data/spec/drillbit/authorizers/parameters/resource_spec.rb +4 -4
- data/spec/drillbit/authorizers/parameters_spec.rb +3 -3
- data/spec/drillbit/authorizers/query_spec.rb +3 -3
- data/spec/drillbit/authorizers/scope_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_api_request_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_request_body_spec.rb +25 -0
- data/spec/drillbit/errors/invalid_subdomain_spec.rb +3 -3
- data/spec/drillbit/errors/invalid_token_spec.rb +3 -3
- data/spec/drillbit/invalid_subdomain_spec.rb +3 -3
- data/spec/drillbit/invalid_token_spec.rb +3 -3
- data/spec/drillbit/matchers/accept_header_spec.rb +3 -3
- data/spec/drillbit/matchers/subdomain_spec.rb +3 -3
- data/spec/drillbit/matchers/version_spec.rb +3 -3
- data/spec/drillbit/middleware/{api_request_spec.rb → api_request_validator_spec.rb} +11 -46
- data/spec/drillbit/middleware/parameter_parser_spec.rb +184 -0
- data/spec/drillbit/middleware/token_processor_spec.rb +27 -0
- data/spec/drillbit/requests/base_spec.rb +3 -3
- data/spec/drillbit/requests/rack_spec.rb +3 -3
- data/spec/drillbit/requests/rails_spec.rb +3 -3
- data/spec/drillbit/resource/model_spec.rb +3 -3
- data/spec/drillbit/resource/processors/filtering_spec.rb +4 -4
- data/spec/drillbit/resource/processors/indexing_spec.rb +4 -4
- data/spec/drillbit/resource/processors/paging_spec.rb +4 -4
- data/spec/drillbit/resource/processors/sorting_spec.rb +4 -4
- data/spec/drillbit/tokens/base64_spec.rb +3 -3
- data/spec/drillbit/tokens/json_web_token_spec.rb +11 -3
- data/spec/drillbit/tokens/json_web_tokens/password_reset_spec.rb +4 -4
- metadata +15 -8
- metadata.gz.sig +0 -0
- data/lib/drillbit/middleware/api_request.rb +0 -49
- data/lib/drillbit/parameters.rb +0 -22
- data/spec/drillbit/parameters_spec.rb +0 -49
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/errors/invalid_request_body'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidRequestBody do
|
8
|
+
let(:error) { InvalidRequestBody.new }
|
9
|
+
|
10
|
+
it 'has a status of 400' do
|
11
|
+
expect(error.http_status).to eql 400
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has a code' do
|
15
|
+
expect(error.code).to eql 'errors.invalid_request_body'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can output the detail' do
|
19
|
+
expect(error.detail).to eql \
|
20
|
+
'The information you attempted to send in the request cannot be parsed as ' \
|
21
|
+
'a valid JSON document.'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/errors/invalid_subdomain'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidSubdomain do
|
8
8
|
let(:error) { InvalidSubdomain.new }
|
9
9
|
|
10
10
|
it 'has a status of 404' do
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/errors/invalid_token'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Errors
|
7
|
+
describe InvalidToken do
|
8
8
|
let(:error) { InvalidToken.new }
|
9
9
|
|
10
10
|
it 'has a status of 401' do
|
@@ -2,9 +2,9 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
require 'drillbit/responses/invalid_subdomain'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
|
5
|
+
module Drillbit
|
6
|
+
module Responses
|
7
|
+
describe InvalidSubdomain, singletons: Erratum::Configuration do
|
8
8
|
it 'returns the proper response' do
|
9
9
|
Erratum.configuration.url_mappings = {
|
10
10
|
'external_documentation_urls' => {
|
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
require 'drillbit/responses/invalid_token'
|
4
4
|
|
5
5
|
# rubocop:disable Metrics/LineLength
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Responses
|
8
|
+
describe InvalidToken, singletons: Erratum::Configuration do
|
9
9
|
it 'returns the proper response' do
|
10
10
|
Erratum.configuration.url_mappings = {
|
11
11
|
'external_documentation_urls' => {
|
@@ -4,9 +4,9 @@ require 'drillbit/requests/base'
|
|
4
4
|
require 'drillbit/matchers/accept_header'
|
5
5
|
|
6
6
|
# rubocop:disable Metrics/LineLength
|
7
|
-
module
|
8
|
-
module
|
9
|
-
|
7
|
+
module Drillbit
|
8
|
+
module Matchers
|
9
|
+
describe AcceptHeader do
|
10
10
|
it 'matches if the subdomain is API and the accept header is valid' do
|
11
11
|
env = {
|
12
12
|
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
@@ -4,9 +4,9 @@ require 'drillbit/requests/base'
|
|
4
4
|
require 'drillbit/matchers/subdomain'
|
5
5
|
require 'drillbit/configuration'
|
6
6
|
|
7
|
-
module
|
8
|
-
module
|
9
|
-
|
7
|
+
module Drillbit
|
8
|
+
module Matchers
|
9
|
+
describe Subdomain do
|
10
10
|
before(:each) do
|
11
11
|
Drillbit.configuration.allowed_subdomains = %w{api}
|
12
12
|
Drillbit.configuration.allowed_api_subdomains = %w{api}
|
@@ -3,9 +3,9 @@ require 'spec_helper'
|
|
3
3
|
require 'drillbit/requests/base'
|
4
4
|
require 'drillbit/matchers/version'
|
5
5
|
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Matchers
|
8
|
+
describe Version do
|
9
9
|
context 'when the version is passed in the accept header' do
|
10
10
|
it 'does not match if the subdomain is API but the requested version does not ' \
|
11
11
|
'equal the version constraint' do
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'spec_helper'
|
3
|
-
require 'drillbit/middleware/
|
3
|
+
require 'drillbit/middleware/api_request_validator'
|
4
4
|
|
5
5
|
# rubocop:disable Metrics/LineLength
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Middleware
|
8
|
+
describe ApiRequestValidator, singletons: Erratum::Configuration do
|
9
9
|
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
10
10
|
|
11
11
|
before(:each) do
|
@@ -26,7 +26,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'allows requests for allowed subdomains without accept headers' do
|
29
|
-
api_request_middleware =
|
29
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
30
30
|
|
31
31
|
request = {
|
32
32
|
'HTTP_HOST' => 'westeros.example.com',
|
@@ -42,7 +42,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
it 'does not allow requests if they are not for an allowed subdomain' do
|
45
|
-
api_request_middleware =
|
45
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
46
46
|
|
47
47
|
request = {
|
48
48
|
'HTTP_HOST' => 'notvalid.example.com',
|
@@ -78,7 +78,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
78
78
|
it 'does not allow requests if they are for an allowed subdomain but does ' \
|
79
79
|
'not have a valid accept header' do
|
80
80
|
|
81
|
-
api_request_middleware =
|
81
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
82
82
|
|
83
83
|
request = {
|
84
84
|
'HTTP_HOST' => 'api.example.com',
|
@@ -112,7 +112,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
112
112
|
end
|
113
113
|
|
114
114
|
it 'does allow requests if both the subdomain and the accept header are valid' do
|
115
|
-
api_request_middleware =
|
115
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
116
116
|
|
117
117
|
request = {
|
118
118
|
'HTTP_HOST' => 'api.example.com',
|
@@ -129,7 +129,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
129
129
|
|
130
130
|
it 'does allow requests if the subdomain, the accept header and the token are valid' do
|
131
131
|
Drillbit.configuration.token_private_key = test_private_key
|
132
|
-
api_request_middleware =
|
132
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
133
133
|
|
134
134
|
request = {
|
135
135
|
'HTTP_HOST' => 'api.example.com',
|
@@ -145,43 +145,8 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
145
145
|
expect(response).to eql 'response'
|
146
146
|
end
|
147
147
|
|
148
|
-
it 'returns the proper response if the token is invalid' do
|
149
|
-
Drillbit.configuration.token_private_key = test_private_key
|
150
|
-
api_request_middleware = ApiRequest.new(app)
|
151
|
-
|
152
|
-
request = {
|
153
|
-
'HTTP_HOST' => 'api.example.com',
|
154
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
155
|
-
'HTTP_AUTHORIZATION' => "Token #{invalid_jwe_token}",
|
156
|
-
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
157
|
-
}
|
158
|
-
|
159
|
-
_status, _headers, response = api_request_middleware.call(request)
|
160
|
-
|
161
|
-
expect(response.first).to include 'errors.invalid_token'
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'converts JSON API compliant dasherized query params to underscored' do
|
165
|
-
app = ->(env) { [200, env, 'response'] }
|
166
|
-
api_request_middleware = ApiRequest.new(app)
|
167
|
-
|
168
|
-
request = {
|
169
|
-
'HTTP_HOST' => 'api.example.com',
|
170
|
-
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
171
|
-
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
172
|
-
'nice-to-meet=you-bob&' \
|
173
|
-
'hows-the-weather=today-bob',
|
174
|
-
}
|
175
|
-
|
176
|
-
_status, headers, _response = api_request_middleware.call(request)
|
177
|
-
|
178
|
-
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
179
|
-
'nice_to_meet=you-bob&' \
|
180
|
-
'hows_the_weather=today-bob'
|
181
|
-
end
|
182
|
-
|
183
148
|
it 'properly converts the content type for Rails when it is the only one' do
|
184
|
-
api_request_middleware =
|
149
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
185
150
|
|
186
151
|
request = {
|
187
152
|
'CONTENT_TYPE' => 'application/vnd.api+json',
|
@@ -199,7 +164,7 @@ RSpec.describe ApiRequest, singletons: Erratum::Configuration do
|
|
199
164
|
end
|
200
165
|
|
201
166
|
it 'properly converts the content type for Rails when it is not the only one' do
|
202
|
-
api_request_middleware =
|
167
|
+
api_request_middleware = ApiRequestValidator.new(app)
|
203
168
|
|
204
169
|
request = {
|
205
170
|
'CONTENT_TYPE' => 'application/vnd.api+json;other',
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/middleware/parameter_parser'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Middleware
|
7
|
+
describe ParameterParser do
|
8
|
+
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
9
|
+
|
10
|
+
it 'converts JSON API compliant dasherized query params to underscored' do
|
11
|
+
app = ->(env) { [200, env, 'response'] }
|
12
|
+
middleware = ParameterParser.new(app)
|
13
|
+
|
14
|
+
request = {
|
15
|
+
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
16
|
+
'nice-to-meet=you-bob&' \
|
17
|
+
'hows-the-weather=today-bob',
|
18
|
+
}
|
19
|
+
|
20
|
+
_status, headers, _response = middleware.call(request)
|
21
|
+
|
22
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
23
|
+
'nice_to_meet=you-bob&' \
|
24
|
+
'hows_the_weather=today-bob'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does not touch query params with no dashes' do
|
28
|
+
app = ->(env) { [200, env, 'response'] }
|
29
|
+
middleware = ParameterParser.new(app)
|
30
|
+
|
31
|
+
request = {
|
32
|
+
'QUERY_STRING' => 'hello_there=bob-jones',
|
33
|
+
}
|
34
|
+
|
35
|
+
_status, headers, _response = middleware.call(request)
|
36
|
+
|
37
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'can handle weirdly formatted query string parameters' do
|
41
|
+
app = ->(env) { [200, env, 'response'] }
|
42
|
+
middleware = ParameterParser.new(app)
|
43
|
+
|
44
|
+
request = {
|
45
|
+
'QUERY_STRING' => 'hello-there=bob-jones&' \
|
46
|
+
'nice-to-meet=you-bob&=',
|
47
|
+
}
|
48
|
+
|
49
|
+
_status, headers, _response = middleware.call(request)
|
50
|
+
|
51
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
52
|
+
'nice_to_meet=you-bob&='
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'can handle query string parameters with no values' do
|
56
|
+
app = ->(env) { [200, env, 'response'] }
|
57
|
+
middleware = ParameterParser.new(app)
|
58
|
+
|
59
|
+
request = {
|
60
|
+
'QUERY_STRING' => 'hello-there&nice-to-meet=you-bob&=',
|
61
|
+
}
|
62
|
+
|
63
|
+
_status, headers, _response = middleware.call(request)
|
64
|
+
|
65
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there&nice_to_meet=you-bob&='
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'can handle query string parameters with parameter name' do
|
69
|
+
app = ->(env) { [200, env, 'response'] }
|
70
|
+
middleware = ParameterParser.new(app)
|
71
|
+
|
72
|
+
request = {
|
73
|
+
'QUERY_STRING' => 'hello-there=bob-jones&=you-bob&nice-to-meet=you-bob&=',
|
74
|
+
}
|
75
|
+
|
76
|
+
_status, headers, _response = middleware.call(request)
|
77
|
+
|
78
|
+
expect(headers['QUERY_STRING']).to eql 'hello_there=bob-jones&' \
|
79
|
+
'=you-bob&' \
|
80
|
+
'nice_to_meet=you-bob&='
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'converts JSON API compliant dasherized request params to underscored' do
|
84
|
+
app = ->(env) { [200, env, 'response'] }
|
85
|
+
middleware = ParameterParser.new(app)
|
86
|
+
|
87
|
+
request = {
|
88
|
+
'CONTENT_LENGTH' => '1',
|
89
|
+
'CONTENT_TYPE' => 'application/json',
|
90
|
+
'RACK_INPUT' => <<-HEREDOC
|
91
|
+
{
|
92
|
+
"single": "word",
|
93
|
+
"double-word": "double double",
|
94
|
+
"triple": {
|
95
|
+
"trippple-tripple": "mcdipple",
|
96
|
+
"quad": {
|
97
|
+
"another-level": "whoa inception"
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
HEREDOC
|
102
|
+
}
|
103
|
+
|
104
|
+
_status, headers, _response = middleware.call(request)
|
105
|
+
|
106
|
+
expect(JSON.load(headers['RACK_INPUT'])).to eql(
|
107
|
+
'single' => 'word',
|
108
|
+
'double_word' => 'double double',
|
109
|
+
'triple' => {
|
110
|
+
'trippple_tripple' => 'mcdipple',
|
111
|
+
'quad' => {
|
112
|
+
'another_level' => 'whoa inception',
|
113
|
+
},
|
114
|
+
},
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not convert to JSON if the content type is not JSON' do
|
119
|
+
app = ->(env) { [200, env, 'response'] }
|
120
|
+
middleware = ParameterParser.new(app)
|
121
|
+
|
122
|
+
request = {
|
123
|
+
'CONTENT_LENGTH' => '1',
|
124
|
+
'CONTENT_TYPE' => 'not_j_s_o_n',
|
125
|
+
'RACK_INPUT' => 'This is not JSON',
|
126
|
+
}
|
127
|
+
|
128
|
+
_status, headers, _response = middleware.call(request)
|
129
|
+
|
130
|
+
expect(headers['RACK_INPUT']).to eql 'This is not JSON'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'does not convert to JSON if there is no content' do
|
134
|
+
app = ->(env) { [200, env, 'response'] }
|
135
|
+
middleware = ParameterParser.new(app)
|
136
|
+
|
137
|
+
request = {
|
138
|
+
'CONTENT_LENGTH' => '0',
|
139
|
+
'CONTENT_TYPE' => 'application/json',
|
140
|
+
'RACK_INPUT' => 'empty',
|
141
|
+
}
|
142
|
+
|
143
|
+
_status, headers, _response = middleware.call(request)
|
144
|
+
|
145
|
+
expect(headers['RACK_INPUT']).to eql 'empty'
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'can handle incorrectly formatted JSON' do
|
149
|
+
app = ->(env) { [200, env, 'response'] }
|
150
|
+
middleware = ParameterParser.new(app)
|
151
|
+
|
152
|
+
request = {
|
153
|
+
'CONTENT_LENGTH' => '1',
|
154
|
+
'CONTENT_TYPE' => 'application/json',
|
155
|
+
'RACK_INPUT' => 'something blah',
|
156
|
+
}
|
157
|
+
|
158
|
+
status, headers, response = middleware.call(request)
|
159
|
+
|
160
|
+
expect(status).to eql 400
|
161
|
+
expect(headers).to eql({})
|
162
|
+
expect(JSON.load(response[0])).to include(
|
163
|
+
'errors' => [
|
164
|
+
{
|
165
|
+
'id' => match(/[a-z0-9\-]+/),
|
166
|
+
'links' => {
|
167
|
+
'about' => nil,
|
168
|
+
'documentation' => nil,
|
169
|
+
},
|
170
|
+
'status' => 400,
|
171
|
+
'code' => 'errors.invalid_request_body',
|
172
|
+
'title' => 'Invalid Request Body',
|
173
|
+
'detail' => 'The information you attempted to send in the ' \
|
174
|
+
'request cannot be parsed as a valid JSON document.',
|
175
|
+
'source' => {
|
176
|
+
'request_body' => 'something blah',
|
177
|
+
},
|
178
|
+
},
|
179
|
+
],
|
180
|
+
)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'drillbit/middleware/token_processor'
|
4
|
+
|
5
|
+
module Drillbit
|
6
|
+
module Middleware
|
7
|
+
describe TokenProcessor do
|
8
|
+
let(:app) { ->(_env) { [200, {}, 'response'] } }
|
9
|
+
|
10
|
+
it 'returns the proper response if the token is invalid' do
|
11
|
+
Drillbit.configuration.token_private_key = test_private_key
|
12
|
+
middleware = TokenProcessor.new(app)
|
13
|
+
|
14
|
+
request = {
|
15
|
+
'HTTP_HOST' => 'api.example.com',
|
16
|
+
'HTTP_ACCEPT' => 'application/vnd.westeros+redkeep;version=1.0.0',
|
17
|
+
'HTTP_AUTHORIZATION' => "Token #{invalid_jwe_token}",
|
18
|
+
'QUERY_STRING' => 'accept=application/vnd.westeros+redkeep;version=1.0.0',
|
19
|
+
}
|
20
|
+
|
21
|
+
_status, _headers, response = middleware.call(request)
|
22
|
+
|
23
|
+
expect(response.first).to include 'errors.invalid_token'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -3,9 +3,9 @@ require 'ostruct'
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'drillbit/requests/base'
|
5
5
|
|
6
|
-
module
|
7
|
-
module
|
8
|
-
|
6
|
+
module Drillbit
|
7
|
+
module Requests
|
8
|
+
describe Base do
|
9
9
|
it 'can resolve itself by returning itself' do
|
10
10
|
raw_request = Base.new(token_private_key: '', request: {})
|
11
11
|
resolved_request = Base.resolve(raw_request)
|