api_hammer 0.19.0 → 0.19.2
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
- data/README.md +0 -2
- data/api_hammer.gemspec +13 -14
- data/lib/api_hammer/body.rb +10 -4
- data/lib/api_hammer/content_type_attrs.rb +2 -0
- data/lib/api_hammer/filtration/json.rb +3 -3
- data/lib/api_hammer/request_logger.rb +11 -5
- data/lib/api_hammer/trailing_newline.rb +4 -2
- data/lib/api_hammer/version.rb +1 -1
- metadata +4 -147
- data/.simplecov +0 -1
- data/.travis.yml +0 -13
- data/Gemfile +0 -10
- data/Rakefile.rb +0 -12
- data/gemfiles/Gemfile_rack_1 +0 -13
- data/gemfiles/Gemfile_rack_2 +0 -12
- data/test/check_required_params_test.rb +0 -80
- data/test/faraday_request_logger_test.rb +0 -228
- data/test/halt_test.rb +0 -66
- data/test/helper.rb +0 -15
- data/test/public_instance_exec_test.rb +0 -43
- data/test/request_logger_test.rb +0 -136
- data/test/show_text_exceptions_test.rb +0 -29
- data/test/trailing_newline_test.rb +0 -24
- data/test/weblink_test.rb +0 -83
- data/test/ycomb_test.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 431eb2a642aea887bd47826572e0631ebc6e18daa0714c430d6b0c690bb68ea3
|
|
4
|
+
data.tar.gz: dd74fb4768fb9f51dcb139fe3ba7cf61e9f949b96ec3013cfbde0f41534ced12
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22aa57cf47a015dc1c62053c21bff79e549ebcf7cc7d3c2702224fdcd1dd86f037186419525dbb4143c75f9ae9ce639df91af621bd7ce6b5d12b6435e200bdad
|
|
7
|
+
data.tar.gz: c136b0b42e7256bcbb9c8b6498a1a52307d116fcf0d8f0f2f8bb41991842912b3b32a95e86f8442ce36ccb42e8563ca2cac17ad969f177d3567c4b627140039e
|
data/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# ApiHammer
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.org/notEthan/api_hammer)
|
|
4
|
-
|
|
5
3
|
a collection of little tools I have used when creating APIs. these are generally too small to have their own
|
|
6
4
|
library, so have been collected here.
|
|
7
5
|
|
data/api_hammer.gemspec
CHANGED
|
@@ -13,27 +13,26 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
'is one large tool.'
|
|
14
14
|
spec.homepage = 'https://github.com/notEthan/api_hammer'
|
|
15
15
|
spec.license = 'MIT'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
spec.files = [
|
|
17
|
+
*%w(
|
|
18
|
+
.yardopts
|
|
19
|
+
CHANGELOG.md
|
|
20
|
+
LICENSE.txt
|
|
21
|
+
README.md
|
|
22
|
+
api_hammer.gemspec
|
|
23
|
+
bin/hc
|
|
24
|
+
),
|
|
25
|
+
*Dir['lib/**/*'],
|
|
26
|
+
].reject { |f| File.lstat(f).ftype == 'directory' }
|
|
27
|
+
spec.bindir = 'bin'
|
|
28
|
+
spec.executables = ['hc']
|
|
22
29
|
spec.require_paths = ['lib']
|
|
23
30
|
|
|
24
31
|
spec.add_dependency 'rack'
|
|
25
32
|
spec.add_dependency 'faraday'
|
|
26
33
|
spec.add_dependency 'term-ansicolor'
|
|
27
34
|
spec.add_dependency 'json'
|
|
28
|
-
spec.add_dependency 'json_pure'
|
|
29
35
|
spec.add_dependency 'addressable'
|
|
30
36
|
spec.add_dependency 'coderay'
|
|
31
37
|
spec.add_dependency 'i18n'
|
|
32
|
-
spec.add_development_dependency 'rake'
|
|
33
|
-
spec.add_development_dependency 'rack-test'
|
|
34
|
-
spec.add_development_dependency 'minitest'
|
|
35
|
-
spec.add_development_dependency 'minitest-reporters'
|
|
36
|
-
spec.add_development_dependency 'yard'
|
|
37
|
-
spec.add_development_dependency 'simplecov'
|
|
38
|
-
spec.add_development_dependency 'actionpack'
|
|
39
38
|
end
|
data/lib/api_hammer/body.rb
CHANGED
|
@@ -12,17 +12,23 @@ module ApiHammer
|
|
|
12
12
|
# parses the body to an object
|
|
13
13
|
def object
|
|
14
14
|
instance_variable_defined?(:@object) ? @object : @object = begin
|
|
15
|
-
if
|
|
15
|
+
if body.nil?
|
|
16
|
+
nil
|
|
17
|
+
elsif media_type == 'application/json'
|
|
16
18
|
JSON.parse(body) rescue nil
|
|
17
19
|
elsif media_type == 'application/x-www-form-urlencoded'
|
|
18
|
-
|
|
20
|
+
Addressable::URI.form_unencode(body).group_by(&:first).transform_values do |kvs|
|
|
21
|
+
kvs.map(&:last).inject(nil) { |c, v| c ? v ? "#{c},#{v}" : c : v }
|
|
22
|
+
end
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
def filtered(options)
|
|
24
28
|
@filtered ||= Body.new(begin
|
|
25
|
-
if
|
|
29
|
+
if body.nil?
|
|
30
|
+
nil
|
|
31
|
+
elsif media_type == 'application/json'
|
|
26
32
|
begin
|
|
27
33
|
ApiHammer::Filtration::Json.new(body, options).filter
|
|
28
34
|
rescue JSON::ParserError
|
|
@@ -82,7 +88,7 @@ module ApiHammer
|
|
|
82
88
|
end
|
|
83
89
|
begin
|
|
84
90
|
JSON.generate([body])
|
|
85
|
-
rescue Encoding::UndefinedConversionError
|
|
91
|
+
rescue Encoding::UndefinedConversionError, JSON::GeneratorError
|
|
86
92
|
# if updating by content-type didn't do it, try UTF8 since JSON wants that - but only
|
|
87
93
|
# if it seems to be valid utf8.
|
|
88
94
|
# don't try utf8 if the response content-type indicated something else.
|
|
@@ -15,7 +15,7 @@ module ApiHammer
|
|
|
15
15
|
|
|
16
16
|
def filter
|
|
17
17
|
reset
|
|
18
|
-
obj = ''
|
|
18
|
+
obj = +''
|
|
19
19
|
while !eos? && scan_result(obj, IGNORE)
|
|
20
20
|
end
|
|
21
21
|
if eos?
|
|
@@ -57,7 +57,7 @@ module ApiHammer
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def filter_array
|
|
60
|
-
result = ''
|
|
60
|
+
result = +''
|
|
61
61
|
delim = false
|
|
62
62
|
until eos?
|
|
63
63
|
if (value = filter_value) != UNPARSED
|
|
@@ -86,7 +86,7 @@ module ApiHammer
|
|
|
86
86
|
FILTERED_JSON = JSON.generate("[FILTERED]", :quirks_mode => true)
|
|
87
87
|
|
|
88
88
|
def filter_object
|
|
89
|
-
result = ''
|
|
89
|
+
result = +''
|
|
90
90
|
delim = false
|
|
91
91
|
until eos?
|
|
92
92
|
if (string = filter_string) != UNPARSED
|
|
@@ -53,14 +53,18 @@ module ApiHammer
|
|
|
53
53
|
super(app, logger)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
Headers = ::Rack.const_defined?(:Headers) ? ::Rack::Headers : ::Rack::Utils::HeaderHash
|
|
57
|
+
|
|
56
58
|
def call(env)
|
|
57
59
|
began_at = Time.now
|
|
58
60
|
began_ns = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
|
59
61
|
|
|
60
62
|
# this is closed after the app is called, so read it before
|
|
61
|
-
env["rack.input"]
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
if env["rack.input"]
|
|
64
|
+
env["rack.input"].rewind
|
|
65
|
+
request_body = env["rack.input"].read
|
|
66
|
+
env["rack.input"].rewind
|
|
67
|
+
end
|
|
64
68
|
|
|
65
69
|
if @logger && @logger.formatter.respond_to?(:current_tags)
|
|
66
70
|
log_tags = @logger.formatter.current_tags.dup
|
|
@@ -76,7 +80,7 @@ module ApiHammer
|
|
|
76
80
|
)
|
|
77
81
|
|
|
78
82
|
status, response_headers, response_body = @app.call(env)
|
|
79
|
-
response_headers =
|
|
83
|
+
response_headers = Headers.new.merge(response_headers)
|
|
80
84
|
body_proxy = ::Rack::BodyProxy.new(response_body) do
|
|
81
85
|
log(env, request_uri, request_body, status, response_headers, response_body, began_at, began_ns, log_tags)
|
|
82
86
|
end
|
|
@@ -135,7 +139,9 @@ module ApiHammer
|
|
|
135
139
|
parsed_body = ApiHammer::Body.new(body, content_type)
|
|
136
140
|
content_type_attrs = ApiHammer::ContentTypeAttrs.new(content_type)
|
|
137
141
|
if content_type_attrs.text?
|
|
138
|
-
if
|
|
142
|
+
if body.nil?
|
|
143
|
+
data[role]['body'] = body
|
|
144
|
+
elsif (400..599).include?(status.to_i) || body.size < LARGE_BODY_SIZE
|
|
139
145
|
# log bodies if they are not large, or if there was an error (either client or server)
|
|
140
146
|
data[role]['body'] = parsed_body.filtered(@options.reject { |k,v| ![:filter_keys].include?(k) }).jsonifiable.body
|
|
141
147
|
else
|
|
@@ -29,8 +29,10 @@ module ApiHammer
|
|
|
29
29
|
_, content_type = headers.detect { |(k,_)| k =~ /\Acontent.type\z/i }
|
|
30
30
|
if env['REQUEST_METHOD'].downcase != 'head' && ApiHammer::ContentTypeAttrs.new(content_type).text?
|
|
31
31
|
body = TNLBodyProxy.new(body){}
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
headers.each_key do |k|
|
|
33
|
+
if "content-length".casecmp?(k)
|
|
34
|
+
headers[k] = body.map(&:bytesize).inject(0, &:+).to_s
|
|
35
|
+
end
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
[status, headers, body]
|
data/lib/api_hammer/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_hammer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.19.
|
|
4
|
+
version: 0.19.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ethan
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rack
|
|
@@ -66,20 +65,6 @@ dependencies:
|
|
|
66
65
|
- - ">="
|
|
67
66
|
- !ruby/object:Gem::Version
|
|
68
67
|
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: json_pure
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
76
|
-
type: :runtime
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
83
68
|
- !ruby/object:Gem::Dependency
|
|
84
69
|
name: addressable
|
|
85
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -122,104 +107,6 @@ dependencies:
|
|
|
122
107
|
- - ">="
|
|
123
108
|
- !ruby/object:Gem::Version
|
|
124
109
|
version: '0'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: rake
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - ">="
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: '0'
|
|
132
|
-
type: :development
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - ">="
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: '0'
|
|
139
|
-
- !ruby/object:Gem::Dependency
|
|
140
|
-
name: rack-test
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - ">="
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0'
|
|
146
|
-
type: :development
|
|
147
|
-
prerelease: false
|
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
-
requirements:
|
|
150
|
-
- - ">="
|
|
151
|
-
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0'
|
|
153
|
-
- !ruby/object:Gem::Dependency
|
|
154
|
-
name: minitest
|
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
|
156
|
-
requirements:
|
|
157
|
-
- - ">="
|
|
158
|
-
- !ruby/object:Gem::Version
|
|
159
|
-
version: '0'
|
|
160
|
-
type: :development
|
|
161
|
-
prerelease: false
|
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
-
requirements:
|
|
164
|
-
- - ">="
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: '0'
|
|
167
|
-
- !ruby/object:Gem::Dependency
|
|
168
|
-
name: minitest-reporters
|
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
|
170
|
-
requirements:
|
|
171
|
-
- - ">="
|
|
172
|
-
- !ruby/object:Gem::Version
|
|
173
|
-
version: '0'
|
|
174
|
-
type: :development
|
|
175
|
-
prerelease: false
|
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
177
|
-
requirements:
|
|
178
|
-
- - ">="
|
|
179
|
-
- !ruby/object:Gem::Version
|
|
180
|
-
version: '0'
|
|
181
|
-
- !ruby/object:Gem::Dependency
|
|
182
|
-
name: yard
|
|
183
|
-
requirement: !ruby/object:Gem::Requirement
|
|
184
|
-
requirements:
|
|
185
|
-
- - ">="
|
|
186
|
-
- !ruby/object:Gem::Version
|
|
187
|
-
version: '0'
|
|
188
|
-
type: :development
|
|
189
|
-
prerelease: false
|
|
190
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
-
requirements:
|
|
192
|
-
- - ">="
|
|
193
|
-
- !ruby/object:Gem::Version
|
|
194
|
-
version: '0'
|
|
195
|
-
- !ruby/object:Gem::Dependency
|
|
196
|
-
name: simplecov
|
|
197
|
-
requirement: !ruby/object:Gem::Requirement
|
|
198
|
-
requirements:
|
|
199
|
-
- - ">="
|
|
200
|
-
- !ruby/object:Gem::Version
|
|
201
|
-
version: '0'
|
|
202
|
-
type: :development
|
|
203
|
-
prerelease: false
|
|
204
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
205
|
-
requirements:
|
|
206
|
-
- - ">="
|
|
207
|
-
- !ruby/object:Gem::Version
|
|
208
|
-
version: '0'
|
|
209
|
-
- !ruby/object:Gem::Dependency
|
|
210
|
-
name: actionpack
|
|
211
|
-
requirement: !ruby/object:Gem::Requirement
|
|
212
|
-
requirements:
|
|
213
|
-
- - ">="
|
|
214
|
-
- !ruby/object:Gem::Version
|
|
215
|
-
version: '0'
|
|
216
|
-
type: :development
|
|
217
|
-
prerelease: false
|
|
218
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
219
|
-
requirements:
|
|
220
|
-
- - ">="
|
|
221
|
-
- !ruby/object:Gem::Version
|
|
222
|
-
version: '0'
|
|
223
110
|
description: actually a set of small API-related tools. very much unlike a hammer
|
|
224
111
|
at all, which is one large tool.
|
|
225
112
|
email:
|
|
@@ -229,18 +116,12 @@ executables:
|
|
|
229
116
|
extensions: []
|
|
230
117
|
extra_rdoc_files: []
|
|
231
118
|
files:
|
|
232
|
-
- ".simplecov"
|
|
233
|
-
- ".travis.yml"
|
|
234
119
|
- ".yardopts"
|
|
235
120
|
- CHANGELOG.md
|
|
236
|
-
- Gemfile
|
|
237
121
|
- LICENSE.txt
|
|
238
122
|
- README.md
|
|
239
|
-
- Rakefile.rb
|
|
240
123
|
- api_hammer.gemspec
|
|
241
124
|
- bin/hc
|
|
242
|
-
- gemfiles/Gemfile_rack_1
|
|
243
|
-
- gemfiles/Gemfile_rack_2
|
|
244
125
|
- lib/api_hammer.rb
|
|
245
126
|
- lib/api_hammer/body.rb
|
|
246
127
|
- lib/api_hammer/content_type_attrs.rb
|
|
@@ -275,21 +156,10 @@ files:
|
|
|
275
156
|
- lib/logstash/filters/request_bodies_parsed.rb
|
|
276
157
|
- lib/logstash/filters/ruby_logger.rb
|
|
277
158
|
- lib/logstash/filters/sidekiq.rb
|
|
278
|
-
- test/check_required_params_test.rb
|
|
279
|
-
- test/faraday_request_logger_test.rb
|
|
280
|
-
- test/halt_test.rb
|
|
281
|
-
- test/helper.rb
|
|
282
|
-
- test/public_instance_exec_test.rb
|
|
283
|
-
- test/request_logger_test.rb
|
|
284
|
-
- test/show_text_exceptions_test.rb
|
|
285
|
-
- test/trailing_newline_test.rb
|
|
286
|
-
- test/weblink_test.rb
|
|
287
|
-
- test/ycomb_test.rb
|
|
288
159
|
homepage: https://github.com/notEthan/api_hammer
|
|
289
160
|
licenses:
|
|
290
161
|
- MIT
|
|
291
162
|
metadata: {}
|
|
292
|
-
post_install_message:
|
|
293
163
|
rdoc_options: []
|
|
294
164
|
require_paths:
|
|
295
165
|
- lib
|
|
@@ -304,20 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
304
174
|
- !ruby/object:Gem::Version
|
|
305
175
|
version: '0'
|
|
306
176
|
requirements: []
|
|
307
|
-
|
|
308
|
-
rubygems_version: 2.7.8
|
|
309
|
-
signing_key:
|
|
177
|
+
rubygems_version: 4.0.5
|
|
310
178
|
specification_version: 4
|
|
311
179
|
summary: an API tool
|
|
312
|
-
test_files:
|
|
313
|
-
- test/check_required_params_test.rb
|
|
314
|
-
- test/faraday_request_logger_test.rb
|
|
315
|
-
- test/halt_test.rb
|
|
316
|
-
- test/helper.rb
|
|
317
|
-
- test/public_instance_exec_test.rb
|
|
318
|
-
- test/request_logger_test.rb
|
|
319
|
-
- test/show_text_exceptions_test.rb
|
|
320
|
-
- test/trailing_newline_test.rb
|
|
321
|
-
- test/weblink_test.rb
|
|
322
|
-
- test/ycomb_test.rb
|
|
323
|
-
- ".simplecov"
|
|
180
|
+
test_files: []
|
data/.simplecov
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
SimpleCov.start
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Rakefile.rb
DELETED
data/gemfiles/Gemfile_rack_1
DELETED
data/gemfiles/Gemfile_rack_2
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
class FakeController
|
|
5
|
-
def self.rescue_from(*args)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
include(ApiHammer::Rails)
|
|
9
|
-
attr_accessor :params
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# strong parameters doesn't require its dependencies so good
|
|
13
|
-
require 'rack/test'
|
|
14
|
-
require 'active_support/core_ext/module'
|
|
15
|
-
require 'action_controller/metal/strong_parameters'
|
|
16
|
-
|
|
17
|
-
[Hash, ActionController::Parameters].each do |params_class|
|
|
18
|
-
describe "ApiHammer::Rails#check_required_params with #{params_class}" do
|
|
19
|
-
define_method(:controller_with_params) do |params|
|
|
20
|
-
FakeController.new.tap { |c| c.params = params_class.new.merge(params) }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
describe 'a moderately complex set of checks' do
|
|
24
|
-
let(:checks) { [:id, :person => [:name, :height], :lucky_numbers => Array] }
|
|
25
|
-
|
|
26
|
-
it 'passes with a moderately complex example' do
|
|
27
|
-
c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'}, :lucky_numbers => ['2'])
|
|
28
|
-
c.check_required_params(checks)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'is missing id' do
|
|
32
|
-
c = controller_with_params(:person => {:name => 'hammer', :height => '3'}, :lucky_numbers => ['2'])
|
|
33
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
34
|
-
assert_equal({'error_message' => 'id is required but was not provided', 'errors' => {'id' => ['id is required but was not provided']}}, err.body)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it 'is missing person' do
|
|
38
|
-
c = controller_with_params(:id => '99', :lucky_numbers => ['2'])
|
|
39
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
40
|
-
assert_equal({'error_message' => 'person is required but was not provided', 'errors' => {'person' => ['person is required but was not provided']}}, err.body)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'is has the wrong type for person' do
|
|
44
|
-
c = controller_with_params(:id => '99', :person => ['hammer', '3'], :lucky_numbers => ['2'])
|
|
45
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
46
|
-
assert_equal({'error_message' => 'person must be a Hash', 'errors' => {'person' => ['person must be a Hash']}}, err.body)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it 'is has the wrong type for person with hash check' do
|
|
50
|
-
c = controller_with_params(:person => [])
|
|
51
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(:person => {:id => Fixnum}) }
|
|
52
|
-
assert_equal({'error_message' => 'person must be a Hash', 'errors' => {'person' => ['person must be a Hash']}}, err.body)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it 'is missing person#name' do
|
|
56
|
-
c = controller_with_params(:id => '99', :person => {:height => '3'}, :lucky_numbers => ['2'])
|
|
57
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
58
|
-
assert_equal({'error_message' => 'person#name is required but was not provided', 'errors' => {'person#name' => ['person#name is required but was not provided']}}, err.body)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it 'is missing lucky_numbers' do
|
|
62
|
-
c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'})
|
|
63
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
64
|
-
assert_equal({'error_message' => 'lucky_numbers is required but was not provided', 'errors' => {'lucky_numbers' => ['lucky_numbers is required but was not provided']}}, err.body)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it 'has the wrong type for lucky_numbers' do
|
|
68
|
-
c = controller_with_params(:id => '99', :person => {:name => 'hammer', :height => '3'}, :lucky_numbers => '2')
|
|
69
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
70
|
-
assert_equal({'error_message' => 'lucky_numbers must be a Array', 'errors' => {'lucky_numbers' => ['lucky_numbers must be a Array']}}, err.body)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
it 'has multiple problems' do
|
|
74
|
-
c = controller_with_params({})
|
|
75
|
-
err = assert_raises(ApiHammer::Rails::Halt) { c.check_required_params(checks) }
|
|
76
|
-
assert_equal({'error_message' => 'id is required but was not provided. person is required but was not provided. lucky_numbers is required but was not provided.', 'errors' => {'id' => ['id is required but was not provided'], 'person' => ['person is required but was not provided'], 'lucky_numbers' => ['lucky_numbers is required but was not provided']}}, err.body)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
4
|
-
require 'helper'
|
|
5
|
-
require 'logger'
|
|
6
|
-
require 'stringio'
|
|
7
|
-
|
|
8
|
-
describe ApiHammer::RequestLogger do
|
|
9
|
-
let(:logio) { StringIO.new }
|
|
10
|
-
let(:logger) { Logger.new(logio) }
|
|
11
|
-
|
|
12
|
-
it 'logs' do
|
|
13
|
-
conn = Faraday.new do |f|
|
|
14
|
-
f.use ApiHammer::Faraday::RequestLogger, logger
|
|
15
|
-
f.use Faraday::Adapter::Rack, proc { |env| [200, {}, []] }
|
|
16
|
-
end
|
|
17
|
-
conn.get '/'
|
|
18
|
-
assert_match(/200/, logio.string)
|
|
19
|
-
lines = logio.string.split("\n")
|
|
20
|
-
assert_equal(2, lines.size)
|
|
21
|
-
assert lines.last =~ /INFO -- : /
|
|
22
|
-
json_bit = $'
|
|
23
|
-
JSON.parse json_bit # should not raise
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
{200 => :intense_green, 400 => :intense_yellow, 500 => :intense_red, 300 => :white}.each do |status, color|
|
|
27
|
-
it "colors by #{status} status" do
|
|
28
|
-
conn = Faraday.new do |f|
|
|
29
|
-
f.use ApiHammer::Faraday::RequestLogger, logger
|
|
30
|
-
f.use Faraday::Adapter::Rack, proc { |env| [status, {}, []] }
|
|
31
|
-
end
|
|
32
|
-
conn.get '/'
|
|
33
|
-
assert(logio.string.include?(Term::ANSIColor.send(color, status.to_s)))
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it 'registers by name' do
|
|
38
|
-
conn = Faraday.new do |f|
|
|
39
|
-
f.request :api_hammer_request_logger, logger
|
|
40
|
-
f.use Faraday::Adapter::Rack, proc { |env| [200, {}, []] }
|
|
41
|
-
end
|
|
42
|
-
conn.get '/'
|
|
43
|
-
assert_match(/200/, logio.string)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
describe 'response body encoding' do
|
|
47
|
-
it 'deals with encoding specified properly by the content type' do
|
|
48
|
-
app = proc do |env|
|
|
49
|
-
[200, {'Content-Type' => 'text/plain; charset=utf-8'}, ["Jalapeños".force_encoding("ASCII-8BIT")]]
|
|
50
|
-
end
|
|
51
|
-
conn = Faraday.new do |f|
|
|
52
|
-
f.request :api_hammer_request_logger, logger
|
|
53
|
-
f.use Faraday::Adapter::Rack, app
|
|
54
|
-
end
|
|
55
|
-
conn.get '/'
|
|
56
|
-
assert_match(/Jalapeños/, logio.string)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it 'deals content type specifying no encoding' do
|
|
60
|
-
app = proc do |env|
|
|
61
|
-
[200, {'Content-Type' => 'text/plain; x=y'}, ["Jalapeños".force_encoding("ASCII-8BIT")]]
|
|
62
|
-
end
|
|
63
|
-
conn = Faraday.new do |f|
|
|
64
|
-
f.request :api_hammer_request_logger, logger
|
|
65
|
-
f.use Faraday::Adapter::Rack, app
|
|
66
|
-
end
|
|
67
|
-
conn.get '/'
|
|
68
|
-
assert_match(/Jalapeños/, logio.string)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it 'deals with no content type' do
|
|
72
|
-
app = proc do |env|
|
|
73
|
-
[200, {}, ["Jalapeños".force_encoding("ASCII-8BIT")]]
|
|
74
|
-
end
|
|
75
|
-
conn = Faraday.new do |f|
|
|
76
|
-
f.request :api_hammer_request_logger, logger
|
|
77
|
-
f.use Faraday::Adapter::Rack, app
|
|
78
|
-
end
|
|
79
|
-
conn.get '/'
|
|
80
|
-
assert_match(/Jalapeños/, logio.string)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it 'falls back to array of codepoints when encoding is improperly specified by the content type' do
|
|
84
|
-
app = proc do |env|
|
|
85
|
-
[200, {'Content-Type' => 'text/plain; charset=utf-8'}, ["xx" + [195].pack("C*")]]
|
|
86
|
-
end
|
|
87
|
-
conn = Faraday.new do |f|
|
|
88
|
-
f.request :api_hammer_request_logger, logger
|
|
89
|
-
f.use Faraday::Adapter::Rack, app
|
|
90
|
-
end
|
|
91
|
-
conn.get '/'
|
|
92
|
-
assert_match('[120,120,195]', logio.string)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
it 'falls back to array of codepoints when encoding is not specified and not valid utf8' do
|
|
96
|
-
app = proc do |env|
|
|
97
|
-
[200, {}, ["xx" + [195].pack("C*")]]
|
|
98
|
-
end
|
|
99
|
-
conn = Faraday.new do |f|
|
|
100
|
-
f.request :api_hammer_request_logger, logger
|
|
101
|
-
f.use Faraday::Adapter::Rack, app
|
|
102
|
-
end
|
|
103
|
-
conn.get '/'
|
|
104
|
-
assert_match('[120,120,195]', logio.string)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
describe 'logging body by content-type' do
|
|
108
|
-
{
|
|
109
|
-
'application/octet-stream' => false,
|
|
110
|
-
'image/png' => false,
|
|
111
|
-
'image/png; charset=what' => false,
|
|
112
|
-
'text/plain' => true,
|
|
113
|
-
'text/plain; charset=utf-8' => true,
|
|
114
|
-
}.each do |content_type, istext|
|
|
115
|
-
it "does #{istext ? '' : 'not'} log body for #{content_type}" do
|
|
116
|
-
app = proc do |env|
|
|
117
|
-
[200, {'Content-Type' => content_type}, ['data go here']]
|
|
118
|
-
end
|
|
119
|
-
conn = Faraday.new do |f|
|
|
120
|
-
f.request :api_hammer_request_logger, logger
|
|
121
|
-
f.use Faraday::Adapter::Rack, app
|
|
122
|
-
end
|
|
123
|
-
conn.get '/'
|
|
124
|
-
assert(logio.string.include?('data go here') == istext)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
describe 'log_bodies' do
|
|
130
|
-
it 'does not log bodies when log_bodies is false' do
|
|
131
|
-
app = proc do |env|
|
|
132
|
-
[200, {'Content-Type' => 'text/plain'}, ['data go here']]
|
|
133
|
-
end
|
|
134
|
-
conn = Faraday.new do |f|
|
|
135
|
-
f.request :api_hammer_request_logger, logger, :log_bodies => false
|
|
136
|
-
f.use Faraday::Adapter::Rack, app
|
|
137
|
-
end
|
|
138
|
-
conn.get '/'
|
|
139
|
-
assert(!logio.string.include?('data go here'))
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
describe 'filtering' do
|
|
144
|
-
describe 'json response' do
|
|
145
|
-
it 'filters' do
|
|
146
|
-
app = proc { |env| [200, {'Content-Type' => 'application/json'}, ['{"pin": "foobar", "bar": "baz"}']] }
|
|
147
|
-
conn = Faraday.new do |f|
|
|
148
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
149
|
-
f.use Faraday::Adapter::Rack, app
|
|
150
|
-
end
|
|
151
|
-
conn.get '/'
|
|
152
|
-
assert_includes(logio.string, %q("body":"{\"pin\": \"[FILTERED]\", \"bar\": \"baz\"}"))
|
|
153
|
-
end
|
|
154
|
-
it 'filters nested' do
|
|
155
|
-
app = proc { |env| [200, {'Content-Type' => 'application/json'}, ['{"object": {"pin": "foobar"}, "bar": "baz"}']] }
|
|
156
|
-
conn = Faraday.new do |f|
|
|
157
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
158
|
-
f.use Faraday::Adapter::Rack, app
|
|
159
|
-
end
|
|
160
|
-
conn.get '/'
|
|
161
|
-
assert_includes(logio.string, %q("body":"{\"object\": {\"pin\": \"[FILTERED]\"}, \"bar\": \"baz\"}"))
|
|
162
|
-
end
|
|
163
|
-
it 'filters in array' do
|
|
164
|
-
app = proc { |env| [200, {'Content-Type' => 'application/json'}, ['[{"object": [{"pin": ["foobar"]}], "bar": "baz"}]']] }
|
|
165
|
-
conn = Faraday.new do |f|
|
|
166
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
167
|
-
f.use Faraday::Adapter::Rack, app
|
|
168
|
-
end
|
|
169
|
-
conn.get '/'
|
|
170
|
-
assert_includes(logio.string, %q("body":"[{\"object\": [{\"pin\": \"[FILTERED]\"}], \"bar\": \"baz\"}]"))
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
describe 'json request' do
|
|
175
|
-
it 'filters a json request' do
|
|
176
|
-
app = proc { |env| [200, {}, []] }
|
|
177
|
-
conn = Faraday.new do |f|
|
|
178
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
179
|
-
f.use Faraday::Adapter::Rack, app
|
|
180
|
-
end
|
|
181
|
-
conn.post '/', '[{"object": [{"pin": ["foobar"]}], "bar": "baz"}]', {'Content-Type' => 'application/json'}
|
|
182
|
-
assert_includes(logio.string, %q("body":"[{\"object\": [{\"pin\": \"[FILTERED]\"}], \"bar\": \"baz\"}]"))
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
describe 'form encoded response' do
|
|
187
|
-
it 'filters' do
|
|
188
|
-
app = proc { |env| [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, ['pin=foobar&bar=baz']] }
|
|
189
|
-
conn = Faraday.new do |f|
|
|
190
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
191
|
-
f.use Faraday::Adapter::Rack, app
|
|
192
|
-
end
|
|
193
|
-
conn.get '/'
|
|
194
|
-
assert_includes(logio.string, %q("body":"pin=[FILTERED]&bar=baz"))
|
|
195
|
-
end
|
|
196
|
-
it 'filters nested' do
|
|
197
|
-
app = proc { |env| [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, ['object[pin]=foobar&bar=baz']] }
|
|
198
|
-
conn = Faraday.new do |f|
|
|
199
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
200
|
-
f.use Faraday::Adapter::Rack, app
|
|
201
|
-
end
|
|
202
|
-
conn.get '/'
|
|
203
|
-
assert_includes(logio.string, %q("body":"object[pin]=[FILTERED]&bar=baz"))
|
|
204
|
-
end
|
|
205
|
-
it 'filters in array' do
|
|
206
|
-
app = proc { |env| [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, ['object[][pin][]=foobar&bar=baz']] }
|
|
207
|
-
conn = Faraday.new do |f|
|
|
208
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
209
|
-
f.use Faraday::Adapter::Rack, app
|
|
210
|
-
end
|
|
211
|
-
conn.get '/'
|
|
212
|
-
assert_includes(logio.string, %q("body":"object[][pin][]=[FILTERED]&bar=baz"))
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
describe 'form encoded request' do
|
|
217
|
-
it 'filters a json request' do
|
|
218
|
-
app = proc { |env| [200, {}, []] }
|
|
219
|
-
conn = Faraday.new do |f|
|
|
220
|
-
f.request :api_hammer_request_logger, logger, :filter_keys => 'pin'
|
|
221
|
-
f.use Faraday::Adapter::Rack, app
|
|
222
|
-
end
|
|
223
|
-
conn.post '/', 'object[pin]=foobar&bar=baz', {'Content-Type' => 'application/x-www-form-urlencoded'}
|
|
224
|
-
assert_includes(logio.string, %q(object[pin]=[FILTERED]&bar=baz))
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
end
|
data/test/halt_test.rb
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
class FakeController
|
|
5
|
-
def self.rescue_from(*args)
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
include(ApiHammer::Rails)
|
|
9
|
-
|
|
10
|
-
attr_reader :rendered
|
|
11
|
-
def render(opts)
|
|
12
|
-
@rendered = opts
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
describe 'ApiHammer::Rails#halt' do
|
|
17
|
-
it 'raises ApiHammer::Rails::Halt' do
|
|
18
|
-
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.halt(200, {}) }
|
|
19
|
-
assert_equal({}, haltex.body)
|
|
20
|
-
assert_equal(200, haltex.render_options[:status])
|
|
21
|
-
end
|
|
22
|
-
describe 'status-specific halts' do
|
|
23
|
-
it 'halts ok' do
|
|
24
|
-
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.halt_ok({}) }
|
|
25
|
-
assert_equal({}, haltex.body)
|
|
26
|
-
assert_equal(200, haltex.render_options[:status])
|
|
27
|
-
end
|
|
28
|
-
it 'halts unprocessable entity' do
|
|
29
|
-
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.halt_unprocessable_entity({}) }
|
|
30
|
-
assert_equal({'errors' => {}}, haltex.body)
|
|
31
|
-
assert_equal(422, haltex.render_options[:status])
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
describe 'find_or_halt' do
|
|
36
|
-
it 'returns a record if it exists' do
|
|
37
|
-
record = Object.new
|
|
38
|
-
model = Class.new do
|
|
39
|
-
define_singleton_method(:where) { |attrs| [record] }
|
|
40
|
-
define_singleton_method(:table_name) { 'records' }
|
|
41
|
-
end
|
|
42
|
-
assert_equal record, FakeController.new.find_or_halt(model, {:id => 'anid'})
|
|
43
|
-
end
|
|
44
|
-
it 'it halts with 404 if not' do
|
|
45
|
-
model = Class.new do
|
|
46
|
-
(class << self; self; end).class_eval do
|
|
47
|
-
define_method(:where) { |attrs| [] }
|
|
48
|
-
define_method(:table_name) { 'record' }
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
haltex = assert_raises(ApiHammer::Rails::Halt) { FakeController.new.find_or_halt(model, {:id => 'anid'}) }
|
|
52
|
-
assert_equal({'error_message' => 'Unknown record! id: anid', 'errors' => {'record' => ['Unknown record! id: anid']}}, haltex.body)
|
|
53
|
-
assert_equal(404, haltex.render_options[:status])
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
describe 'ApiHammer::Rails#handle_halt' do
|
|
59
|
-
it 'renders the things from the error' do
|
|
60
|
-
controller = FakeController.new
|
|
61
|
-
haltex = (FakeController.new.halt_unprocessable_entity({}) rescue $!)
|
|
62
|
-
controller.handle_halt(haltex)
|
|
63
|
-
assert_equal(422, controller.rendered[:status])
|
|
64
|
-
assert_equal({'errors' => {}}, controller.rendered[:json])
|
|
65
|
-
end
|
|
66
|
-
end
|
data/test/helper.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('../lib', File.dirname(__FILE__)))
|
|
2
|
-
|
|
3
|
-
require 'bundler'
|
|
4
|
-
Bundler.setup
|
|
5
|
-
|
|
6
|
-
require 'simplecov'
|
|
7
|
-
|
|
8
|
-
# NO EXPECTATIONS
|
|
9
|
-
ENV["MT_NO_EXPECTATIONS"] = ''
|
|
10
|
-
|
|
11
|
-
require 'minitest/autorun'
|
|
12
|
-
require 'minitest/reporters'
|
|
13
|
-
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
|
14
|
-
|
|
15
|
-
require 'api_hammer'
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
require 'api_hammer/public_instance_exec'
|
|
5
|
-
|
|
6
|
-
class Foo
|
|
7
|
-
def public_method(arg = :public)
|
|
8
|
-
arg
|
|
9
|
-
end
|
|
10
|
-
protected
|
|
11
|
-
def protected_method(arg = :protected)
|
|
12
|
-
arg
|
|
13
|
-
end
|
|
14
|
-
private
|
|
15
|
-
def private_method(arg = :private)
|
|
16
|
-
arg
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
describe '#public_instance_exec' do
|
|
21
|
-
it 'does things' do
|
|
22
|
-
foo = Foo.new
|
|
23
|
-
assert_equal(:public_exec, foo.public_instance_exec(:public_exec) { |arg| public_method(arg) })
|
|
24
|
-
regularex = (foo.protected_method rescue $!)
|
|
25
|
-
ex = assert_raises(regularex.class) { foo.public_instance_exec(:protected_exec) { |arg| protected_method(arg) } }
|
|
26
|
-
assert_includes(regularex.message, ex.message)
|
|
27
|
-
regularex = (foo.private_method rescue $!)
|
|
28
|
-
ex = assert_raises(regularex.class) { foo.public_instance_exec(:private_exec) { |arg| private_method(arg) } }
|
|
29
|
-
assert_includes(regularex.message, ex.message)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
describe '#public_instance_eval' do
|
|
33
|
-
it 'does things' do
|
|
34
|
-
foo = Foo.new
|
|
35
|
-
assert_equal(:public, foo.public_instance_eval { public_method })
|
|
36
|
-
regularex = (foo.protected_method rescue $!)
|
|
37
|
-
ex = assert_raises(regularex.class) { foo.public_instance_eval { protected_method } }
|
|
38
|
-
assert_includes(regularex.message, ex.message)
|
|
39
|
-
regularex = (foo.private_method rescue $!)
|
|
40
|
-
ex = assert_raises(regularex.class) { foo.public_instance_eval { private_method } }
|
|
41
|
-
assert_includes(regularex.message, ex.message)
|
|
42
|
-
end
|
|
43
|
-
end
|
data/test/request_logger_test.rb
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
3
|
-
require 'helper'
|
|
4
|
-
require 'logger'
|
|
5
|
-
require 'stringio'
|
|
6
|
-
|
|
7
|
-
describe ApiHammer::RequestLogger do
|
|
8
|
-
let(:logio) { StringIO.new }
|
|
9
|
-
let(:logger) { Logger.new(logio) }
|
|
10
|
-
|
|
11
|
-
it 'logs' do
|
|
12
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [200, {}, []] }, logger)
|
|
13
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
14
|
-
assert_match(/200/, logio.string)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it 'colors by status' do
|
|
18
|
-
{200 => :intense_green, 400 => :intense_yellow, 500 => :intense_red, 300 => :white}.each do |status, color|
|
|
19
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [status, {}, []] }, logger)
|
|
20
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
21
|
-
assert(logio.string.include?(Term::ANSIColor.send(color, status.to_s)))
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it 'logs id and uuid (json)' do
|
|
26
|
-
body = %Q({"uuid": "theuuid", "foo_uuid": "thefoouuid", "id": "theid", "id_for_x": "theidforx", "bar.id": "thebarid", "baz-guid": "bazzz", "bigthing": "#{' ' * 4096}"})
|
|
27
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [200, {"Content-Type" => 'application/json; charset=UTF8'}, [body]] }, logger)
|
|
28
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
29
|
-
assert_match(%q("body_ids":{"uuid":"theuuid","foo_uuid":"thefoouuid","id":"theid","id_for_x":"theidforx","bar.id":"thebarid","baz-guid":"bazzz"}), logio.string)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it 'logs id and uuid (json array)' do
|
|
33
|
-
body = %Q([{"uuid": "theuuid", "foo_uuid": "thefoouuid"}, {"id": "theid", "id_for_x": "theidforx"}, {"bar.id": "thebarid", "baz-guid": "bazzz", "bigthing": "#{' ' * 4096}"}])
|
|
34
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [200, {"Content-Type" => 'application/json; charset=UTF8'}, [body]] }, logger)
|
|
35
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
36
|
-
assert_match(%q("body_ids":[{"uuid":"theuuid","foo_uuid":"thefoouuid"},{"id":"theid","id_for_x":"theidforx"},{"bar.id":"thebarid","baz-guid":"bazzz"}]), logio.string)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'logs id and uuid (form encoded)' do
|
|
40
|
-
body = %Q(uuid=theuuid&foo_uuid=thefoouuid&id=theid&id_for_x=theidforx&bar.id=thebarid&baz-guid=bazzz&bigthing=#{' ' * 4096})
|
|
41
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [200, {"Content-Type" => 'application/x-www-form-urlencoded; charset=UTF8'}, [body]] }, logger)
|
|
42
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
43
|
-
assert_match(%q("body_ids":{"uuid":"theuuid","foo_uuid":"thefoouuid","id":"theid","id_for_x":"theidforx","bar.id":"thebarid","baz-guid":"bazzz"}), logio.string)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it 'logs not-too-big request response bodies' do
|
|
47
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [200, {}, ['the_response_body']] }, logger)
|
|
48
|
-
app.call(Rack::MockRequest.env_for('/', :input => 'the_request_body')).last.close
|
|
49
|
-
assert_match(/"request":\{.*"body":"the_request_body/, logio.string)
|
|
50
|
-
assert_match(/"response":\{.*"body":"the_response_body/, logio.string)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it 'logs request and response body on error (even if big)' do
|
|
54
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [400, {}, ["the_response_body #{' ' * 4096}"]] }, logger)
|
|
55
|
-
app.call(Rack::MockRequest.env_for('/', :input => "the_request_body #{' ' * 4096}")).last.close
|
|
56
|
-
assert_match(/"request":\{.*"body":"the_request_body/, logio.string)
|
|
57
|
-
assert_match(/"response":\{.*"body":"the_response_body/, logio.string)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
describe 'filtering' do
|
|
61
|
-
describe 'json response' do
|
|
62
|
-
it 'filters' do
|
|
63
|
-
body = %Q({"pin": "foobar"})
|
|
64
|
-
app = proc { |env| [200, {"Content-Type" => 'application/json; charset=UTF8'}, [body]] }
|
|
65
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
66
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
67
|
-
|
|
68
|
-
assert_includes(logio.string, %q("body":"{\"pin\": \"[FILTERED]\"}"))
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it 'filters unicode' do
|
|
72
|
-
body = %Q({"key": "Björn"})
|
|
73
|
-
app = proc { |env| [200, {"Content-Type" => 'application/json; charset=utf-8'}, [body]] }
|
|
74
|
-
app = ApiHammer::RequestLogger.new(app, logger)
|
|
75
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
76
|
-
|
|
77
|
-
assert_includes(logio.string, %q("body":"{\"key\": \"Björn\"}"))
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
it 'filters nested' do
|
|
81
|
-
body = %Q({"object": {"pin": "foobar"}})
|
|
82
|
-
app = proc { |env| [200, {"Content-Type" => 'application/json; charset=UTF8'}, [body]] }
|
|
83
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
84
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
85
|
-
|
|
86
|
-
assert_includes(logio.string, %q("body":"{\"object\": {\"pin\": \"[FILTERED]\"}}"))
|
|
87
|
-
end
|
|
88
|
-
it 'filters in array' do
|
|
89
|
-
body = %Q([{"object": [{"pin": ["foobar"]}]}])
|
|
90
|
-
app = proc { |env| [200, {"Content-Type" => 'application/json; charset=UTF8'}, [body]] }
|
|
91
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
92
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
93
|
-
|
|
94
|
-
assert_includes(logio.string, %q("body":"[{\"object\": [{\"pin\": \"[FILTERED]\"}]}]"))
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
describe 'json request' do
|
|
99
|
-
it 'filters a json request' do
|
|
100
|
-
app = ApiHammer::RequestLogger.new(proc { |env| [200, {}, []] }, logger, :filter_keys => 'pin')
|
|
101
|
-
app.call(Rack::MockRequest.env_for('/', :input => '[{"object": [{"pin": ["foobar"]}]}]', 'CONTENT_TYPE' => 'application/json')).last.close
|
|
102
|
-
assert_includes(logio.string, %q("body":"[{\"object\": [{\"pin\": \"[FILTERED]\"}]}]"))
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
describe('form encoded response') do
|
|
107
|
-
it 'filters' do
|
|
108
|
-
app = proc { |env| [200, {"Content-Type" => 'application/x-www-form-urlencoded'}, ['pin=foobar']] }
|
|
109
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
110
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
111
|
-
assert_includes(logio.string, %q("body":"pin=[FILTERED]"))
|
|
112
|
-
end
|
|
113
|
-
it 'filters nested' do
|
|
114
|
-
app = proc { |env| [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, ['object[pin]=foobar']] }
|
|
115
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
116
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
117
|
-
assert_includes(logio.string, %q("body":"object[pin]=[FILTERED]"))
|
|
118
|
-
end
|
|
119
|
-
it 'filters in array' do
|
|
120
|
-
app = proc { |env| [200, {'Content-Type' => 'application/x-www-form-urlencoded'}, ['object[][pin][]=foobar']] }
|
|
121
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
122
|
-
app.call(Rack::MockRequest.env_for('/')).last.close
|
|
123
|
-
assert_includes(logio.string, %q("body":"object[][pin][]=[FILTERED]"))
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
describe 'form encoded request' do
|
|
128
|
-
it 'filters a json request' do
|
|
129
|
-
app = proc { |env| [200, {}, []] }
|
|
130
|
-
app = ApiHammer::RequestLogger.new(app, logger, :filter_keys => 'pin')
|
|
131
|
-
app.call(Rack::MockRequest.env_for('/', :input => 'object[pin]=foobar', 'CONTENT_TYPE' => 'application/x-www-form-urlencoded')).last.close
|
|
132
|
-
assert_includes(logio.string, %q(object[pin]=[FILTERED]))
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
describe ApiHammer::ShowTextExceptions do
|
|
5
|
-
it 'lets normal responses through untouched' do
|
|
6
|
-
orig_response = [200, {}, []]
|
|
7
|
-
app = ApiHammer::ShowTextExceptions.new(proc { |env| orig_response }, {})
|
|
8
|
-
app_response = app.call(Rack::MockRequest.env_for('/'))
|
|
9
|
-
assert_equal(orig_response, app_response)
|
|
10
|
-
end
|
|
11
|
-
it '500s' do
|
|
12
|
-
app = ApiHammer::ShowTextExceptions.new(proc { |env| raise }, :full_error => true)
|
|
13
|
-
assert_equal(500, app.call(Rack::MockRequest.env_for('/')).first)
|
|
14
|
-
end
|
|
15
|
-
it 'includes the full error' do
|
|
16
|
-
app = ApiHammer::ShowTextExceptions.new(proc { |env| raise 'foo' }, :full_error => true)
|
|
17
|
-
assert_match(/RuntimeError: foo/, app.call(Rack::MockRequest.env_for('/')).last.to_enum.to_a.join)
|
|
18
|
-
end
|
|
19
|
-
it 'does not include the full error' do
|
|
20
|
-
app = ApiHammer::ShowTextExceptions.new(proc { |env| raise }, :full_error => false)
|
|
21
|
-
assert_equal("Internal Server Error\n", app.call(Rack::MockRequest.env_for('/')).last.to_enum.to_a.join)
|
|
22
|
-
end
|
|
23
|
-
it 'logs' do
|
|
24
|
-
logio=StringIO.new
|
|
25
|
-
app = ApiHammer::ShowTextExceptions.new(proc { |env| raise 'foo' }, :logger => Logger.new(logio))
|
|
26
|
-
app.call(Rack::MockRequest.env_for('/'))
|
|
27
|
-
assert_match(/RuntimeError: foo/, logio.string)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
describe ApiHammer::TrailingNewline do
|
|
5
|
-
it 'adds a trailing newline when one is missing' do
|
|
6
|
-
app = ApiHammer::TrailingNewline.new(proc { |env| [200, {}, ["foo"]] })
|
|
7
|
-
assert_equal("foo\n", app.call(Rack::MockRequest.env_for('/')).last.to_enum.to_a.join)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it 'does not add a trailing newline when one is present' do
|
|
11
|
-
app = ApiHammer::TrailingNewline.new(proc { |env| [200, {}, ["foo\n"]] })
|
|
12
|
-
assert_equal("foo\n", app.call(Rack::MockRequest.env_for('/')).last.to_enum.to_a.join)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'does not add a trailing newline when the response is blank' do
|
|
16
|
-
app = ApiHammer::TrailingNewline.new(proc { |env| [200, {}, []] })
|
|
17
|
-
assert_equal([], app.call(Rack::MockRequest.env_for('/')).last.to_enum.to_a)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it 'updates Content-Length if present' do
|
|
21
|
-
app = ApiHammer::TrailingNewline.new(proc { |env| [200, {'Content-Length' => '3'}, ['foo']] })
|
|
22
|
-
assert_equal('4', app.call(Rack::MockRequest.env_for('/'))[1]['Content-Length'])
|
|
23
|
-
end
|
|
24
|
-
end
|
data/test/weblink_test.rb
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
describe ApiHammer::Weblink do
|
|
5
|
-
describe '#parse_link_value' do
|
|
6
|
-
it 'parses link headers' do
|
|
7
|
-
examples = [
|
|
8
|
-
# one link with some attributes
|
|
9
|
-
[ %q(<http://example.com>; rel=foo; title=example; title*="an example"),
|
|
10
|
-
'http://example.com',
|
|
11
|
-
{'rel' => 'foo', 'title' => 'example', 'title*' => 'an example'},
|
|
12
|
-
],
|
|
13
|
-
# two links
|
|
14
|
-
[ %q(<http://example.com>; rel=foo; title=example; title*="an example", <http://example2.com>; rel=bar),
|
|
15
|
-
'http://example.com',
|
|
16
|
-
{'rel' => 'foo', 'title' => 'example', 'title*' => 'an example'},
|
|
17
|
-
'http://example2.com',
|
|
18
|
-
{'rel' => 'bar'},
|
|
19
|
-
],
|
|
20
|
-
# spaces
|
|
21
|
-
[ %q( <http://example.com> ;rel = foo ;title=example; title*="an example" ),
|
|
22
|
-
'http://example.com',
|
|
23
|
-
{'rel' => 'foo', 'title' => 'example', 'title*' => 'an example'},
|
|
24
|
-
],
|
|
25
|
-
# empty returns no links
|
|
26
|
-
[''],
|
|
27
|
-
]
|
|
28
|
-
examples.each do |example|
|
|
29
|
-
link_value = example.shift
|
|
30
|
-
links = ApiHammer::Weblink.parse_link_value(link_value)
|
|
31
|
-
assert_equal(example.size, links.size * 2)
|
|
32
|
-
example.each_slice(2).zip(links).each do |((target_uri, attributes), link)|
|
|
33
|
-
assert_equal(Addressable::URI.parse(target_uri), link.target_uri)
|
|
34
|
-
assert_equal(attributes, link.attributes)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'gives an absolute uri based on context' do
|
|
40
|
-
link = ApiHammer::Weblink.parse_link_value('</bar>; rel=foo', 'http://example.com/foo').first
|
|
41
|
-
assert_equal(Addressable::URI.parse('http://example.com/bar'), link.absolute_target_uri)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it 'errors without context, trying to generate an absolute uri' do
|
|
45
|
-
link = ApiHammer::Weblink.parse_link_value('</bar>; rel=foo').first
|
|
46
|
-
assert_raises(ApiHammer::Weblink::NoContextError) { link.absolute_target_uri }
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
it 'returns an empty array for nil link header' do
|
|
50
|
-
assert_equal([], ApiHammer::Weblink.parse_link_value(nil))
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it 'parse errors' do
|
|
54
|
-
examples = [
|
|
55
|
-
# missing >
|
|
56
|
-
%q(<http://example.com),
|
|
57
|
-
# missing <
|
|
58
|
-
%q(http://example.com>; rel=foo),
|
|
59
|
-
# , instead of ;
|
|
60
|
-
%q(<http://example.com>, rel=foo),
|
|
61
|
-
# non-ptoken characters (\,) unquoted
|
|
62
|
-
%q(<http://example.com>; rel=b\\ar; title=example),
|
|
63
|
-
%q(<http://example.com>; rel=b,ar; title=example),
|
|
64
|
-
]
|
|
65
|
-
examples.each do |example|
|
|
66
|
-
assert_raises(ApiHammer::Weblink::ParseError) { ApiHammer::Weblink.parse_link_value(example) }
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
describe '#to_s' do
|
|
71
|
-
it 'makes a string' do
|
|
72
|
-
link = ApiHammer::Weblink.new('http://example.com', :rel => 'foo', :title => 'example', 'title*' => 'an example')
|
|
73
|
-
assert_equal(%q(<http://example.com>; rel="foo"; title="example"; title*="an example"), link.to_s)
|
|
74
|
-
end
|
|
75
|
-
it 'parses the string to the same values' do
|
|
76
|
-
link = ApiHammer::Weblink.new('http://example.com', 'rel' => 'foo', 'title' => 'example', 'title*' => 'an example')
|
|
77
|
-
parsed_link = ApiHammer::Weblink.parse_link_value(link.to_s).first
|
|
78
|
-
assert_equal(link.target_uri, parsed_link.target_uri)
|
|
79
|
-
assert_equal(link.attributes, parsed_link.attributes)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
data/test/ycomb_test.rb
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
|
2
|
-
require 'helper'
|
|
3
|
-
|
|
4
|
-
require 'api_hammer/ycomb'
|
|
5
|
-
describe 'ycomb' do
|
|
6
|
-
it 'does the needful' do
|
|
7
|
-
length = ycomb do |len|
|
|
8
|
-
proc{|list| list == [] ? 0 : 1 + len.call(list[1..-1]) }
|
|
9
|
-
end
|
|
10
|
-
assert_equal(0, length.call([]))
|
|
11
|
-
assert_equal(3, length.call([:a, :b, :c]))
|
|
12
|
-
end
|
|
13
|
-
end
|