dalziel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6944cdfc4fdc1dd2a028bfb9c6fe2583daf677a9
4
+ data.tar.gz: 2da11520a936a1a849d3309273f65f50ff65a992
5
+ SHA512:
6
+ metadata.gz: 3ad60a04ed570187c166d925c6da42758d0b306860edce7966ff76f5146840f07b80a2e8e6bbd0de9e4b91b1b09952271546abca82c89cc2f09dc2983c428741
7
+ data.tar.gz: ab6de7d98900481fad52bb34684327050f3f90953ee8a149a254ebcea0869d392c9fde3a39bd5ada610639e9c38df5808929cec926bc64f24f334b9351871c12
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.14.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dalziel.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Dalziel
2
+
3
+ Convenience library for testing JSON API calls in RSpec.
4
+ Uses [WebMock](https://github.com/bblimke/webmock) and
5
+ [JSON Expressions](https://github.com/chancancode/json_expressions).
6
+
7
+ * Easily specify JSON responses for stubbed calls with WebMock.
8
+ * Verify that the request you've sent contains the right data.
9
+ * Verify that you respond as a proper JSON API.
10
+ * Easy to read failure messages
11
+
12
+ ## Usage
13
+
14
+ Testing outgoing requests:
15
+
16
+ ``` ruby
17
+
18
+ it "makes the right request" do
19
+
20
+ # lightweight wrapper around webmock for stubbing JSON calls
21
+ request = stub_json_request(
22
+ :put,
23
+ "http://some-api-call"
24
+ user: {
25
+ id: 5,
26
+ name: "Pascoe"
27
+ }
28
+ )
29
+
30
+ call_your_code
31
+
32
+ # make sure you sent the right data to the external service
33
+ expect(request).to match_json_request(
34
+ user: {
35
+ name: "Pascoe",
36
+ password: String,
37
+ }.ignore_extra_keys!
38
+ )
39
+ end
40
+ ```
41
+
42
+ The other side of the request, verify that you behave as a JSON API:
43
+
44
+ ``` ruby
45
+ it "creates a user" do
46
+ post "/users", user: { name: "Pascoe" }
47
+
48
+ expect(User.count).to eq 1
49
+
50
+ expect(last_response).to match_json_response(
51
+ user: {
52
+ id: Integer,
53
+ name: String,
54
+ }.ignore_extra_keys!
55
+ ).status(201) # defaults to 200
56
+ end
57
+ ```
58
+
59
+ This tests your headers too.
60
+
61
+ ## Installation
62
+
63
+ Add this line to your application's Gemfile:
64
+
65
+ ```ruby
66
+ gem 'dalziel', group: :test
67
+ ```
68
+
69
+ ## Development
70
+
71
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
72
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
73
+ prompt that will allow you to experiment.
74
+
75
+ To install this gem onto your local machine, run `bundle exec rake install`.
76
+ To release a new version, update the version number in `version.rb`,
77
+ and then run `bundle exec rake release`, which will create a git tag
78
+ for the version, push git commits and tags, and push the `.gem` file to
79
+ [rubygems.org](https://rubygems.org).
80
+
81
+ ## License
82
+
83
+ The gem is available as open source under the terms of the [MIT
84
+ License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dalziel"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/dalziel.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dalziel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dalziel"
8
+ spec.version = Dalziel::VERSION
9
+ spec.authors = ["iain"]
10
+ spec.email = ["iain@iain.nl"]
11
+
12
+ spec.summary = %q{Convenience gem for testing JSON API calls}
13
+ spec.description = %q{Convenience gem for testing JSON API calls}
14
+ spec.homepage = "https://github.com/iain/dalziel"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+
28
+ spec.add_dependency "json_expressions", "~> 0.8"
29
+ spec.add_dependency "webmock", "~> 2.3"
30
+ end
@@ -0,0 +1,3 @@
1
+ module Dalziel
2
+ VERSION = "0.1.0"
3
+ end
data/lib/dalziel.rb ADDED
@@ -0,0 +1,232 @@
1
+ require "dalziel/version"
2
+ require "json_expressions"
3
+ require "webmock"
4
+
5
+ module Dalziel
6
+
7
+ def self.format_headers(headers)
8
+ size = headers.keys.map(&:size).max
9
+ headers.map { |k,v| "%-#{size + 2}s %s" % [ "#{k}:", v ] }.join("\n")
10
+ end
11
+
12
+ def self.indent(string)
13
+ string.split("\n").map { |line| " #{line}" }.join("\n")
14
+ end
15
+
16
+ module Matchers
17
+
18
+ # Stubs outgoing JSON requests with WebMock.
19
+ #
20
+ # Usage:
21
+ #
22
+ # stub_json_request(:get, "url", user: { id: 1 })
23
+ def stub_json_request(verb, url, body, status = 200)
24
+ stub_request(verb, url).to_return(
25
+ headers: { content_type: "application/json" },
26
+ body: body.to_json,
27
+ status: status,
28
+ )
29
+ end
30
+
31
+
32
+ # Verifies outgoing request body stubbed with WebMock.
33
+ #
34
+ # Usage:
35
+ #
36
+ # req = stub_json_request(:get, "url", user: { id: 1 })
37
+ #
38
+ # act
39
+ #
40
+ # expect(req).to match_json_request(
41
+ # foo: {
42
+ # bar: Integer
43
+ # }
44
+ # )
45
+ def match_json_request(pattern)
46
+ RequestMatcher.new(pattern)
47
+ end
48
+
49
+ # Verifies that the response is a proper JSON response.
50
+ # Optionally you can chain `status` to verify the status code.
51
+ #
52
+ # Usage:
53
+ #
54
+ # get "/foo/bar"
55
+ # expect(last_response).to match_json_response(
56
+ # foo: {
57
+ # bar: Integer
58
+ # }
59
+ # )
60
+ def match_json_response(pattern)
61
+ ResponseMatcher.new(pattern)
62
+ end
63
+
64
+ # Replacement for the json_expression default matcher, shows prettier output.
65
+ #
66
+ # Usage:
67
+ #
68
+ # expect(json_or_hash).to match_json_expression(
69
+ # user: {
70
+ # id: Integer
71
+ # }
72
+ # )
73
+ def match_json_expression(pattern)
74
+ JSONPatternMatcher.new(pattern)
75
+ end
76
+
77
+ class JSONPatternMatcher
78
+
79
+ attr_reader :json_expression, :request, :body
80
+
81
+ def initialize(json_expression)
82
+ @json_expression = json_expression
83
+ end
84
+
85
+ def does_not_match?(*)
86
+ fail "Inverted matching is not implemented with this matcher"
87
+ end
88
+
89
+ def matches?(json)
90
+ @original = json
91
+ @hash = json.is_a?(String) ? JSON.parse(json) : json
92
+ matcher =~ @hash
93
+ rescue JSON::ParserError => error
94
+ @not_parsable_json = error
95
+ false
96
+ end
97
+
98
+ def failure_message
99
+ if @not_parsable_json
100
+ original = Dalziel.indent(@original.inspect)
101
+ error = "#{@not_parsable_json.class}: #{@not_parsable_json}"
102
+ "Couldn't parse the following:\n\n%s\n\n%s" % [ original, error ]
103
+ else
104
+ json = Dalziel.indent(JSON.pretty_generate(@hash))
105
+ type = @original.is_a?(String) ? "JSON" : @original.class.to_s
106
+ "Got the following %s:\n\n%s\n\n%s" % [ type, json, matcher.last_error ]
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def matcher
113
+ @matcher ||= JsonExpressions::Matcher.new(json_expression)
114
+ end
115
+
116
+ end
117
+
118
+ class RequestMatcher
119
+
120
+ attr_reader :json_expression, :request, :body
121
+
122
+ def initialize(json_expression)
123
+ @json_expression = json_expression
124
+ end
125
+
126
+ def matches?(request_pattern)
127
+ @request = nil
128
+ all_stubbed_requests.each { |request_signature, _count|
129
+ if request_pattern.matches?(request_signature)
130
+ @request = request_signature
131
+ break
132
+ end
133
+ }
134
+ return false if @request.nil?
135
+
136
+ @body = JSON.parse(@request.body)
137
+ @accept = @request.headers["Accept"]
138
+
139
+ @is_json = @accept =~ /\bjson$/
140
+ @json_match = payload_matcher =~ @body
141
+
142
+ @is_json && @json_match
143
+ end
144
+
145
+ def does_not_match?(response)
146
+ fail "Inverted matching is not implemented with this matcher"
147
+ end
148
+
149
+ def failure_message
150
+ if @request == []
151
+ "No request matched"
152
+ elsif !@is_json
153
+ "Accept header is not JSON.\n\n%s\n\nAccept is %s" % [ show_request, @accept.inspect ]
154
+ else
155
+ "Request body did not match.\n\n%s\n\n%s" % [ show_request, payload_matcher.last_error ]
156
+ end
157
+ end
158
+
159
+ def show_request
160
+ headers = Dalziel.format_headers(request.headers)
161
+ Dalziel.indent "%s %s\n\n%s\n\n%s" % [ request.method.to_s.upcase, request.uri.to_s, headers, JSON.pretty_generate(body) ]
162
+ end
163
+
164
+ def payload_matcher
165
+ @payload_matcher ||= JsonExpressions::Matcher.new(json_expression)
166
+ end
167
+
168
+ def all_stubbed_requests
169
+ WebMock::RequestRegistry.instance.requested_signatures
170
+ end
171
+
172
+ end
173
+
174
+ class ResponseMatcher
175
+
176
+ attr_reader :json_expression, :response, :body
177
+
178
+ def initialize(json_expression)
179
+ @json_expression = json_expression
180
+ @status = 200
181
+ end
182
+
183
+ def matches?(response)
184
+ @response = response
185
+ @body = JSON.parse(response.body)
186
+ @content_type = response.headers["Content-Type"]
187
+
188
+ @is_json = (@content_type.to_s.split(";",2).first =~ /\bjson$/)
189
+ @json_match = (payload_matcher =~ @body)
190
+ @status_match = (@status == response.status)
191
+
192
+ @status_match && @is_json && @json_match
193
+ end
194
+
195
+ def does_not_match?(response)
196
+ fail "Inverted matching is not implemented with this matcher"
197
+ end
198
+
199
+ def failure_message
200
+ if !@is_json
201
+ "Content-Type is not JSON.\n\n%s\n\nContent-Type is %s" % [ show_response, @content_type.inspect ]
202
+ elsif !@status_match
203
+ "Unexpected response status.\n\n%s\n\nExpected response status to be %s, but was %s." % [ show_response, @status.inspect, response.status ]
204
+ else
205
+ "Response body did not match.\n\n%s\n\n%s" % [ show_response, payload_matcher.last_error ]
206
+ end
207
+ end
208
+
209
+ def payload_matcher
210
+ @payload_matcher ||= JsonExpressions::Matcher.new(json_expression)
211
+ end
212
+
213
+ def show_response
214
+ headers = Dalziel.format_headers(response.headers)
215
+ Dalziel.indent "HTTP/1.1 %s\n\n%s\n\n%s" % [ response.status, headers, JSON.pretty_generate(body) ]
216
+ end
217
+
218
+ def status(code)
219
+ @status = code
220
+ self
221
+ end
222
+
223
+ end
224
+
225
+ end
226
+ end
227
+
228
+ if defined?(RSpec)
229
+ RSpec.configure do |config|
230
+ config.include Dalziel::Matchers
231
+ end
232
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dalziel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - iain
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: json_expressions
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.8'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.3'
83
+ description: Convenience gem for testing JSON API calls
84
+ email:
85
+ - iain@iain.nl
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - README.md
95
+ - Rakefile
96
+ - bin/console
97
+ - bin/setup
98
+ - dalziel.gemspec
99
+ - lib/dalziel.rb
100
+ - lib/dalziel/version.rb
101
+ homepage: https://github.com/iain/dalziel
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.5.1
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Convenience gem for testing JSON API calls
125
+ test_files: []