rspec-rest 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 +7 -0
- data/.gitignore +69 -0
- data/.rubocop.yml +49 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +74 -0
- data/LICENSE +21 -0
- data/README.md +447 -0
- data/Rakefile +8 -0
- data/exe/rspec-rest +12 -0
- data/lib/rspec/rest/captures.rb +27 -0
- data/lib/rspec/rest/class_level_contracts.rb +40 -0
- data/lib/rspec/rest/class_level_presets.rb +90 -0
- data/lib/rspec/rest/config.rb +27 -0
- data/lib/rspec/rest/contract_expectations.rb +38 -0
- data/lib/rspec/rest/contract_matcher.rb +60 -0
- data/lib/rspec/rest/dsl.rb +239 -0
- data/lib/rspec/rest/error_expectations.rb +57 -0
- data/lib/rspec/rest/errors.rb +21 -0
- data/lib/rspec/rest/expectations.rb +114 -0
- data/lib/rspec/rest/formatters/helpers.rb +26 -0
- data/lib/rspec/rest/formatters/request_dump.rb +101 -0
- data/lib/rspec/rest/formatters/request_recorder.rb +76 -0
- data/lib/rspec/rest/header_expectations.rb +36 -0
- data/lib/rspec/rest/json_selector.rb +79 -0
- data/lib/rspec/rest/json_type_helpers.rb +27 -0
- data/lib/rspec/rest/pagination_expectations.rb +60 -0
- data/lib/rspec/rest/request_builders.rb +73 -0
- data/lib/rspec/rest/response.rb +37 -0
- data/lib/rspec/rest/session.rb +136 -0
- data/lib/rspec/rest/version.rb +7 -0
- data/lib/rspec/rest.rb +18 -0
- metadata +157 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Rest
|
|
5
|
+
module HeaderExpectations
|
|
6
|
+
def expect_header(key, value_or_regex)
|
|
7
|
+
with_request_dump_on_failure do
|
|
8
|
+
actual = header_value_for(key)
|
|
9
|
+
available_keys = rest_response.headers.keys.map(&:to_s).sort.join(", ")
|
|
10
|
+
message = "Expected response header #{key.inspect} to be present. " \
|
|
11
|
+
"Available headers: [#{available_keys}]"
|
|
12
|
+
raise ::RSpec::Expectations::ExpectationNotMetError, message if actual.nil?
|
|
13
|
+
|
|
14
|
+
if value_or_regex.is_a?(Regexp)
|
|
15
|
+
expect(actual).to match(value_or_regex)
|
|
16
|
+
else
|
|
17
|
+
expect(actual).to eq(value_or_regex)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def header_value_for(key)
|
|
25
|
+
headers = rest_response.headers
|
|
26
|
+
return headers[key] if headers.key?(key)
|
|
27
|
+
|
|
28
|
+
key_str = key.to_s
|
|
29
|
+
pair = headers.find do |header_key, _|
|
|
30
|
+
header_key.to_s.casecmp(key_str).zero?
|
|
31
|
+
end
|
|
32
|
+
pair&.last
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "errors"
|
|
4
|
+
module RSpec
|
|
5
|
+
module Rest
|
|
6
|
+
class JsonSelector
|
|
7
|
+
TOKEN_PATTERN = /
|
|
8
|
+
(?:
|
|
9
|
+
\.([a-zA-Z_][a-zA-Z0-9_]*)
|
|
10
|
+
)|
|
|
11
|
+
(?:
|
|
12
|
+
\[(\d+)\]
|
|
13
|
+
)
|
|
14
|
+
/x
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def extract(json, selector)
|
|
18
|
+
tokens = parse(selector)
|
|
19
|
+
current = json
|
|
20
|
+
|
|
21
|
+
tokens.each do |token|
|
|
22
|
+
current = dig(current, token, selector)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
current
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def parse(selector)
|
|
31
|
+
selector_str = selector.to_s
|
|
32
|
+
unless selector_str.start_with?("$")
|
|
33
|
+
raise InvalidJsonSelectorError, "Invalid selector #{selector.inspect}. Selector must start with '$'."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
remaining = selector_str[1..]
|
|
37
|
+
tokens = []
|
|
38
|
+
|
|
39
|
+
until remaining.empty?
|
|
40
|
+
match = TOKEN_PATTERN.match(remaining)
|
|
41
|
+
if match.nil? || match.begin(0) != 0
|
|
42
|
+
raise InvalidJsonSelectorError,
|
|
43
|
+
"Invalid selector #{selector.inspect}. Supported forms include '$.a.b' and '$.items[0].id'."
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
tokens << if match[1]
|
|
47
|
+
[:key, match[1]]
|
|
48
|
+
else
|
|
49
|
+
[:index, match[2].to_i]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
remaining = remaining[match[0].length..]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
tokens
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def dig(value, token, selector)
|
|
59
|
+
type, key = token
|
|
60
|
+
|
|
61
|
+
case type
|
|
62
|
+
when :key
|
|
63
|
+
unless value.is_a?(Hash) && value.key?(key)
|
|
64
|
+
raise MissingJsonPathError, "Selector #{selector.inspect} did not match path segment #{key.inspect}."
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
value[key]
|
|
68
|
+
when :index
|
|
69
|
+
unless value.is_a?(Array) && key < value.length
|
|
70
|
+
raise MissingJsonPathError, "Selector #{selector.inspect} did not match array index #{key}."
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
value[key]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Rest
|
|
5
|
+
module JsonTypeHelpers
|
|
6
|
+
def integer
|
|
7
|
+
be_a(Integer)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def string
|
|
11
|
+
be_a(String)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def boolean
|
|
15
|
+
satisfy("be boolean") { |value| [true, false].include?(value) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def array_of(matcher)
|
|
19
|
+
all(matcher)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def hash_including(*)
|
|
23
|
+
a_hash_including(*)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RSpec
|
|
4
|
+
module Rest
|
|
5
|
+
module PaginationExpectations
|
|
6
|
+
def expect_page_size(size, selector: "$")
|
|
7
|
+
with_request_dump_on_failure do
|
|
8
|
+
collection = extract_collection(selector)
|
|
9
|
+
expect(collection.size).to eq(size)
|
|
10
|
+
collection
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def expect_max_page_size(max, selector: "$")
|
|
15
|
+
with_request_dump_on_failure do
|
|
16
|
+
collection = extract_collection(selector)
|
|
17
|
+
expect(collection.size).to be <= max
|
|
18
|
+
collection
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def expect_ids_in_order(ids, selector: "$[*].id")
|
|
23
|
+
with_request_dump_on_failure do
|
|
24
|
+
actual_ids = extract_id_list(selector)
|
|
25
|
+
expect(actual_ids).to eq(ids)
|
|
26
|
+
actual_ids
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def extract_collection(selector)
|
|
33
|
+
payload = rest_response.json
|
|
34
|
+
selected = selector == "$" ? payload : JsonSelector.extract(payload, selector)
|
|
35
|
+
|
|
36
|
+
unless selected.is_a?(Array)
|
|
37
|
+
raise ::RSpec::Expectations::ExpectationNotMetError,
|
|
38
|
+
"Expected selector #{selector.inspect} to resolve to an Array, got #{selected.class}."
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
selected
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extract_id_list(selector)
|
|
45
|
+
wildcard_match = /\A\$\[\*\]\.([a-zA-Z_][a-zA-Z0-9_]*)\z/.match(selector.to_s)
|
|
46
|
+
return JsonSelector.extract(rest_response.json, selector) unless wildcard_match
|
|
47
|
+
|
|
48
|
+
key = wildcard_match[1]
|
|
49
|
+
extract_collection("$").map.with_index do |item, index|
|
|
50
|
+
unless item.is_a?(Hash) && item.key?(key)
|
|
51
|
+
raise ::RSpec::Expectations::ExpectationNotMetError,
|
|
52
|
+
"Selector #{selector.inspect} did not match element #{index} for key #{key.inspect}."
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
item[key]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rack/test"
|
|
4
|
+
|
|
5
|
+
module RSpec
|
|
6
|
+
module Rest
|
|
7
|
+
module RequestBuilders
|
|
8
|
+
def header(key, value)
|
|
9
|
+
rest_request_state[:headers][key] = value
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def headers(value)
|
|
13
|
+
rest_request_state[:headers].merge!(value)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def query(value)
|
|
17
|
+
rest_request_state[:query] ||= {}
|
|
18
|
+
rest_request_state[:query].merge!(value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def json(value)
|
|
22
|
+
rest_request_state[:json] = value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def multipart!
|
|
26
|
+
rest_request_state[:multipart] = true
|
|
27
|
+
rest_request_state[:params] ||= {}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def file(param_key, file_or_path, content_type: nil, filename: nil)
|
|
31
|
+
multipart!
|
|
32
|
+
rest_request_state[:params][param_key] = build_uploaded_file(
|
|
33
|
+
file_or_path,
|
|
34
|
+
content_type: content_type,
|
|
35
|
+
filename: filename
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def path_params(value)
|
|
40
|
+
rest_request_state[:path_params].merge!(value.transform_keys(&:to_s))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def bearer(token)
|
|
44
|
+
header("Authorization", "Bearer #{token}")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def unauthenticated!
|
|
48
|
+
header("Authorization", nil)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def build_uploaded_file(file_or_path, content_type:, filename:)
|
|
54
|
+
if file_or_path.is_a?(Rack::Test::UploadedFile)
|
|
55
|
+
if content_type || filename
|
|
56
|
+
raise ArgumentError,
|
|
57
|
+
"content_type and filename cannot be specified when file_or_path is a Rack::Test::UploadedFile"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
return file_or_path
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
path = if file_or_path.respond_to?(:to_path)
|
|
64
|
+
file_or_path.to_path
|
|
65
|
+
else
|
|
66
|
+
file_or_path.to_s
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
Rack::Test::UploadedFile.new(path, content_type, false, original_filename: filename)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
|
|
6
|
+
module RSpec
|
|
7
|
+
module Rest
|
|
8
|
+
class Response
|
|
9
|
+
attr_reader :raw_response
|
|
10
|
+
|
|
11
|
+
def initialize(raw_response)
|
|
12
|
+
@raw_response = raw_response
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def status
|
|
16
|
+
raw_response.status
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def headers
|
|
20
|
+
raw_response.headers
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def body
|
|
24
|
+
raw_response.body.to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def json
|
|
28
|
+
return @json if instance_variable_defined?(:@json)
|
|
29
|
+
|
|
30
|
+
@json = JSON.parse(body)
|
|
31
|
+
rescue JSON::ParserError => e
|
|
32
|
+
snippet = body[0, 200]
|
|
33
|
+
raise InvalidJsonError, "Failed to parse JSON response: #{e.message}. Body snippet: #{snippet.inspect}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "rack/test"
|
|
5
|
+
require "rack/utils"
|
|
6
|
+
require_relative "errors"
|
|
7
|
+
require_relative "response"
|
|
8
|
+
|
|
9
|
+
module RSpec
|
|
10
|
+
module Rest
|
|
11
|
+
class Session
|
|
12
|
+
SUPPORTED_HTTP_METHODS = %i[get post put patch delete].freeze
|
|
13
|
+
|
|
14
|
+
attr_reader :config, :last_request
|
|
15
|
+
|
|
16
|
+
def initialize(config)
|
|
17
|
+
@config = config
|
|
18
|
+
validate_config!
|
|
19
|
+
@rack_session = Rack::Test::Session.new(Rack::MockSession.new(config.app))
|
|
20
|
+
@last_request = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def request(method:, path:, **options)
|
|
24
|
+
method = validate_http_method!(method)
|
|
25
|
+
resource_path = options[:resource_path]
|
|
26
|
+
headers = options[:headers]
|
|
27
|
+
query = options[:query]
|
|
28
|
+
json = options[:json]
|
|
29
|
+
params = options[:params]
|
|
30
|
+
|
|
31
|
+
request_path = build_path(config.base_path, resource_path, path)
|
|
32
|
+
request_path = append_query(request_path, query)
|
|
33
|
+
|
|
34
|
+
request_headers = build_headers(headers, include_json_content_type: !json.nil?)
|
|
35
|
+
rack_env_headers = build_rack_env_headers(request_headers)
|
|
36
|
+
request_payload = build_payload(json: json, params: params)
|
|
37
|
+
|
|
38
|
+
@last_request = {
|
|
39
|
+
method: method.upcase,
|
|
40
|
+
path: request_path,
|
|
41
|
+
url: build_url(config.base_url, request_path),
|
|
42
|
+
headers: request_headers,
|
|
43
|
+
env: rack_env_headers,
|
|
44
|
+
body: request_payload
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@rack_session.public_send(method, request_path, request_payload, rack_env_headers)
|
|
48
|
+
response
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def response
|
|
52
|
+
Response.new(@rack_session.last_response)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def validate_config!
|
|
58
|
+
return unless config.app.nil?
|
|
59
|
+
|
|
60
|
+
raise MissingAppError, "Config#app is required to initialize RSpec::Rest::Session"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def validate_http_method!(method)
|
|
64
|
+
normalized = method.to_s.downcase
|
|
65
|
+
return normalized if SUPPORTED_HTTP_METHODS.include?(normalized.to_sym)
|
|
66
|
+
|
|
67
|
+
supported = SUPPORTED_HTTP_METHODS.map(&:to_s).join(", ")
|
|
68
|
+
raise UnsupportedHttpMethodError,
|
|
69
|
+
"Unsupported HTTP method: #{method.inspect}. Supported methods: #{supported}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def build_headers(request_headers, include_json_content_type:)
|
|
73
|
+
headers = config.base_headers.dup
|
|
74
|
+
|
|
75
|
+
if config.default_format == :json
|
|
76
|
+
headers["Accept"] ||= "application/json"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
merge_request_headers!(headers, request_headers || {})
|
|
80
|
+
if include_json_content_type
|
|
81
|
+
headers["Content-Type"] ||= "application/json"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
headers
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def merge_request_headers!(headers, request_headers)
|
|
88
|
+
request_headers.each do |key, value|
|
|
89
|
+
if value.nil?
|
|
90
|
+
headers.delete_if { |header_key, _| header_key.to_s.casecmp(key.to_s).zero? }
|
|
91
|
+
next
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
existing_key = headers.keys.find { |header_key| header_key.to_s.casecmp(key.to_s).zero? }
|
|
95
|
+
headers.delete(existing_key) if existing_key && existing_key != key
|
|
96
|
+
headers[key] = value
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def build_payload(json:, params:)
|
|
101
|
+
return JSON.dump(json) unless json.nil?
|
|
102
|
+
|
|
103
|
+
params || {}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def build_rack_env_headers(headers)
|
|
107
|
+
headers.transform_keys { |key| normalize_header_key(key) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def normalize_header_key(key)
|
|
111
|
+
key_str = key.to_s
|
|
112
|
+
return key_str if key_str.start_with?("HTTP_", "CONTENT_TYPE", "CONTENT_LENGTH", "rack.")
|
|
113
|
+
return "CONTENT_TYPE" if key_str.casecmp("Content-Type").zero?
|
|
114
|
+
return "CONTENT_LENGTH" if key_str.casecmp("Content-Length").zero?
|
|
115
|
+
|
|
116
|
+
"HTTP_#{key_str.tr('-', '_').upcase}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def append_query(path, query)
|
|
120
|
+
return path if query.nil? || query.empty?
|
|
121
|
+
|
|
122
|
+
"#{path}?#{Rack::Utils.build_query(query)}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def build_path(base_path, resource_path, endpoint_path)
|
|
126
|
+
segments = [base_path, resource_path, endpoint_path].compact.map(&:to_s)
|
|
127
|
+
normalized = segments.map { |segment| segment.gsub(%r{\A/+|/+\z}, "") }.reject(&:empty?)
|
|
128
|
+
"/#{normalized.join('/')}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def build_url(base_url, path)
|
|
132
|
+
"#{base_url.to_s.sub(%r{/+\z}, '')}#{path}"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
data/lib/rspec/rest.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "rest/version"
|
|
4
|
+
require_relative "rest/errors"
|
|
5
|
+
require_relative "rest/config"
|
|
6
|
+
require_relative "rest/response"
|
|
7
|
+
require_relative "rest/session"
|
|
8
|
+
require_relative "rest/json_selector"
|
|
9
|
+
require_relative "rest/formatters/request_recorder"
|
|
10
|
+
require_relative "rest/dsl"
|
|
11
|
+
|
|
12
|
+
module RSpec
|
|
13
|
+
module Rest
|
|
14
|
+
def self.included(base)
|
|
15
|
+
base.include(DSL)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rspec-rest
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Carl
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-03-07 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rack-test
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.1'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '13.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '13.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.13'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.13'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubocop
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.72'
|
|
62
|
+
- - "<"
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
version: '2.0'
|
|
65
|
+
type: :development
|
|
66
|
+
prerelease: false
|
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '1.72'
|
|
72
|
+
- - "<"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '2.0'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: rubocop-rspec
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.4'
|
|
82
|
+
type: :development
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '3.4'
|
|
89
|
+
description: A Ruby gem for concise, behavior-first REST API specs backed by Rack::Test
|
|
90
|
+
and RSpec.
|
|
91
|
+
email:
|
|
92
|
+
- carl@example.com
|
|
93
|
+
executables:
|
|
94
|
+
- rspec-rest
|
|
95
|
+
extensions: []
|
|
96
|
+
extra_rdoc_files: []
|
|
97
|
+
files:
|
|
98
|
+
- ".gitignore"
|
|
99
|
+
- ".rubocop.yml"
|
|
100
|
+
- CHANGELOG.md
|
|
101
|
+
- Gemfile
|
|
102
|
+
- Gemfile.lock
|
|
103
|
+
- LICENSE
|
|
104
|
+
- README.md
|
|
105
|
+
- Rakefile
|
|
106
|
+
- exe/rspec-rest
|
|
107
|
+
- lib/rspec/rest.rb
|
|
108
|
+
- lib/rspec/rest/captures.rb
|
|
109
|
+
- lib/rspec/rest/class_level_contracts.rb
|
|
110
|
+
- lib/rspec/rest/class_level_presets.rb
|
|
111
|
+
- lib/rspec/rest/config.rb
|
|
112
|
+
- lib/rspec/rest/contract_expectations.rb
|
|
113
|
+
- lib/rspec/rest/contract_matcher.rb
|
|
114
|
+
- lib/rspec/rest/dsl.rb
|
|
115
|
+
- lib/rspec/rest/error_expectations.rb
|
|
116
|
+
- lib/rspec/rest/errors.rb
|
|
117
|
+
- lib/rspec/rest/expectations.rb
|
|
118
|
+
- lib/rspec/rest/formatters/helpers.rb
|
|
119
|
+
- lib/rspec/rest/formatters/request_dump.rb
|
|
120
|
+
- lib/rspec/rest/formatters/request_recorder.rb
|
|
121
|
+
- lib/rspec/rest/header_expectations.rb
|
|
122
|
+
- lib/rspec/rest/json_selector.rb
|
|
123
|
+
- lib/rspec/rest/json_type_helpers.rb
|
|
124
|
+
- lib/rspec/rest/pagination_expectations.rb
|
|
125
|
+
- lib/rspec/rest/request_builders.rb
|
|
126
|
+
- lib/rspec/rest/response.rb
|
|
127
|
+
- lib/rspec/rest/session.rb
|
|
128
|
+
- lib/rspec/rest/version.rb
|
|
129
|
+
homepage: https://github.com/llwebconsulting/rspec-rest
|
|
130
|
+
licenses:
|
|
131
|
+
- MIT
|
|
132
|
+
metadata:
|
|
133
|
+
allowed_push_host: https://rubygems.org
|
|
134
|
+
homepage_uri: https://github.com/llwebconsulting/rspec-rest
|
|
135
|
+
source_code_uri: https://github.com/llwebconsulting/rspec-rest
|
|
136
|
+
changelog_uri: https://github.com/llwebconsulting/rspec-rest/blob/main/CHANGELOG.md
|
|
137
|
+
rubygems_mfa_required: 'true'
|
|
138
|
+
post_install_message:
|
|
139
|
+
rdoc_options: []
|
|
140
|
+
require_paths:
|
|
141
|
+
- lib
|
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: 3.2.0
|
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
requirements: []
|
|
153
|
+
rubygems_version: 3.5.18
|
|
154
|
+
signing_key:
|
|
155
|
+
specification_version: 4
|
|
156
|
+
summary: Rack::Test + RSpec DSL for REST API testing
|
|
157
|
+
test_files: []
|