rspec-api-requests 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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: []