airborne 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e0383ebeb5ffd4ec46857517f5d06acd73a387c1
4
+ data.tar.gz: a1f56fa04c0229e5979b42b3e47afead49e3c525
5
+ SHA512:
6
+ metadata.gz: cc23450cf9d2c3004adc11fc6e88b7efdde5d203b3002e9239bb5fb5d5b59ecac2ccc3d6b2c6def8b1335684036c58eabbf4e31c9af3f5b4c6442bbcf4bc5057
7
+ data.tar.gz: 83b5725ba4b99e0c7c7d2a2a477f2cbe211975f453737a2364df7bf8410f2eb540edef692bd1a81108d9390fc4a1a1fbfcb32b5b2cb262f2ef78e078eef43034
@@ -0,0 +1,36 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
35
+
36
+ .DS_store
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ - 2.1.1
6
+
7
+
8
+ script: bundle exec rspec spec
9
+ before_install:
10
+ - gem update --system
11
+ - gem --version
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rspec'
4
+ gem 'rest-client'
5
+
6
+ group :test do
7
+ gem 'webmock'
8
+ end
@@ -0,0 +1,36 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.3.6)
5
+ crack (0.4.2)
6
+ safe_yaml (~> 1.0.0)
7
+ diff-lcs (1.2.5)
8
+ mime-types (2.3)
9
+ netrc (0.7.7)
10
+ rest-client (1.7.2)
11
+ mime-types (>= 1.16, < 3.0)
12
+ netrc (~> 0.7)
13
+ rspec (3.1.0)
14
+ rspec-core (~> 3.1.0)
15
+ rspec-expectations (~> 3.1.0)
16
+ rspec-mocks (~> 3.1.0)
17
+ rspec-core (3.1.3)
18
+ rspec-support (~> 3.1.0)
19
+ rspec-expectations (3.1.1)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.1.0)
22
+ rspec-mocks (3.1.0)
23
+ rspec-support (~> 3.1.0)
24
+ rspec-support (3.1.0)
25
+ safe_yaml (1.0.3)
26
+ webmock (1.18.0)
27
+ addressable (>= 2.3.6)
28
+ crack (>= 0.3.2)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ rest-client
35
+ rspec
36
+ webmock
data/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2011-2014 brooklyndev, sethpollack
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,209 @@
1
+ # Airborne
2
+
3
+ [![airborne travis](http://img.shields.io/travis/brooklynDev/airborne.svg?branch=master&style=flat)](https://travis-ci.org/brooklynDev/airborne)
4
+ [![airborne gem version](http://img.shields.io/gem/v/airborne.svg?style=flat)](http://rubygems.org/gems/airborne)
5
+ [![airbore gem downloads](http://img.shields.io/gem/dt/airborne.svg?style=flat)](http://rubygems.org/gems/airborne)
6
+ [![airborne gem stable downloads](http://img.shields.io/gem/dv/airborne/stable.svg?style=flat)](http://rubygems.org/gems/airborne)
7
+
8
+ RSpec driven API testing framework inspired by [frisby.js](https://github.com/vlucas/frisby)
9
+
10
+ ## Installation
11
+ Install Airborne:
12
+
13
+ gem install airborne
14
+
15
+ ##Creating Tests
16
+
17
+ require 'airborne'
18
+
19
+ describe 'sample spec' do
20
+ it 'should validate types' do
21
+ get 'http://example.com/api/v1/simple_get' #json api that returns { "name" : "John Doe" }
22
+ expect_json_types({name: :string})
23
+ end
24
+
25
+ it 'should validate values' do
26
+ get 'http://example.com/api/v1/simple_get' #json api that returns { "name" : "John Doe" }
27
+ expect_json({:name => "John Doe"})
28
+ end
29
+ end
30
+
31
+ When calling expect_json_types, these are the valid types that can be tested against:
32
+
33
+ * `:int` or `:integer`
34
+ * `:float`
35
+ * `:bool` or `:boolean`
36
+ * `:string`
37
+ * `:object`
38
+ * `:array`
39
+ * `:array_of_integers` or `:array_of_ints`
40
+ * `:array_of_floats`
41
+ * `:array_of_strings`
42
+ * `:array_of_booleans` or `:array_of_bools`
43
+ * `:array_of_objects`
44
+ * `:array_of_arrays`
45
+
46
+ If the properties are optional and may not appear in the response, you can append `_or_null` to the types above.
47
+
48
+ describe 'sample spec' do
49
+ it 'should validate types' do
50
+ get 'http://example.com/api/v1/simple_get' #json api that returns { "name" : "John Doe" } or { "name" : "John Doe", "age" : 45 }
51
+ expect_json_types({name: :string, age: :int_or_null})
52
+ end
53
+ end
54
+
55
+ When calling `expect_json`, you can optionally provide a block and run your own `rspec` expectations:
56
+
57
+ describe 'sample spec' do
58
+ it 'should validate types' do
59
+ get 'http://example.com/api/v1/simple_get' #json api that returns { "name" : "John Doe" }
60
+ expect_json({name: lambda |name| expect(name.length).to eq(8)})
61
+ end
62
+ end
63
+
64
+
65
+ ##Making requests
66
+ Airborne uses `rest_client` to make the HTTP request, and supports all HTTP verbs. When creating a test, you can call any of the following methods: `get`, `post`, `put`, `patch`, `delete`. This will then give you access the following properties:
67
+
68
+ * `response` - The HTTP response returned from the request
69
+ * `headers` - A symbolized hash of the response headers returned by the request
70
+ * `body` - The raw HTTP body returned from the request
71
+ * `json_body` - A symbolized hash representation of the JSON returned by the request
72
+
73
+ When calling any of the methods above, you can pass request headers to be used.
74
+
75
+ get 'http://example.com/api/v1/my_api', {'x-auth-token' => 'my_token'}
76
+
77
+ For requests that require a body (`post`, `put`, `patch`) you can pass the body as a hash as well:
78
+
79
+ post 'http://example.com/api/v1/my_api', {:name => 'John Doe'}, {'x-auth-token' => 'my_token'}
80
+
81
+ ##API
82
+ * `expect_json_types` - Tests the types of the JSON property values returned
83
+ * `expect_json` - Tests the values of the JSON property values returned
84
+ * `expect_json_keys` - Tests the existence of the specified keys in the JSON object
85
+ * `expect_status` - Tests the HTTP status code returned
86
+ * `expect_header` - Tests for a specified header in the response
87
+ * `expect_header_contains` - Partial match test on a specified header
88
+
89
+ ##Path Matching
90
+ When calling `expect_json_types`, `expect_json` or `expect_json_keys` you can optionaly specify a path as a first parameter.
91
+
92
+ For example, if our API returns the following JSON:
93
+
94
+ {
95
+ "name": "Alex",
96
+ "address": {
97
+ "street": "Area 51",
98
+ "city": "Roswell",
99
+ "state": "NM",
100
+ "coordinates":{
101
+ "lattitude": 33.3872,
102
+ "longitude": 104.5281
103
+ }
104
+ }
105
+ }
106
+
107
+ This test would only test the address object:
108
+
109
+ describe 'path spec' do
110
+ it 'should allow simple path and verify only that path' do
111
+ get 'http://example.com/api/v1/simple_path_get'
112
+ expect_json_types('address', {street: :string, city: :string, state: :string, coordinates: :object })
113
+ #or this
114
+ expect_json_types('address', {street: :string, city: :string, state: :string, coordinates: { lattitude: :float, longitude: :float } })
115
+ end
116
+ end
117
+
118
+ Alternativley, if we only want to test `coordinates` we can dot into just the `coordinates`:
119
+
120
+ it 'should allow nested paths' do
121
+ get 'http://example.com/api/v1/simple_path_get'
122
+ expect_json('address.coordinates', {lattitude: 33.3872, longitutde: 104.5281} )
123
+ end
124
+
125
+ When dealing with `arrays`, we can optionally test all (`*`) or a single (`?` - any, `0` - index) element of the array:
126
+
127
+ Given the following JSON:
128
+
129
+ {
130
+ "cars":[
131
+ {"make": "Tesla", "model": "Model S"},
132
+ {"make": "Lamborghini", "model": "Aventador"}
133
+ ]
134
+ }
135
+
136
+ We can test against just the first car like this:
137
+
138
+ it 'should index into array and test against specific element' do
139
+ get '/array_api'
140
+ expect_json('cars.0', {make: "Tesla", model: "Model S"})
141
+ end
142
+
143
+ To test the types of all elements in the array:
144
+
145
+ it 'should test all elements of the array' do
146
+ get 'http://example.com/api/v1/array_api
147
+ expect_json('cars.?', {make: "Tesla", model: "Model S"}) # tests that one car in array matches the tesla
148
+ expect_json_types('cars.*', {make: :string, model: :string}) # tests all cars in array for make and model of type string
149
+ end
150
+
151
+ `*` and `?` work for nested arrays as well. Given the following JSON:
152
+
153
+ {
154
+ "cars": [{
155
+ "make": "Tesla",
156
+ "model": "Model S",
157
+ "owners": [{
158
+ "name": "Bart Simpson"
159
+ }]
160
+ }, {
161
+ "make": "Lamborghini",
162
+ "model": "Aventador",
163
+ "owners": [{
164
+ "name": "Peter Griffin"
165
+ }]
166
+ }]
167
+ }
168
+
169
+ ===
170
+
171
+ it 'should check all nested arrays for specified elements' do
172
+ get 'http://example.com/api/v1/array_with_nested'
173
+ expect_json_types('cars.*.owners.*', {name: :string})
174
+ end
175
+
176
+
177
+ ##Configuration
178
+
179
+ When setting up Airborne, you can call `configure` just like you would with `rspec`:
180
+
181
+ Airborne.configure.do |config| #config is the RSpec configuration and can be used just like it
182
+ config.include MyModule
183
+ end
184
+
185
+ Additionally, you can specify a `base_url` and default `headers` to be used on every request (unless overriden in the actual request):
186
+
187
+ Airborne.configure.do |config|
188
+ config.base_url = 'http://example.com/api/v1'
189
+ config.headers = {'x-auth-token' => 'my_token'}
190
+ end
191
+
192
+ describe 'spec' do
193
+ it 'now we no longer need the full url' do
194
+ get '/simple_get'
195
+ expect_json_types({name: :string})
196
+ end
197
+ end
198
+
199
+ ## License
200
+
201
+ The MIT License
202
+
203
+ Copyright (c) 2011-2014 brooklyndev, sethpollack
204
+
205
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
206
+
207
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
208
+
209
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'airborne'
3
+ s.version = '0.0.14'
4
+ s.date = '2014-09-12'
5
+ s.summary = "RSpec driven API testing framework"
6
+ s.authors = ["Alex Friedman", "Seth Pollack"]
7
+ s.email = ['a.friedman07@gmail.com', 'teampollack@gmail.com']
8
+ s.require_paths = ['lib']
9
+ s.files = `git ls-files`.split("\n")
10
+ s.license = 'MIT'
11
+ s.add_runtime_dependency 'rspec', '~> 3.1', '>= 3.1.0'
12
+ s.add_runtime_dependency 'rest-client', '~> 1.7', '>= 1.7.2'
13
+ s.add_development_dependency 'webmock', '~> 0'
14
+ end
@@ -0,0 +1,9 @@
1
+ require 'airborne/path_matcher'
2
+ require 'airborne/request_expectations'
3
+ require 'airborne/base'
4
+
5
+ RSpec.configure do |config|
6
+ config.include Airborne
7
+ config.add_setting :base_url
8
+ config.add_setting :headers
9
+ end
@@ -0,0 +1,90 @@
1
+ require 'rest_client'
2
+ require 'json'
3
+
4
+ module Airborne
5
+ include RequestExpectations
6
+
7
+ def self.configure
8
+ RSpec.configure do |config|
9
+ yield config
10
+ end
11
+ end
12
+
13
+ def self.configuration
14
+ RSpec.configuration
15
+ end
16
+
17
+ def get(url, headers = nil)
18
+ make_request(:get, url, {headers: headers})
19
+ end
20
+
21
+ def post(url, post_body = nil, headers = nil)
22
+ make_request(:post, url, {body: post_body, headers: headers})
23
+ end
24
+
25
+ def patch(url, patch_body = nil, headers = nil )
26
+ make_request(:patch, url, {body: patch_body, headers: headers})
27
+ end
28
+
29
+ def put(url, put_body = nil, headers = nil )
30
+ make_request(:put, url, {body: put_body, headers: headers})
31
+ end
32
+
33
+ def delete(url, headers = nil)
34
+ make_request(:delete, url, {headers: headers})
35
+ end
36
+
37
+ def response
38
+ @response
39
+ end
40
+
41
+ def headers
42
+ @headers
43
+ end
44
+
45
+ def body
46
+ @body
47
+ end
48
+
49
+ def json_body
50
+ @json_body
51
+ end
52
+
53
+ private
54
+
55
+ def make_request(method, url, options = {})
56
+ headers = options[:headers] || {}
57
+ base_headers = Airborne.configuration.headers || {}
58
+ headers = base_headers.merge(headers)
59
+ res = if method == :post || method == :patch || method == :put
60
+ begin
61
+ RestClient.send(method, get_url(url), options[:body].nil? ? "" : options[:body], headers)
62
+ rescue RestClient::Exception => e
63
+ e.response
64
+ end
65
+ else
66
+ begin
67
+ RestClient.send(method, get_url(url), headers)
68
+ rescue RestClient::Exception => e
69
+ e.response
70
+ end
71
+
72
+ end
73
+ set_response(res)
74
+ end
75
+
76
+ def get_url(url)
77
+ base = Airborne.configuration.base_url || ""
78
+ base + url
79
+ end
80
+
81
+ def set_response(res)
82
+ @response = res
83
+ @body = res.body
84
+ @headers = res.headers
85
+ begin
86
+ @json_body = JSON.parse(res.body, symbolize_names: true) unless res.body == ""
87
+ rescue
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,45 @@
1
+ module Airborne
2
+ module PathMatcher
3
+ def get_by_path(path, json, &block)
4
+ type = false
5
+ parts = path.split('.')
6
+ parts.each_with_index do |part, index|
7
+ if part == '*' || part == '?'
8
+ type = part
9
+ raise "Expected #{path} to be array got #{json.class} from JSON response" unless json.class == Array
10
+ if index < parts.length - 1
11
+ json.each do |element|
12
+ sub_path = parts[(index + 1)..(parts.length-1)].join('.')
13
+ get_by_path(sub_path, element, &block)
14
+ end
15
+ return
16
+ end
17
+ next
18
+ end
19
+ if /^[\d]+(\.[\d]+){0,1}$/ === part
20
+ part = part.to_i
21
+ json = json[part]
22
+ else
23
+ json = json[part.to_sym]
24
+ raise "Expected #{path} to be object or array got #{json.class} from JSON response" unless json.class == Array || json.class == Hash
25
+ end
26
+ end
27
+ if type == '*'
28
+ json.each{|part| yield part}
29
+ elsif type == '?'
30
+ item_count = json.length
31
+ error_count = 0
32
+ json.each do |part|
33
+ begin
34
+ yield part
35
+ rescue Exception => e
36
+ error_count += 1
37
+ raise "Expected one object in path #{path} to match provided JSON values" if item_count == error_count
38
+ end
39
+ end
40
+ else
41
+ yield json
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,118 @@
1
+ require 'rspec'
2
+
3
+ module Airborne
4
+ module RequestExpectations
5
+ include RSpec
6
+ include PathMatcher
7
+
8
+ def expect_json_types(*args)
9
+ call_with_path(args) do |param, body|
10
+ expect_json_types_impl(param, body)
11
+ end
12
+ end
13
+
14
+ def expect_json(*args)
15
+ call_with_path(args) do |param, body|
16
+ expect_json_impl(param, body)
17
+ end
18
+ end
19
+
20
+ def expect_json_keys(*args)
21
+ call_with_path(args) do |param, body|
22
+ expect(body.keys).to include(*param)
23
+ end
24
+ end
25
+
26
+ def expect_status(code)
27
+ expect(response.code).to eq(code)
28
+ end
29
+
30
+ def expect_header(key, content)
31
+ header = headers[key]
32
+ expect(header).to_not be_nil
33
+ if header
34
+ expect(header.downcase).to eq(content.downcase)
35
+ else
36
+ raise "Header #{key} not present in HTTP response"
37
+ end
38
+ end
39
+
40
+ def expect_header_contains(key, content)
41
+ header = headers[key]
42
+ expect(header).to_not be_nil
43
+ if header
44
+ expect(header.downcase).to include(content.downcase)
45
+ else
46
+ raise "Header #{key} not present in HTTP response"
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def call_with_path(args)
53
+ if args.length == 2
54
+ get_by_path(args[0], json_body) do|json_chunk|
55
+ yield(args[1], json_chunk)
56
+ end
57
+ else
58
+ yield(args[0], json_body)
59
+ end
60
+ end
61
+
62
+ def get_mapper
63
+ base_mapper = {
64
+ integer: [Fixnum,Bignum],
65
+ array_of_integers: [Fixnum,Bignum],
66
+ int: [Fixnum,Bignum],
67
+ array_of_ints: [Fixnum,Bignum],
68
+ float: [Float,Fixnum,Bignum],
69
+ array_of_floats: [Float,Fixnum,Bignum],
70
+ string: [String],
71
+ array_of_strings: [String],
72
+ boolean: [TrueClass, FalseClass],
73
+ array_of_booleans: [TrueClass, FalseClass],
74
+ bool: [TrueClass, FalseClass],
75
+ array_of_bools: [TrueClass, FalseClass],
76
+ object: [Hash],
77
+ array_of_objects: [Hash],
78
+ array: [Array],
79
+ array_of_arrays: [Array]
80
+ }
81
+
82
+ mapper = base_mapper.clone
83
+ base_mapper.each do |key, value|
84
+ mapper[(key.to_s + "_or_null").to_sym] = value + [NilClass]
85
+ end
86
+ mapper
87
+ end
88
+
89
+ def expect_json_types_impl(expectations, hash)
90
+ @mapper ||= get_mapper
91
+ expectations.each do |prop_name, expected_type|
92
+ value = hash[prop_name]
93
+ if expected_type.class == Hash
94
+ expect_json_types_impl(expected_type, value)
95
+ elsif expected_type.to_s.include?("array_of")
96
+ expect(value.class).to eq(Array), "Expected #{prop_name} to be of type #{expected_type}, got #{value.class} instead"
97
+ value.each do |val|
98
+ expect(@mapper[expected_type].include?(val.class)).to eq(true), "Expected #{prop_name} to be of type #{expected_type}, got #{val.class} instead"
99
+ end
100
+ else
101
+ expect(@mapper[expected_type].include?(value.class)).to eq(true), "Expected #{prop_name} to be of type #{expected_type}, got #{value.class} instead"
102
+ end
103
+ end
104
+ end
105
+
106
+ def expect_json_impl(expectations, hash)
107
+ expectations.each do |prop_name, expected_value|
108
+ actual_value = hash[prop_name]
109
+ if(expected_value.class == Hash)
110
+ expect_json_impl(expected_value, actual_value)
111
+ else
112
+ expect(expected_value).to eq(actual_value)
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'base spec' do
4
+ it 'when request is made response should be set' do
5
+ mock_get('simple_get')
6
+ get '/simple_get'
7
+ expect(response).to_not be(nil)
8
+ end
9
+
10
+ it 'when request is made headers should be set' do
11
+ mock_get('simple_get')
12
+ get '/simple_get'
13
+ expect(headers).to_not be(nil)
14
+ end
15
+
16
+ it 'when request is made headers should be symbolized hash' do
17
+ mock_get('simple_get', {'Content-Type' => 'application/json'})
18
+ get '/simple_get'
19
+ expect(headers).to be_kind_of(Hash)
20
+ expect(headers.first[0]).to be_kind_of(Symbol)
21
+ end
22
+
23
+ it 'when request is made body should be set' do
24
+ mock_get('simple_get')
25
+ get '/simple_get'
26
+ expect(body).to_not be(nil)
27
+ end
28
+
29
+ it 'when request is made json body should be symbolized hash' do
30
+ mock_get('simple_get')
31
+ get '/simple_get'
32
+ expect(json_body).to be_kind_of(Hash)
33
+ expect(json_body.first[0]).to be_kind_of(Symbol)
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'delete' do
4
+ it 'should allow testing on delete requests' do
5
+ mock_delete 'simple_delete'
6
+ delete '/simple_delete'
7
+ expect_status(200)
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'expect_json_keys' do
4
+ it 'should fail when json keys are missing' do
5
+ mock_get('simple_json')
6
+ get '/simple_json', {}
7
+ expect{expect_json_keys([:foo, :bar, :baz, :bax])}.to raise_error
8
+ end
9
+
10
+ it 'should ensure correct json keys' do
11
+ mock_get('simple_json')
12
+ get '/simple_json', {}
13
+ expect_json_keys([:foo, :bar, :baz])
14
+ end
15
+
16
+ it 'should ensure correct partial json keys' do
17
+ mock_get('simple_json')
18
+ get '/simple_json', {}
19
+ expect_json_keys([:foo, :bar])
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'expect_json' do
4
+ it 'should ensure correct json values' do
5
+ mock_get('simple_get')
6
+ get '/simple_get'
7
+ expect_json({name: "Alex", age: 32 })
8
+ end
9
+
10
+ it 'should fail when incorrect json is tested' do
11
+ mock_get('simple_get')
12
+ get '/simple_get'
13
+ expect{expect_json({bad: "data"})}.to raise_error
14
+ end
15
+
16
+ it 'should allow simple path and verify only that path' do
17
+ mock_get('simple_path_get')
18
+ get '/simple_path_get'
19
+ expect_json('address', {street: "Area 51", city: "Roswell", state: "NM"})
20
+ end
21
+
22
+ it 'should allow nested paths' do
23
+ mock_get('simple_nested_path')
24
+ get '/simple_nested_path'
25
+ expect_json('address.coordinates', {lattitude: 33.3872, longitutde: 104.5281} )
26
+ end
27
+
28
+ it 'should index into array and test against specific element' do
29
+ mock_get('array_with_index')
30
+ get '/array_with_index'
31
+ expect_json('cars.0', {make: "Tesla", model: "Model S"})
32
+ end
33
+
34
+ it 'should test against all elements in the array' do
35
+ mock_get('array_with_index')
36
+ get '/array_with_index'
37
+ expect_json('cars.?', {make: "Tesla", model: "Model S"})
38
+ expect_json('cars.?', {make: "Lamborghini", model: "Aventador"})
39
+ end
40
+
41
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'expect_json_types' do
4
+ it 'should detect current type' do
5
+ mock_get('simple_get')
6
+ get '/simple_get'
7
+ expect_json_types({name: :string, age: :int})
8
+ end
9
+
10
+ it 'should fail when incorrect json types tested' do
11
+ mock_get('simple_get')
12
+ get '/simple_get'
13
+ expect{expect_json_types({bad: :bool})}.to raise_error
14
+ end
15
+
16
+ it 'should not fail when optional property is not present' do
17
+ mock_get('simple_get')
18
+ get '/simple_get'
19
+ expect_json_types({name: :string, age: :int, optional: :bool_or_null })
20
+ end
21
+
22
+ it 'should allow simple path and verify only that path' do
23
+ mock_get('simple_path_get')
24
+ get '/simple_path_get'
25
+ expect_json_types('address', {street: :string, city: :string, state: :string})
26
+ end
27
+
28
+ it 'should allow nested paths' do
29
+ mock_get('simple_nested_path')
30
+ get '/simple_nested_path'
31
+ expect_json_types('address.coordinates', {lattitude: :float, longitutde: :float} )
32
+ end
33
+
34
+ it 'should index into array and test against specific element' do
35
+ mock_get('array_with_index')
36
+ get '/array_with_index'
37
+ expect_json_types('cars.0', {make: :string, model: :string})
38
+ end
39
+
40
+ it 'should test against all elements in the array' do
41
+ mock_get('array_with_index')
42
+ get '/array_with_index'
43
+ expect_json_types('cars.*', {make: :string, model: :string})
44
+ end
45
+
46
+ it 'should ensure all elements of array are valid' do
47
+ mock_get('array_with_index')
48
+ get '/array_with_index'
49
+ expect{expect_json_types('cars.*', {make: :string, model: :int})}.to raise_error
50
+ end
51
+
52
+ it 'should check all nested arrays for specified elements' do
53
+ mock_get('array_with_nested')
54
+ get '/array_with_nested'
55
+ expect_json_types('cars.*.owners.*', {name: :string})
56
+ end
57
+
58
+ it 'should ensure all nested arrays contain correct data' do
59
+ mock_get('array_with_nested_bad_data')
60
+ get '/array_with_nested_bad_data'
61
+ expect{expect_json_types('cars.*.owners.*', {name: :string})}.to raise_error
62
+ end
63
+
64
+ it 'should check all types in a simple array' do
65
+ mock_get('array_of_values')
66
+ get '/array_of_values'
67
+ expect_json_types({grades: :array_of_ints})
68
+ end
69
+
70
+ it 'should ensure all valid types in a simple array' do
71
+ mock_get('array_of_values')
72
+ get '/array_of_values'
73
+ expect{expect_json_types({bad: :array_of_ints})}.to raise_error
74
+ end
75
+
76
+ it 'should deep symbolize array responses' do
77
+ mock_get('array_response')
78
+ get '/array_response'
79
+ expect_json_types("*", {name: :string})
80
+ end
81
+
82
+ it 'should allow empty array' do
83
+ mock_get('array_of_values')
84
+ get '/array_of_values'
85
+ expect_json_types({emptyArray: :array_of_ints})
86
+ end
87
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'expect_status' do
4
+ it 'should verify correct status code' do
5
+ mock_get('simple_get')
6
+ get '/simple_get'
7
+ expect_status(200)
8
+ end
9
+
10
+ it 'should fail when incorrect status code is returned' do
11
+ mock_get('simple_get')
12
+ get '/simple_get'
13
+ expect{expect_status(123)}.to raise_error
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'expect header' do
4
+ it 'should find exact match for header content' do
5
+ mock_get('simple_get', {'Content-Type' => 'application/json'})
6
+ get '/simple_get'
7
+ expect_header(:content_type, 'application/json')
8
+ end
9
+
10
+ it 'should ensure correct headers are present' do
11
+ mock_get('simple_get', {'Content-Type' => 'application/json'})
12
+ get '/simple_get'
13
+ expect{expect_header(:foo, 'bar')}.to raise_error
14
+ end
15
+ end
16
+
17
+ describe 'expect header contains' do
18
+ it 'should ensure partial header match exists' do
19
+ mock_get('simple_get', {'Content-Type' => 'application/json'})
20
+ get '/simple_get'
21
+ expect_header_contains(:content_type, 'json')
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'patch' do
4
+ it 'should allow testing on patch requests' do
5
+ mock_patch('simple_patch')
6
+ patch '/simple_patch', {}
7
+ expect_json_types({status: :string, someNumber: :int})
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'post' do
4
+ it 'should allow testing on post requests' do
5
+ mock_post('simple_post')
6
+ post '/simple_post', {}
7
+ expect_json_types({status: :string, someNumber: :int})
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'put' do
4
+ it 'should allow testing on put requests' do
5
+ mock_put('simple_put')
6
+ put '/simple_put', {}
7
+ expect_json_types({status: :string, someNumber: :int})
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ require 'airborne'
2
+ require 'stub_helper'
3
+
4
+ Airborne.configure do |config|
5
+ config.base_url = 'http://www.example.com'
6
+ config.include StubHelper
7
+ end
8
+
@@ -0,0 +1,37 @@
1
+ require 'webmock/rspec'
2
+
3
+ module StubHelper
4
+
5
+ def initialize
6
+ @base_url = 'http://www.example.com/'
7
+ end
8
+
9
+ def mock_get(url, response_headers = {})
10
+ stub_request(:get, @base_url + url).to_return(headers: response_headers, body: get_json_response_file(url))
11
+ end
12
+
13
+ def mock_post(url, options = {})
14
+ stub_request(:post, @base_url + url).with(body: options[:request_body] || {})
15
+ .to_return(headers: options[:response_headers] || {}, body: get_json_response_file(url))
16
+ end
17
+
18
+ def mock_put(url, options = {})
19
+ stub_request(:put, @base_url + url).with(body: options[:request_body] || {})
20
+ .to_return(headers: options[:response_headers] || {}, body: get_json_response_file(url))
21
+ end
22
+
23
+ def mock_patch(url, options = {})
24
+ stub_request(:patch, @base_url + url).with(body: options[:request_body] || {})
25
+ .to_return(headers: options[:response_headers] || {}, body: get_json_response_file(url))
26
+ end
27
+
28
+ def mock_delete(url)
29
+ stub_request(:delete, @base_url + url)
30
+ end
31
+
32
+ private
33
+
34
+ def get_json_response_file(name)
35
+ IO.read(File.join('spec/test_responses', name + ".json"))
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ {
2
+ "grades":[89,65,100,92],
3
+ "bad": [89,67,"foo"],
4
+ "emptyArray": []
5
+ }
@@ -0,0 +1,5 @@
1
+ [
2
+ {
3
+ "name":"Seth"
4
+ }
5
+ ]
@@ -0,0 +1,6 @@
1
+ {
2
+ "cars":[
3
+ {"make": "Tesla", "model": "Model S"},
4
+ {"make": "Lamborghini", "model": "Aventador"}
5
+ ]
6
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cars": [{
3
+ "make": "Tesla",
4
+ "model": "Model S",
5
+ "owners": [{
6
+ "name": "Bart Simpson"
7
+ }]
8
+ }, {
9
+ "make": "Lamborghini",
10
+ "model": "Aventador",
11
+ "owners": [{
12
+ "name": "Peter Griffin"
13
+ }]
14
+ }]
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cars": [{
3
+ "make": "Tesla",
4
+ "model": "Model S",
5
+ "owners": [{
6
+ "name": "Bart Simpson"
7
+ }]
8
+ }, {
9
+ "make": "Lamborghini",
10
+ "model": "Aventador",
11
+ "owners": [{
12
+ "name": 123
13
+ }]
14
+ }]
15
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "Alex",
3
+ "age": 32
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "foo": "foo",
3
+ "bar": "bar",
4
+ "baz": "baz"
5
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "Alex",
3
+ "address": {
4
+ "street": "Area 51",
5
+ "city": "Roswell",
6
+ "state": "NM",
7
+ "coordinates":{
8
+ "lattitude": 33.3872,
9
+ "longitutde": 104.5281
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "status": "ok",
3
+ "someNumber": 100
4
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "Alex",
3
+ "address": {
4
+ "street": "Area 51",
5
+ "city": "Roswell",
6
+ "state": "NM"
7
+ }
8
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "status": "ok",
3
+ "someNumber": 100
4
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "status": "ok",
3
+ "someNumber": 100
4
+ }
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: airborne
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.14
5
+ platform: ruby
6
+ authors:
7
+ - Alex Friedman
8
+ - Seth Pollack
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-09-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 3.1.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '3.1'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.0
34
+ - !ruby/object:Gem::Dependency
35
+ name: rest-client
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.7.2
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '1.7'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.7.2
54
+ - !ruby/object:Gem::Dependency
55
+ name: webmock
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ description:
69
+ email:
70
+ - a.friedman07@gmail.com
71
+ - teampollack@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - LICENSE
81
+ - README.md
82
+ - airborne.gemspec
83
+ - lib/airborne.rb
84
+ - lib/airborne/base.rb
85
+ - lib/airborne/path_matcher.rb
86
+ - lib/airborne/request_expectations.rb
87
+ - spec/airborne/base_spec.rb
88
+ - spec/airborne/delete_spec.rb
89
+ - spec/airborne/expect_json_keys_spec.rb
90
+ - spec/airborne/expect_json_spec.rb
91
+ - spec/airborne/expect_json_types_spec.rb
92
+ - spec/airborne/expect_status_spec.rb
93
+ - spec/airborne/headers_spec.rb
94
+ - spec/airborne/patch_spec.rb
95
+ - spec/airborne/post_spec.rb
96
+ - spec/airborne/put_spec.rb
97
+ - spec/spec_helper.rb
98
+ - spec/stub_helper.rb
99
+ - spec/test_responses/array_of_values.json
100
+ - spec/test_responses/array_response.json
101
+ - spec/test_responses/array_with_index.json
102
+ - spec/test_responses/array_with_nested.json
103
+ - spec/test_responses/array_with_nested_bad_data.json
104
+ - spec/test_responses/simple_get.json
105
+ - spec/test_responses/simple_json.json
106
+ - spec/test_responses/simple_nested_path.json
107
+ - spec/test_responses/simple_patch.json
108
+ - spec/test_responses/simple_path_get.json
109
+ - spec/test_responses/simple_post.json
110
+ - spec/test_responses/simple_put.json
111
+ homepage:
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.2.0
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: RSpec driven API testing framework
135
+ test_files: []