rspec-api 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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