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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43a33c5f9ae38cb2f9bdc1fa75f16c9bbb58abc228ae400d1321f90872dc09e6
4
- data.tar.gz: c69e131b3dc28183e32f1cc5a601f38bdb7cd82a8493e0b854818e66c8f9215d
3
+ metadata.gz: 431eb2a642aea887bd47826572e0631ebc6e18daa0714c430d6b0c690bb68ea3
4
+ data.tar.gz: dd74fb4768fb9f51dcb139fe3ba7cf61e9f949b96ec3013cfbde0f41534ced12
5
5
  SHA512:
6
- metadata.gz: a581e28995ffdeee7f712d9bc743b77db7deca57748267cfbc17e2fb0bf481542facf6a5d511379ed5d8c9291ccb57f4539021eb1e0951834872da6754cdd1bb
7
- data.tar.gz: a1fada15803bcb03ba0745c1c46bdd062845b9c511d792d48795a67271ccac78203106a9e9e955e4aad29fad9ba950b5430db51e2c2f472431e9864358a0bfc1
6
+ metadata.gz: 22aa57cf47a015dc1c62053c21bff79e549ebcf7cc7d3c2702224fdcd1dd86f037186419525dbb4143c75f9ae9ce639df91af621bd7ce6b5d12b6435e200bdad
7
+ data.tar.gz: c136b0b42e7256bcbb9c8b6498a1a52307d116fcf0d8f0f2f8bb41991842912b3b32a95e86f8442ce36ccb42e8563ca2cac17ad969f177d3567c4b627140039e
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # ApiHammer
2
2
 
3
- [![Build Status](https://travis-ci.org/notEthan/api_hammer.svg?branch=master)](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
- spec.files = `git ls-files -z`.split("\x0") - ['.gitignore']
18
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
- spec.test_files = `git ls-files -z test`.split("\x0") + [
20
- '.simplecov',
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
@@ -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 media_type == 'application/json'
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
- CGI.parse(body).map { |k, vs| {k => vs.last} }.inject({}, &:update)
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 media_type == 'application/json'
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.
@@ -1,3 +1,5 @@
1
+ require 'strscan'
2
+
1
3
  module ApiHammer
2
4
  # parses attributes out of content type header
3
5
  class ContentTypeAttrs
@@ -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"].rewind
62
- request_body = env["rack.input"].read
63
- env["rack.input"].rewind
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 = ::Rack::Utils::HeaderHash.new(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 (400..599).include?(status.to_i) || body.size < LARGE_BODY_SIZE
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
- if headers["Content-Length"]
33
- headers["Content-Length"] = body.map(&:bytesize).inject(0, &:+).to_s
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]
@@ -1,3 +1,3 @@
1
1
  module ApiHammer
2
- VERSION = "0.19.0"
2
+ VERSION = "0.19.2"
3
3
  end
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.0
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: 2019-02-01 00:00:00.000000000 Z
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
- rubyforge_project:
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
@@ -1,13 +0,0 @@
1
- rvm:
2
- - 2.2.10
3
- - 2.3.8
4
- - 2.6.1
5
- gemfile:
6
- - gemfiles/Gemfile_rack_1
7
- - gemfiles/Gemfile_rack_2
8
- matrix:
9
- fast_finish: true
10
- exclude:
11
- - rvm: 2.0.0
12
- gemfile: gemfiles/Gemfile_rack_2
13
- script: rake test
data/Gemfile DELETED
@@ -1,10 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in api_hammer.gemspec
4
- gemspec
5
-
6
- gem 'byebug'
7
- gem 'wwtd'
8
- if RUBY_VERSION == '2.0.0'
9
- gem 'nokogiri', '~> 1.6.8'
10
- end
data/Rakefile.rb DELETED
@@ -1,12 +0,0 @@
1
- require 'rake/testtask'
2
- Rake::TestTask.new do |t|
3
- t.name = 'test'
4
- t.test_files = FileList['test/**/*_test.rb']
5
- t.verbose = true
6
- end
7
- require 'wwtd/tasks'
8
- task 'default' => 'wwtd'
9
-
10
- require 'yard'
11
- YARD::Rake::YardocTask.new do |t|
12
- end
@@ -1,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '..'
4
-
5
- gem 'wwtd'
6
-
7
- gem 'rack', '~> 1.0'
8
- gem 'actionpack', '~> 4.0'
9
-
10
- if RUBY_VERSION == '2.0.0'
11
- gem 'nokogiri', '~> 1.6.8'
12
- gem 'public_suffix', '~> 2.0'
13
- end
@@ -1,12 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec path: '..'
4
-
5
- gem 'wwtd'
6
-
7
- gem 'rack', '~> 2.0'
8
- gem 'actionpack', '~> 5.0'
9
-
10
- if RUBY_VERSION == '2.0.0'
11
- gem 'nokogiri', '~> 1.6.8'
12
- end
@@ -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
@@ -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