rspec-api 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c5aca9f3d178f7137fee5b2a36a6687031421977
4
- data.tar.gz: 59c9f1b202549efeaaa6d0f858da3ab8ffd9202e
3
+ metadata.gz: 5408552b4ac63e6bdbbd372c3c900c963f14ffbe
4
+ data.tar.gz: 53fa90f938352139f11677ae6b414fc32535ea47
5
5
  SHA512:
6
- metadata.gz: 7b1daaed33cc5842157eed50faadb3f7660fb65ac13d699a0d16589b9b2029a0cf7515f6c29ecf8ba79e2b9e572cfefeffbf6d4f87e1b3f10bed7954a2a549e7
7
- data.tar.gz: 506702acc31a909f7772a7e3ee15b0a66046fbff49f59f94f0408f6187b2b67448dc87c28cdd9939cddc0ab2f078b9ce28b47141b19f0004c89ac4b9ab95d738
6
+ metadata.gz: 8b86970f33d662320ac5189e82aa1117f1cf44d0ffe583a30c0cae3d4440181769e96cff3cfa7766fd75126d5227423931ffcbc0e3e9801bf123d776bbf53c58
7
+ data.tar.gz: dddc04c7ced68aa44e82856ce4b73187af7671cc7b6be7b33f1a72ff84f264895356e85542d6eaa3cf6a9974c18b62c2d30c72f8545ef4585b6a4ddd87860429
@@ -0,0 +1,85 @@
1
+ require 'faraday'
2
+
3
+ module DSL
4
+ module ActiveResource
5
+ module Route
6
+ extend ActiveSupport::Concern
7
+
8
+ def send_request(verb, route, body)
9
+ conn = Faraday.new 'https://api.github.com/' do |c|
10
+ c.use Faraday::Response::Logger, Logger.new('log/faraday.log')
11
+ c.use Faraday::Adapter::NetHttp
12
+ end
13
+
14
+ conn.headers[:user_agent] = 'RSpec API for Github'
15
+ conn.authorization *authorization.flatten
16
+
17
+ @last_response = conn.send verb, route, (body.to_json if body.present?)
18
+ end
19
+
20
+ def authorization
21
+ # TODO: Any other way to access metadata in a before(:all) ?
22
+ self.class.metadata[:rspec_api][:authorization]
23
+ end
24
+
25
+ module ClassMethods
26
+
27
+ def setup_fixtures
28
+ # nothing to do for now...
29
+ end
30
+
31
+ def existing(field)
32
+ case field
33
+ when :user then 'claudiob'
34
+ when :gist_id then '0d7b597d822102148810'
35
+ when :id then '921225'
36
+ end
37
+ end
38
+
39
+ def unknown(field)
40
+ case field
41
+ when :user then 'not-a-valid-user'
42
+ when :gist_id then 'not-a-valid-gist-id'
43
+ when :id then 'not-a-valid-id'
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ module DSL
53
+ module ActiveResource
54
+ module Resource
55
+ extend ActiveSupport::Concern
56
+
57
+ module ClassMethods
58
+ def authorize_with(options = {})
59
+ rspec_api[:authorization] = options
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+ module DSL
68
+ module ActiveResource
69
+ module Request
70
+ extend ActiveSupport::Concern
71
+
72
+ def response
73
+ @last_response
74
+ end
75
+
76
+ def request_params
77
+ # TO DO
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ RSpec.configuration.include DSL::ActiveResource::Resource, rspec_api_dsl: :resource
84
+ RSpec.configuration.include DSL::ActiveResource::Request, rspec_api_dsl: :request
85
+ RSpec.configuration.include DSL::ActiveResource::Route, rspec_api_dsl: :route
data/lib/rspec-api/dsl.rb CHANGED
@@ -1,8 +1,20 @@
1
- require 'rspec_api_documentation'
2
- require 'rspec_api_documentation/dsl'
3
-
4
- require 'rspec-api/accept_helper'
5
- require 'rspec-api/attributes_helper'
6
- require 'rspec-api/description_helper'
7
- require 'rspec-api/api_helper'
8
- require 'rspec-api/instances_helper'
1
+ require 'rspec-api/dsl/resource'
2
+ require 'rspec-api/dsl/get'
3
+ require 'rspec-api/dsl/request'
4
+
5
+ module DSL
6
+ end
7
+
8
+ def resource(name, args = {}, &block)
9
+ args.merge! rspec_api_dsl: :resource, rspec_api: {resource_name: name}
10
+ describe name, args, &block
11
+ end
12
+
13
+ def rspec_api
14
+ metadata[:rspec_api]
15
+ end
16
+
17
+ RSpec.configuration.include DSL::Resource, rspec_api_dsl: :resource
18
+ RSpec.configuration.include DSL::Route, rspec_api_dsl: :route
19
+ RSpec.configuration.include DSL::Request, rspec_api_dsl: :request
20
+ # requires rspec >= 2.14 : RSpec.configuration.backtrace_exclusion_patterns << %r{lib/rspec-api/dsl\.rb}
@@ -0,0 +1,93 @@
1
+ module DSL
2
+ module Route
3
+ extend ActiveSupport::Concern
4
+
5
+ def send_request(verb, route, body)
6
+ # To be overriden by more specific modules
7
+ end
8
+
9
+ module ClassMethods
10
+ def request(*args, &block)
11
+ text, values = parse_request_arguments args
12
+ extra_parameters.each do |params|
13
+ request_with_extra_params text, values.merge(params), &block
14
+ end
15
+ end
16
+
17
+ def setup_fixtures
18
+ # To be overriden by more specific modules
19
+ end
20
+
21
+ def existing(field)
22
+ # To be overriden by more specific modules
23
+ end
24
+
25
+ private
26
+
27
+ def request_with_extra_params(text, values = {}, &block)
28
+ context request_description(text, values), rspec_api_dsl: :request do
29
+ # NOTE: Having setup_fixtures inside the context sets up different
30
+ # fixtures for each `request` inside the same `get`. This might be
31
+ # a little slower on the DB, but ensures that two `request`s do not
32
+ # conflict. For instance, if you have two `request` inside a `delete`
33
+ # and the first deletes an instance, the second `request` is no
34
+ # affected.
35
+ setup_fixtures
36
+ setup_request rspec_api[:verb], rspec_api[:route], values
37
+ instance_eval(&block) if block_given?
38
+ end
39
+ end
40
+
41
+ def extra_parameters
42
+ [].tap do |optional_params|
43
+ optional_params << {} # default: no extra params
44
+ if rspec_api[:array]
45
+ if rspec_api[:sort]
46
+ optional_params << {sort: rspec_api[:sort][:parameter]}
47
+ optional_params << {sort: "-#{rspec_api[:sort][:parameter]}"}
48
+ end
49
+ if rspec_api[:page]
50
+ optional_params << {page: 2}
51
+ end
52
+ if rspec_api[:filter]
53
+ optional_params << {rspec_api[:filter][:parameter] => existing(rspec_api[:filter][:attribute])}
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def setup_request(verb, route, values)
60
+ request = Proc.new {
61
+ interpolated_route, body = route.dup, values.dup
62
+ body.keys.each do |key|
63
+ if interpolated_route[":#{key}"]
64
+ value = body.delete(key)
65
+ value = value.call if value.is_a?(Proc)
66
+ interpolated_route[":#{key}"] = value.to_s
67
+ (@request_params ||= {})[key] = value
68
+ else
69
+ body[key] = body[key].call if body[key].is_a?(Proc)
70
+ end
71
+ end
72
+ [interpolated_route, body]
73
+ }
74
+ before(:all) { send_request verb, *instance_eval(&request) }
75
+ end
76
+
77
+ def request_description(text, values)
78
+ if values.empty?
79
+ 'by default'
80
+ else
81
+ text = "with" unless text.present?
82
+ "#{text} #{values.map{|k,v| "#{k}#{" #{v}" unless v.is_a?(Proc)}"}.to_sentence}"
83
+ end
84
+ end
85
+
86
+ def parse_request_arguments(args)
87
+ text = args.first.is_a?(String) ? args.first : ''
88
+ values = args.first.is_a?(String) ? args.second : args.first
89
+ [text, values || {}] # NOTE: In Ruby 2.0 we could write values.to_h
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,62 @@
1
+ require 'rspec-api/dsl/request/status'
2
+ require 'rspec-api/dsl/request/headers'
3
+ require 'rspec-api/dsl/request/body'
4
+
5
+ module DSL
6
+ module Request
7
+ extend ActiveSupport::Concern
8
+
9
+ def response
10
+ # To be overriden by more specific modules
11
+ OpenStruct.new # body: nil, status: nil, headers: {}
12
+ end
13
+
14
+ def response_body
15
+ JSON response.body
16
+ rescue JSON::ParserError, JSON::GeneratorError
17
+ nil
18
+ end
19
+
20
+ def response_headers
21
+ response.headers || {}
22
+ end
23
+
24
+ def response_status
25
+ response.status
26
+ end
27
+
28
+ def request_params
29
+ {} # To be overriden by more specific modules
30
+ end
31
+
32
+ module ClassMethods
33
+ def respond_with(status_symbol, &block)
34
+ status_code = to_code status_symbol
35
+
36
+ context 'responds with a status code that' do
37
+ should_match_status_expectations(status_code)
38
+ end
39
+ context 'responds with headers that' do
40
+ should_match_headers_expectations(status_code)
41
+ end
42
+ context 'responds with a body that' do
43
+ should_match_body_expectations(status_code, &block)
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def to_code(status_symbol)
50
+ Rack::Utils.status_code status_symbol
51
+ end
52
+
53
+ def has_entity_body?(status_code)
54
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.exclude? status_code
55
+ end
56
+
57
+ def success?(status_code)
58
+ has_entity_body?(status_code) && status_code < 400
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ require 'rspec-api/matchers'
2
+
3
+ module DSL
4
+ module Request
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def should_match_body_expectations(status_code, &block)
9
+ should_return_a_json rspec_api[:array] if success? status_code
10
+ should_include_attributes rspec_api.fetch(:attributes, {}) if success? status_code
11
+ if rspec_api[:array]
12
+ should_include_fixture_data unless rspec_api[:page]
13
+ should_be_sorted_by(rspec_api[:sort]) if rspec_api[:sort]
14
+ should_be_filtered_by(rspec_api[:filter]) if rspec_api[:filter]
15
+ end
16
+ should_satisfy_expectations_in &block if block_given?
17
+ end
18
+
19
+ private
20
+
21
+ def should_return_a_json(is_array)
22
+ it { expect(response_body).to be_a_json(is_array ? Array : Hash) }
23
+ end
24
+
25
+ def should_include_fixture_data
26
+ it { expect(response_body).to include_fixture_data }
27
+ end
28
+
29
+ def should_be_sorted_by(sort_options)
30
+ it {
31
+ if sort_options[:parameter].to_s == request_params['sort']
32
+ expect(response_body).to be_sorted_by(sort_options[:attribute], verse: :asc)
33
+ elsif "-#{sort_options[:parameter].to_s}" == request_params['sort']
34
+ expect(response_body).to be_sorted_by(sort_options[:attribute], verse: :desc)
35
+ else
36
+ expect(response_body).to be_sorted_by(nil)
37
+ end
38
+ }
39
+ end
40
+
41
+ def should_be_filtered_by(filter_options)
42
+ it {
43
+ if json_value = request_params[filter_options[:parameter].to_s]
44
+ expect(response_body).to be_filtered_by(filter_options[:attribute], json_value)
45
+ else
46
+ expect(response_body).to be_filtered_by(nil)
47
+ end
48
+ }
49
+ end
50
+
51
+ def should_satisfy_expectations_in(&block)
52
+ it { instance_exec(response_body, @request_params, &block) }
53
+ end
54
+
55
+ ## Attributes... might clean them up
56
+ def should_include_attributes(attributes, ancestors = [], can_be_nil=false)
57
+ attributes.each do |name, options = {}|
58
+ should_match_attributes name, options, ancestors, can_be_nil
59
+ should_include_nested_attributes name, options, ancestors
60
+ end
61
+ end
62
+
63
+ def should_match_attributes(name, options, ancestors, can_be_nil)
64
+ it {
65
+ parent = ancestors.inject(response_body) do |chain, ancestor|
66
+ Array.wrap(chain).map{|item| item[ancestor.to_s]}.flatten
67
+ end
68
+ expect(parent).to have_attribute(name, options.merge(parent_can_be_nil: can_be_nil, parent_can_be_empty: true))
69
+ }
70
+ end
71
+
72
+ def should_include_nested_attributes(name, options, ancestors)
73
+ attributes = options.fetch :attributes, {}
74
+ should_include_attributes attributes, ancestors + [name], options[:can_be_nil]
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,24 @@
1
+ require 'rspec-api/matchers'
2
+
3
+ module DSL
4
+ module Request
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def should_match_headers_expectations(status_code)
9
+ should_have_json_content_type if has_entity_body? status_code
10
+ should_be_paginated(rspec_api[:page]) if rspec_api[:array] && rspec_api[:page]
11
+ end
12
+
13
+ private
14
+
15
+ def should_have_json_content_type
16
+ it { expect(response_headers).to have_json_content_type }
17
+ end
18
+
19
+ def should_be_paginated(page_parameter)
20
+ it { expect(response_headers).to have_pagination_links(request_params[page_parameter.to_s]) }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require 'rspec-api/matchers'
2
+
3
+ module DSL
4
+ module Request
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ def should_match_status_expectations(status_code)
9
+ it { expect(response_status).to be_status status_code }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ module DSL
2
+ module Resource
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def self.define_action(verb)
7
+ define_method verb do |route, args = {}, &block|
8
+ rspec_api.merge! array: args.delete(:array), verb: verb, route: route
9
+ args.merge! rspec_api_dsl: :route
10
+ describe("#{verb.upcase} #{route}", args, &block)
11
+ end
12
+ end
13
+
14
+ define_action :get
15
+ define_action :put
16
+ define_action :post
17
+ define_action :delete
18
+
19
+ def has_attribute(name, type, options = {})
20
+ parent = (@attribute_ancestors || []).inject(rspec_api) {|chain, step| chain[:attributes][step]}
21
+ (parent[:attributes] ||= {})[name] = options.merge(type: type)
22
+ nested_attribute(name, &Proc.new) if block_given?
23
+ end
24
+
25
+ def accepts_page(page_parameter)
26
+ rspec_api[:page] = page_parameter
27
+ end
28
+
29
+ def accepts_sort(sort_parameter, options={})
30
+ rspec_api[:sort] = {parameter: sort_parameter, attribute: options[:on]}
31
+ end
32
+
33
+ # TODO: the second 'accepts_filter' should not override the first, but add
34
+ def accepts_filter(filter_parameter, options={})
35
+ rspec_api[:filter] = {parameter: filter_parameter, attribute: options[:on]}
36
+ end
37
+
38
+ private
39
+
40
+ def nested_attribute(name)
41
+ (@attribute_ancestors ||= []).push name
42
+ yield
43
+ @attribute_ancestors.pop
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,40 @@
1
+ require 'rack/test'
2
+
3
+ def app
4
+ Rails.application
5
+ end
6
+
7
+ module DSL
8
+ module RackTest
9
+ module Route
10
+ extend ActiveSupport::Concern
11
+
12
+ def send_request(verb, route, body)
13
+ header 'Accept', 'application/json'
14
+ send verb, route, body
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+
21
+ module DSL
22
+ module RackTest
23
+ module Request
24
+ extend ActiveSupport::Concern
25
+
26
+ def response
27
+ last_response
28
+ end
29
+
30
+ def request_params
31
+ last_request.params
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+ RSpec.configuration.include Rack::Test::Methods, rspec_api_dsl: :route
39
+ RSpec.configuration.include DSL::RackTest::Route, rspec_api_dsl: :route
40
+ RSpec.configuration.include DSL::RackTest::Request, rspec_api_dsl: :request
@@ -0,0 +1,9 @@
1
+ require_relative 'matchers/status'
2
+ require_relative 'matchers/body'
3
+ require_relative 'matchers/content_type'
4
+ require_relative 'matchers/fields'
5
+ require_relative 'matchers/attributes'
6
+ require_relative 'matchers/fixtures'
7
+ require_relative 'matchers/page'
8
+ require_relative 'matchers/sort'
9
+ require_relative 'matchers/filter'
@@ -0,0 +1,33 @@
1
+ RSpec::Matchers.define :have_attribute do |name, options = {}|
2
+ name, can_be_nil, type = name.to_s, options[:can_be_nil], options[:type]
3
+
4
+ match do |json|
5
+ Array.wrap(json).all? do |item|
6
+ if (options[:parent_can_be_nil] and item.nil?) || (options[:parent_can_be_empty] and item.empty?)
7
+ true
8
+ elsif can_be_nil
9
+ item.key?(name)
10
+ else
11
+ matches_type?(item[name], type)
12
+ end
13
+ end
14
+ end
15
+
16
+ description do # TODO: add parent name
17
+ type = "#{options[:type]}#{' or nil' if can_be_nil}"
18
+ %Q(include the field #{name.to_json} of type #{type})
19
+ end
20
+
21
+ failure_message_for_should do |json|
22
+ %Q(should #{description}, but is #{json})
23
+ end
24
+ end
25
+
26
+ def matches_type?(value, type)
27
+ case type
28
+ when :url then value =~ URI::regexp
29
+ when :timestamp then DateTime.iso8601 value rescue false
30
+ when :boolean then [TrueClass, FalseClass].include? value.class
31
+ else value.is_a? type.to_s.classify.constantize
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ RSpec::Matchers.define :be_a_json do |expected_type|
2
+ match do |json|
3
+ json.is_a? expected_type
4
+ end
5
+
6
+ description do
7
+ "be a JSON #{expected_type}"
8
+ end
9
+
10
+ failure_message_for_should do |json|
11
+ %Q(should #{description}, but is #{json})
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ RSpec::Matchers.define :have_json_content_type do
2
+ match do |response|
3
+ response_headers['Content-Type'] == 'application/json; charset=utf-8'
4
+ end
5
+
6
+ description do
7
+ "include 'Content-Type': 'application/json; charset=utf-8'"
8
+ end
9
+
10
+ failure_message_for_should do |item|
11
+ %Q(should #{description}, but are #{response_headers})
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ RSpec::Matchers.define :have_field do |key, options = {}|
2
+ value = options[:value]
3
+
4
+ match do |item|
5
+ item[key.to_s] == value
6
+ end
7
+
8
+ description do
9
+ %Q(have the value #{value.to_json} in the field #{key})
10
+ end
11
+
12
+ failure_message_for_should do |item|
13
+ %Q(should #{description}, but got #{item})
14
+ end
15
+ end
16
+
17
+ RSpec::Matchers.define :have_fields do |key, options = {}|
18
+ value = options[:value]
19
+ after = options[:after]
20
+
21
+ match do |items|
22
+ items.all?{|item| item[key.to_s].send(after) == value }
23
+ end
24
+
25
+ description do
26
+ %Q(have the value #{value.to_json} in the field #{key} after #{after})
27
+ end
28
+
29
+ failure_message_for_should do |items|
30
+ %Q(should #{description}, but got #{items})
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ RSpec::Matchers.define :be_filtered_by do |filtered_attribute, json_value=nil|
2
+ match do |items|
3
+ if filtered_attribute.nil?
4
+ true
5
+ else
6
+ items.all?{|item| item[filtered_attribute.to_s].to_s == json_value}
7
+ end
8
+ end
9
+
10
+ description do
11
+ if filtered_attribute.nil?
12
+ %Q(not be filtered by any specific attribute)
13
+ else
14
+ %Q(be filtered by #{filtered_attribute.to_json} => #{json_value})
15
+ end
16
+ end
17
+
18
+ failure_message_for_should do |items|
19
+ %Q(should #{description}, but is #{items})
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ RSpec::Matchers.define :include_fixture_data do
2
+ match do |body|
3
+ if body.empty? #&& did_not_declare_fixtures
4
+ pending 'Make your tests more meaningful by declaring fixtures!'
5
+ end
6
+ !body.empty?
7
+ end
8
+
9
+ description do
10
+ %Q(have some values from the fixtures)
11
+ end
12
+
13
+ failure_message_for_should do |response|
14
+ %Q(should #{description}, but got an empty array)
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ RSpec::Matchers.define :have_pagination_links do |page|
2
+ match do |response_headers|
3
+ if page.nil?
4
+ true
5
+ else
6
+ links = response_headers['Link'] || '' # https://github.com/lostisland/faraday/pull/306
7
+ rels = links.split(',').map{|link| link[/<.+?>; rel="(.*)"$/, 1]}
8
+ rels.sort == ['first', 'prev']
9
+ end
10
+ end
11
+
12
+ description do
13
+ %Q(include 'Link' (for pagination))
14
+ end
15
+
16
+ failure_message_for_should do |response_headers|
17
+ %Q(should #{description}, but are #{response_headers})
18
+ end
19
+ end
20
+
@@ -0,0 +1,25 @@
1
+ RSpec::Matchers.define :be_sorted_by do |sorting_field, options = {}|
2
+ match do |items|
3
+ if sorting_field.nil?
4
+ true
5
+ else
6
+ values = items.map{|item| item[sorting_field.to_s]}
7
+ values.reverse! if options[:verse] == :desc
8
+ values == values.sort
9
+ end
10
+ end
11
+
12
+ description do
13
+ # NOTE: Since `accepts_sort random: nil` is acceptable, this description
14
+ # should say "you should not expect any sorting by any specific field"
15
+ if sorting_field.nil?
16
+ %Q(not be sorted by any specific attribute)
17
+ else
18
+ %Q(be sorted by #{sorting_field.to_json} #{options[:verse]})
19
+ end
20
+ end
21
+
22
+ failure_message_for_should do |items|
23
+ %Q(should #{description}, but is #{items})
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ RSpec::Matchers.define :be_status do |expected|
2
+ match do |actual|
3
+ actual == expected
4
+ end
5
+
6
+ description do
7
+ %Q(be #{expected})
8
+ end
9
+
10
+ failure_message_for_should do |actual|
11
+ %Q(should #{description}, but is #{actual})
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module RspecApi
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-04 00:00:00.000000000 Z
11
+ date: 2013-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rspec-api-documentation
14
+ name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - '>='
@@ -31,16 +31,29 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
- - lib/rspec-api/accept_helper.rb
35
- - lib/rspec-api/api_helper.rb
36
- - lib/rspec-api/attributes_helper.rb
37
- - lib/rspec-api/description_helper.rb
34
+ - lib/rspec-api/active_resource.rb
35
+ - lib/rspec-api/dsl/get.rb
36
+ - lib/rspec-api/dsl/request/body.rb
37
+ - lib/rspec-api/dsl/request/headers.rb
38
+ - lib/rspec-api/dsl/request/status.rb
39
+ - lib/rspec-api/dsl/request.rb
40
+ - lib/rspec-api/dsl/resource.rb
38
41
  - lib/rspec-api/dsl.rb
39
- - lib/rspec-api/instances_helper.rb
42
+ - lib/rspec-api/http/rack_test.rb
43
+ - lib/rspec-api/matchers/attributes.rb
44
+ - lib/rspec-api/matchers/body.rb
45
+ - lib/rspec-api/matchers/content_type.rb
46
+ - lib/rspec-api/matchers/fields.rb
47
+ - lib/rspec-api/matchers/filter.rb
48
+ - lib/rspec-api/matchers/fixtures.rb
49
+ - lib/rspec-api/matchers/page.rb
50
+ - lib/rspec-api/matchers/sort.rb
51
+ - lib/rspec-api/matchers/status.rb
52
+ - lib/rspec-api/matchers.rb
40
53
  - lib/rspec-api/version.rb
41
54
  - MIT-LICENSE
42
55
  - README.md
43
- homepage: https://github.com/claudiob/rspec-api
56
+ homepage: https://github.com/rspec-api/rspec-api
44
57
  licenses:
45
58
  - MIT
46
59
  metadata: {}
@@ -1,61 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- def accepts(options = {}, &block)
4
- parameters = block_given? ? options.merge(block: block) : options
5
- (metadata[:query_parameters] ||= []).push parameters
6
- end
7
-
8
- RSpec::Matchers.define :be_sorted_by do |attribute|
9
- match do |items|
10
- values = items.map{|item| item[attribute.to_s]}
11
- values.reverse! if example.metadata[:request_params][:sort][0] == '-'
12
- values == values.sort
13
- end
14
- end
15
-
16
- def assert_pagination_links
17
- expect(response_headers).to have_key 'Link'
18
- links = response_headers['Link'].split(',')
19
- rels = links.map{|link| link[/<.+?>; rel="(.*)"$/, 1]}
20
- expect(rels).to match_array ['last', 'next']
21
- end
22
-
23
- def query_parameters_requests
24
- metadata.fetch(:query_parameters, []).map do |params|
25
- if params.has_key? :filter
26
- filter_parameters_requests params
27
- elsif params.has_key? :sort
28
- sort_parameters_requests params
29
- elsif params.has_key? :page
30
- page_parameters_requests params
31
- end
32
- end.flatten
33
- end
34
-
35
- def filter_parameters_requests(params)
36
- params.except(:given, :block).tap do |req|
37
- value = params.fetch :given, apply(:as_json, to: existing(params[:on]))
38
- req[:description] = " filtered by #{params[:filter]}"
39
- req[:request_params] = {params[:filter] => value}
40
- req[:block] = params[:block]
41
- end
42
- end
43
-
44
- def sort_parameters_requests(params)
45
- [true, false].map do |ascending|
46
- params.except(:block).tap do |req|
47
- req[:description] = " sorted by #{params[:sort]} #{ascending ? '↑' : '↓'}"
48
- req[:request_params] = {sort: "#{ascending ? '' : '-'}#{params[:sort]}"}
49
- req[:block] = params[:block]
50
- end
51
- end
52
- end
53
-
54
- def page_parameters_requests(params)
55
- {}.tap do |req|
56
- req[:description] = " paginated by #{params[:page]}"
57
- (req[:request_params] = {})[params[:page]] = 1
58
- req[:min_pages] = 2
59
- req[:block] = -> _ { assert_pagination_links }
60
- end
61
- end
@@ -1,66 +0,0 @@
1
- shared_context 'accept_json', accepts: :json do
2
- header 'Accept', 'application/json'
3
- end
4
-
5
- shared_context 'return_json', returns: :json do
6
- after { expect(json_response?) }
7
- end
8
-
9
- def request(description, request_params = {})
10
- default_request = {}
11
- example_requests = [default_request]
12
- example_requests.concat query_parameters_requests if metadata[:array]
13
-
14
- example_requests.each do |request_metadata|
15
- (request_metadata[:description] ||= '').prepend description
16
- (request_metadata[:request_params] ||= {}).merge! request_params
17
- metadata.merge! request_metadata
18
- yield if block_given?
19
- end
20
- end
21
-
22
- def request_params
23
- example.metadata[:request_params]
24
- end
25
-
26
- def respond_with(expected_status, &block)
27
- description = metadata[:description]
28
- example description do
29
- setup_instances
30
- evaluate_request_params!
31
- do_request request_params.dup
32
- assert_response expected_status, &example.metadata.fetch(:block, block)
33
- end
34
- end
35
-
36
- def assert_response(expected_status, &block)
37
- assert_status expected_status
38
- if block_given? || success? && returns_content?
39
- json = JSON response_body
40
- assert_attributes json if success?
41
- assert_instances json
42
- instance_exec(json, &block) if block_given?
43
- end
44
- end
45
-
46
- def evaluate_request_params!
47
- request_params.each do |name, value|
48
- request_params[name] = instance_exec(&value) if value.is_a? Proc
49
- end
50
- end
51
-
52
- def success?
53
- status < 400
54
- end
55
-
56
- def returns_content?
57
- [100, 101, 102, 204, 205, 304].exclude? status
58
- end
59
-
60
- def assert_status(expected_status)
61
- expect(status).to be Rack::Utils.status_code(expected_status)
62
- end
63
-
64
- def json_response?
65
- response_headers['Content-Type'] == 'application/json; charset=utf-8'
66
- end
@@ -1,53 +0,0 @@
1
- require 'spec_helper'
2
- require 'rspec_api_documentation/dsl'
3
-
4
- def has_attribute(name, type, options = {})
5
- (metadata[:attributes] ||= {})[name] = options.merge(type: type)
6
- end
7
-
8
- def assert_attributes(json)
9
- expect(json).to be_a (example.metadata[:array] ? Array : Hash)
10
- example.metadata[:attributes].each do |name, options|
11
- values = Array.wrap(json).map{|item| item[name.to_s]}
12
- assert_attribute_types(values, options[:type], options[:can_be_nil])
13
- end
14
- end
15
-
16
- def random_values_for_attributes
17
- {}.tap do |values|
18
- example.metadata[:attributes].each do |name, options|
19
- can_be_nil = options[:can_be_nil] && (name != example.metadata[:on])
20
- values[name] = random_attribute_value options.merge(can_be_nil: can_be_nil)
21
- end
22
- end
23
- end
24
-
25
- def random_attribute_value(options)
26
- if options[:can_be_nil] && [true, false].sample
27
- nil
28
- else
29
- case options[:type]
30
- when :string then [*('a'..'z'), *('A'..'Z')].sample(Random.rand 32).join
31
- when :integer then Random.rand(2**16)
32
- when :url then "http://example.com/#{SecureRandom.urlsafe_base64}"
33
- end
34
- end
35
- end
36
-
37
- def assert_attribute_types(values, expected_type, can_be_nil)
38
- values.compact! if can_be_nil
39
- expect(values).to all_match_type expected_type
40
- end
41
-
42
- def matches_type?(value, type)
43
- case type
44
- when :url then value =~ URI::regexp
45
- else value.is_a? type.to_s.classify.constantize
46
- end
47
- end
48
-
49
- RSpec::Matchers.define :all_match_type do |type|
50
- match do |values|
51
- values.all? {|value| matches_type? value, type}
52
- end
53
- end
@@ -1,55 +0,0 @@
1
- def existing(key)
2
- metadata[:description_attribute] = 'an existing'
3
- -> { instances.any key }
4
- end
5
-
6
- def unknown(key)
7
- metadata[:description_attribute] = 'an unknown'
8
- -> { instances.none key }
9
- end
10
-
11
- def apply(method_name, options = {})
12
- proc = options[:to]
13
- -> { proc.call.send method_name }
14
- end
15
-
16
- def with(request_params = {})
17
- request_params[:attribute] ||= metadata.delete :description_attribute
18
- request description_for(request_params), request_params, &Proc.new
19
- end
20
-
21
- def no_params
22
- {}
23
- end
24
-
25
- def valid(request_params)
26
- request_params.merge attribute: 'an valid'
27
- end
28
-
29
- def invalid(request_params)
30
- request_params.merge attribute: 'an invalid'
31
- end
32
-
33
- def description_for(request_params = {})
34
- [description_verb, description_object(request_params)].join ' '
35
- end
36
-
37
- def description_verb
38
- case metadata[:method]
39
- when :get then 'Getting'
40
- when :post then 'Creating'
41
- when :put then 'Updating'
42
- when :delete then 'Deleting'
43
- end
44
- end
45
-
46
- def description_object(request_params = {})
47
- attribute = request_params.delete :attribute
48
- if metadata[:array]
49
- "a list of #{metadata[:resource_name]}".tap do |objects|
50
- objects << " by #{request_params.keys.join(', ')}" if request_params.any?
51
- end
52
- else
53
- [attribute, metadata[:resource_name].singularize].join ' '
54
- end.downcase
55
- end
@@ -1,37 +0,0 @@
1
- def setup_instances
2
- instances.create random_values_for_attributes
3
- instances.create random_values_for_attributes
4
- stub_instances_total_pages example.metadata[:min_pages]
5
- end
6
-
7
- def instances
8
- example.metadata[:resource_name].singularize.constantize
9
- end
10
-
11
- def assert_instances(json)
12
- expect(json).not_to be_empty
13
- end
14
-
15
- def existing(key)
16
- -> { instances.pluck(key).first }
17
- end
18
-
19
- def unknown(key)
20
- keys = 0.downto(-Float::INFINITY).lazy
21
- -> { keys.reject {|value| instances.exists? key => value}.first }
22
- end
23
-
24
- def apply(method_name, options = {})
25
- proc = options[:to]
26
- -> { proc.call.send method_name }
27
- end
28
-
29
- def stub_instances_total_pages(total_pages)
30
- return unless instances.respond_to?(:page) and total_pages.present?
31
- page_method = instances.method :page
32
- instances.stub(:page) do |page|
33
- page_method.call(page).tap do |proxy|
34
- proxy.stub(:total_pages).and_return total_pages
35
- end
36
- end
37
- end