rspec-api-requests 0.7.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: 56121c96b1439ce85f3e8e8b31dda28b4c09f296
4
+ data.tar.gz: 59188cc6915288d1105851a14f5733e456a79e07
5
+ SHA512:
6
+ metadata.gz: b7c4402a820271a69898767bf48d56e31b6d26d7b81bf34f0d9c466f3963bc4b421250673dec31671a0dee6530d814ec8edf46d788762b6a9d2619ffa7af492a
7
+ data.tar.gz: e02f853e29ed5db826ed8fb73012a73db40414890265da21deadd382d4a0af1cc3986b0c4696e4efc13059abadf94ea77e6a5313b4e99583ef3bf8f051619f1d
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 claudiob
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ RSpec API Requests
2
+ ==================
3
+
4
+ RSpecApi::Requests lets you make requests to web APIs and verify their expectations:
5
+
6
+ params = {host: 'http://api.example.com', route: '/concerts', attributes: {where: {type: :string}}
7
+
8
+ describe 'GET /concerts', rspec_api_params: params do
9
+ respond_with :ok
10
+ end
11
+
12
+ More documentation and examples about RSpecApi are available at [http://rspec-api.github.io](http://rspec-api.github.io)
13
+
14
+ [![Build Status](https://travis-ci.org/rspec-api/rspec-api-requests.png?branch=master)](https://travis-ci.org/rspec-api/rspec-api-requests)
15
+ [![Code Climate](https://codeclimate.com/github/rspec-api/rspec-api-requests.png)](https://codeclimate.com/github/rspec-api/rspec-api-requests)
16
+ [![Coverage Status](https://coveralls.io/repos/rspec-api/rspec-api-requests/badge.png)](https://coveralls.io/r/rspec-api/rspec-api-requests)
17
+ [![Dependency Status](https://gemnasium.com/rspec-api/rspec-api-requests.png)](https://gemnasium.com/rspec-api/rspec-api-requests)
18
+
19
+
20
+ Basic example
21
+ -------------
22
+
23
+ # 1. Specify how to connect to the API and what to expect:
24
+ gists_params = {
25
+ host: 'https://api.github.com',
26
+ action: 'get',
27
+ authorize_with: {token: YOUR-GITHUB-TOKEN-HERE},
28
+ route: '/gists',
29
+ collection: true,
30
+ attributes: {url: {type: {string: :url}}},
31
+ extra_requests: [{
32
+ expect: {filter: {by: :updated_at, compare_with: :>=, value: '2012-10-10T00:00:00Z'}},
33
+ params: {since: '2012-10-10T00:00:00Z'}
34
+ }]
35
+ }
36
+
37
+ # 2. Use `respond_to` to send the request and verify the expectations
38
+ describe 'GitHub Gists', rspec_api_params: gists_params do
39
+ respond_with :ok
40
+ end
41
+
42
+ Running the example above returns the following successful output:
43
+
44
+ Examples from GitHub API
45
+ GitHub Gists
46
+ GET /gists with {:since=>"2012-10-10T00:00:00Z"}
47
+ responds with a body that
48
+ should be a collection
49
+ should have attributes {:url=>{:type=>{:string=>:url}}}
50
+ should be filtered by updated_at>=2012-10-10T00:00:00Z
51
+ responds with a status code that
52
+ should be 200
53
+ GET /gists
54
+ responds with a body that
55
+ should have attributes {:url=>{:type=>{:string=>:url}}}
56
+ should be a collection
57
+ responds with a status code that
58
+ should be 200
59
+
60
+ Finished in 0.01056 seconds
61
+ 7 examples, 0 failures
62
+
63
+ Note that, in order run the example, above, you need to replace
64
+ `YOUR-GITHUB-TOKEN-HERE` with your [GitHub Personal Access Token](https://github.com/settings/applications).
65
+
66
+
67
+ Available request options
68
+ =========================
69
+
70
+ RSpecApi::Requests makes available one method `respond_to`, which sends the
71
+ request to the API and verify the expectations specified using RSpec metadata
72
+ `rspec_api_params` in the surrounding `describe` block, as shown above.
73
+ The following are the valid request options for `rspec_api_params`.
74
+
75
+ :host and :adapter (*either one or the other is required*)
76
+ ----------------------------------------------------------
77
+
78
+ Respectively the URL where the API is hosted and the adapter to connect to it.
79
+ By default, RSpecApi::Requests uses `Net::HTTP`, e.g:
80
+
81
+ host: 'http://example.com' # => connect via Net::HTTP
82
+
83
+ This behavior can be overridden specifying a different adapter:
84
+
85
+ adapter: [:rack, app] # => connect via Rack::Test to a local Rack app
86
+
87
+ Therefore, RSpecApi::Requests can be used both to send requests to **remote**
88
+ and to **local** web API, making API development easier.
89
+
90
+ :route (*required*)
91
+ -------------------
92
+
93
+ The API route to access a resource, e.g.:
94
+
95
+ host: 'http://example.com', route: '/concerts'
96
+ # => connect to 'http://example.com/concerts'
97
+
98
+ :action (*required*)
99
+ --------------------
100
+
101
+ The HTTP method to access a resource, e.g.:
102
+
103
+ action: :delete, route: '/concerts'
104
+ # => sends 'DELETE /concerts'
105
+
106
+ :authorize_with (*optional*)
107
+ ----------------------------
108
+
109
+ The HTTP authentication payload to send with the request, e.g.:
110
+
111
+ authorize_with: {token: 'foo'}
112
+ # => sends [:token, 'foo'] as the HTTP authentication
113
+
114
+
115
+ Available expectations
116
+ ======================
117
+
118
+ The following are the valid expectations for `rspec_api_params`.
119
+
120
+ :collection (*optional*)
121
+ ------------------------
122
+
123
+ Whether the response body will include a *single* JSON object
124
+ (`collection: false`) or a *collection* of objects (`collection: true`), e.g.:
125
+
126
+ action: :get, route: '/concerts', collection: true
127
+ # => expect `GET /concerts' to return a collection
128
+
129
+ action: :get, route: '/concerts/1', collection: false
130
+ # => expect `GET /concerts/1' to return an object
131
+
132
+ :attributes (*optional*)
133
+ ------------------------
134
+
135
+ Which attributes the JSON object or collection in the response body will contain, e.g.:
136
+
137
+ action: :get, route: '/concerts/1', attributes: {url: {type: {string: :url}}}
138
+ # => expect `GET /concerts/1' to return an object with a 'url' field
139
+ # that should contain a URL-formatted string
140
+
141
+ Extra requests
142
+ ==============
143
+
144
+ The last option of `rspec_api_params` allows to send multiple API requests:
145
+
146
+ :extra_requests (*optional*)
147
+ ----------------------------
148
+
149
+ Which extra requests should be sent and what to expect from them, e.g.:
150
+
151
+ action: :get, route: '/concerts', extra_requests: [{
152
+ expect: {filter: {by: :created_at, compare_with: :>=, value: '2012-10-10T00:00:00Z'}},
153
+ params: {since: '2012-10-10T00:00:00Z'}
154
+ }]
155
+ # => expect `GET /concerts/1?since=2012-10-10T00:00:00Z' to only return
156
+ # concerts created after October 10th, 2012.
157
+
158
+
159
+ How to install
160
+ ==============
161
+
162
+ To install on your system, run `gem install rspec-api-requests`.
163
+ To use inside a bundled Ruby project, add this line to the Gemfile:
164
+
165
+ gem 'rspec-api-requests', '~> 0.7.0'
166
+
167
+ The rspec-api-requests gem follows [Semantic Versioning](http://semver.org).
168
+ Any new release that is fully backward-compatible bumps the *patch* version (0.0.x).
169
+ Any new version that breaks compatibility bumps the *minor* version (0.x.0)
170
+
171
+ Indicating the full version in your Gemfile (*major*.*minor*.*patch*) guarantees
172
+ that your project won’t occur in any error when you `bundle update` and a new
173
+ version of RSpecApi::Requests is released.
174
+
175
+
176
+ How to contribute
177
+ =================
178
+
179
+ Don’t hesitate to send me code comments, issues or pull requests through GitHub!
180
+
181
+ All feedback is appreciated. Thanks :)
@@ -0,0 +1 @@
1
+ require 'rspec-api/requests'
@@ -0,0 +1,21 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'faraday'
3
+ require 'faraday_middleware' # TODO: use autoload, we only need EncodeJson
4
+
5
+ module RSpecApi
6
+ module HttpClient
7
+ def send_request(options = {})
8
+ conn = Faraday.new options[:host] do |c|
9
+ c.use FaradayMiddleware::EncodeJson
10
+ c.adapter *(options.fetch :adapter, [:net_http])
11
+ end
12
+ conn.headers[:user_agent] = 'RSpec API'
13
+ conn.headers[:accept] = 'application/json'
14
+ conn.authorization *options[:authorize_with].flatten if options[:authorize_with]
15
+ sleep options[:throttle] if options[:throttle]
16
+ conn.send *options.values_at(:action, :route, :body)
17
+ rescue Faraday::Error::ConnectionFailed => e
18
+ pending "Could not connect: #{e}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,31 @@
1
+ require 'rspec/core'
2
+ require 'rspec-api/respond_with'
3
+
4
+ module RSpecApi
5
+ # Provides the `respond_with` method to RSpec Example groups, useful to
6
+ # make requests to web APIs and verify their expectations:
7
+ #
8
+ # To have these matchers available inside of an RSpec `describe` block,
9
+ # include that block inside a block with the `:rspec_api` metadata, or
10
+ # explicitly include the RSpecApi::Requests module.
11
+ #
12
+ # @example Tag a `describe` block as `:rspec_api`:
13
+ # describe "Artists", rspec_api: true do
14
+ # describe 'GET /artists', rspec_api_requests: {...}
15
+ # ... # here you can write `respond_with :ok`
16
+ # end
17
+ # end
18
+ #
19
+ # @example Explicitly include the RSpecApi::Responses module
20
+ # describe "Artists" do
21
+ # include RSpecApi::Requests
22
+ # describe 'GET /artists', rspec_api_requests: {...}
23
+ # ... # here you can write `respond_with :ok`
24
+ # end
25
+ # end
26
+ module Requests
27
+ include RespondWith
28
+ end
29
+ end
30
+
31
+ RSpec.configuration.extend RSpecApi::Requests, rspec_api: true
@@ -0,0 +1,15 @@
1
+ module RSpecApi
2
+ module Requests
3
+ module Pending
4
+ def pending_request
5
+ it {
6
+ pending <<-EOF
7
+ To use respond_with, specify the parameters for the request and
8
+ the expectations in the :rspec_api_params example metadata,
9
+ indicating the action, the route, and either host or adapter.
10
+ EOF
11
+ }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ require 'rspec-api/http_client'
2
+ require 'rspec-api/expectations'
3
+
4
+ module RSpecApi
5
+ module Requests
6
+ module Valid
7
+ include RSpecApi::HttpClient
8
+ include RSpecApi::Expectations
9
+
10
+ def valid_request(request = {}, expectations = {}, prefix_params = {}, &block)
11
+ response = send_request request
12
+ expect_response response, expectations, prefix_params, &block
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ module RSpecApi
2
+ module Requests
3
+ VERSION = '0.7.0'
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ require 'rspec-api/respond_with/metadata'
2
+ require 'rspec-api/respond_with/valid'
3
+ require 'rspec-api/respond_with/request'
4
+
5
+ module RSpecApi
6
+ module RespondWith
7
+ include Metadata
8
+ include Valid
9
+ include Request
10
+
11
+ def respond_with(status, values = {}, &block)
12
+ request_params = build_request_params
13
+ result = request status, request_params, values, &block
14
+
15
+ extra_requests(request_params).map do |extra_request|
16
+ body = values.merge extra_request.fetch(:params, {})
17
+ params = request_params.merge extra_request.fetch(:expect, {})
18
+ result = [*result] << request(status, params, body, &block)
19
+ end
20
+
21
+ result
22
+ end
23
+
24
+ private
25
+
26
+ def build_request_params
27
+ rspec_api_params.dup.tap do |params|
28
+ params[:route] = params[:route].dup if params[:route]
29
+ params[:extra] = {}
30
+ end
31
+ end
32
+
33
+ def extra_requests(params = {})
34
+ if valid?(params) && params[:collection]
35
+ params.fetch :extra_requests, {}
36
+ else
37
+ {}
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ module RSpecApi
2
+ module RespondWith
3
+ module Metadata
4
+ def rspec_api_params
5
+ metadata.fetch :rspec_api_params, {}
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ require 'rspec-api/respond_with/valid'
2
+ require 'rspec-api/requests/valid'
3
+ require 'rspec-api/requests/pending'
4
+
5
+ module RSpecApi
6
+ module RespondWith
7
+ module Request
8
+ include Valid
9
+
10
+ def request(status, params = {}, values = {}, &block)
11
+ valid = valid? params
12
+ expectations = expectations_for status, params
13
+ request, prefix_params = request_for params, values
14
+
15
+ describe description_for(request) do
16
+ if valid
17
+ extend RSpecApi::Requests::Valid
18
+ valid_request request, expectations, prefix_params, &block
19
+ else
20
+ extend RSpecApi::Requests::Pending
21
+ pending_request
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def request_for(params, values)
29
+ keys = [:host, :action, :adapter, :authorize_with, :throttle]
30
+ route, body, extra = interpolate params[:route], values, params[:extra]
31
+ [params.slice(*keys).merge(route: route, body: body), params[:extra]]
32
+ end
33
+
34
+ def expectations_for(status, params)
35
+ keys = [:collection, :attributes, :sort, :filter, :page_links, :callback]
36
+ params.slice(*keys).delete_if{|k, v| v.blank?}.tap do |expectations|
37
+ expectations[:status] = status
38
+ end
39
+ end
40
+
41
+ def description_for(opts = {})
42
+ "#{opts.fetch(:action, '').upcase} #{opts[:route]}".tap do |desc|
43
+ desc << " with #{opts[:body]}" if opts.fetch(:body, {}).any?
44
+ end
45
+ end
46
+
47
+ def interpolate(route, values = {}, extra = {})
48
+ [route, values, extra].tap do |route, values, extra|
49
+ route = route.tap do |route|
50
+ values.keys.each do |param|
51
+ values.delete(param).tap do |value|
52
+ route.gsub! "/:#{param}", "/#{value}"
53
+ extra[param] = value
54
+ end if route.match "/:#{param}"
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ module RSpecApi
2
+ module RespondWith
3
+ module Valid
4
+ def valid?(opts = {})
5
+ opts.any? && opts.key?(:route) && opts.key?(:action) && (opts.key?(:host) || opts.key?(:adapter))
6
+ end
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,182 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-api-requests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.0
5
+ platform: ruby
6
+ authors:
7
+ - claudiob
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-api-expectations
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.7.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: faraday_middleware
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
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rack-test
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
+ description: ''
140
+ email:
141
+ - claudiob@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - lib/rspec-api/http_client.rb
147
+ - lib/rspec-api/requests/pending.rb
148
+ - lib/rspec-api/requests/valid.rb
149
+ - lib/rspec-api/requests/version.rb
150
+ - lib/rspec-api/requests.rb
151
+ - lib/rspec-api/respond_with/metadata.rb
152
+ - lib/rspec-api/respond_with/request.rb
153
+ - lib/rspec-api/respond_with/valid.rb
154
+ - lib/rspec-api/respond_with.rb
155
+ - lib/rspec-api-requests.rb
156
+ - MIT-LICENSE
157
+ - README.md
158
+ homepage: https://github.com/rspec-api/rspec-api-requests
159
+ licenses:
160
+ - MIT
161
+ metadata: {}
162
+ post_install_message:
163
+ rdoc_options: []
164
+ require_paths:
165
+ - lib
166
+ required_ruby_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - '>='
169
+ - !ruby/object:Gem::Version
170
+ version: 1.9.3
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - '>='
174
+ - !ruby/object:Gem::Version
175
+ version: 1.3.6
176
+ requirements: []
177
+ rubyforge_project:
178
+ rubygems_version: 2.1.10
179
+ signing_key:
180
+ specification_version: 4
181
+ summary: ''
182
+ test_files: []