cucumber-rest 0.0.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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZGZhNzhkMDJlMzgyZTFjMTNlNjUzM2VkODM2NjhiYjUxODc4ZWIxZA==
5
+ data.tar.gz: !binary |-
6
+ MmM1ZGIwMTQ2ZDlkNGQ3OWIwYTJjNzQxNWJiYzc3OWZiZTA2ODUwNg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MTVlYjc2ZTFkN2ZmNGUwYmFjMDVlZmYyMzUzM2FkODU2NTNiMjFhY2ZlYTE0
10
+ ZDdhYzhkMzlmOGI1YTNmNzc2YTZiMzkxOTlhOGM1NjUyY2E0NmIyNzBjMzRi
11
+ MzljZmRkNTFlNmM5ZjdhNTY1NTFlNTg3YjRmMmVhNGE1ZjRmZWU=
12
+ data.tar.gz: !binary |-
13
+ NWNlNDBlMjgyOTcxODRjNzVkZDg4NGMxMTYwZjU2M2IzMTRhYzdkZmM0NzBi
14
+ ZjM5OTc4M2EzM2U2NzljYjA2MTE4NGQ2ZTBiZDExMjQwY2I4MzY4MDdmOGU1
15
+ ZTI3NmU5MTYwOGE2ZWRlZmMxYzkyOTgyNTBjZGM3YWU3MTZkOTM=
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ log
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --no-color
2
+ --require spec_helper
3
+ --format progress -o log/spec.log
4
+ --format html -o log/spec.html
5
+ -cfd
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.2
5
+ deploy:
6
+ provider: rubygems
7
+ api_key:
8
+ secure: DfssT15CddiWJaakPGmBqroMixeyvSDtJsDa6dXW1qfO7gnoYQ1VGc0pevMs/1NsJBS/LDPGMN30Y+aTQ/JceiItp6O4LZaWN894C9+tXb3/9QKmTUFQkLqsb44lQeuRESPTtmtVKqMwbaLfiya6BActAfQvNdGY1JmBNAQsx8g=
9
+ gem: cucumber-rest
10
+ on:
11
+ repo: blinkboxbooks/cucumber-rest.rb
12
+ branch: master
data/CHANGELOG.md ADDED
@@ -0,0 +1,83 @@
1
+ # Change log
2
+
3
+ ## 0.1.10 ([#27](https://git.mobcastdev.com/TEST/cucumber-rest/pull/27) 2014-12-16 13:37:19)
4
+
5
+ Step def for asserting HTTP 501 responses
6
+
7
+ Patch
8
+
9
+ * Step def for asserting HTTP 501 responses
10
+
11
+ ## 0.1.9 ([#26](https://git.mobcastdev.com/TEST/cucumber-rest/pull/26) 2014-12-16 11:09:35)
12
+
13
+ Add a step for checking we specifically return a HTTP 202
14
+
15
+ Patch
16
+
17
+ * Adds a step def for checking HTTP 202 responses
18
+
19
+ ## 0.1.8 ([#25](https://git.mobcastdev.com/TEST/cucumber-rest/pull/25) 2014-12-10 16:31:08)
20
+
21
+ Http 204 status codes
22
+
23
+ Patch
24
+
25
+ Add support for handling HTTP 204 responses, checking that the body is empty.
26
+
27
+ Also whitespace changes because hard tabs are bad, mmkay?
28
+
29
+ ## 0.1.7 ([#24](https://git.mobcastdev.com/TEST/cucumber-rest/pull/24) 2014-12-08 16:47:02)
30
+
31
+ Using `ensure_status` instead of `ensure_status_class`
32
+
33
+ A patch to fix checking the status with the correct method.
34
+
35
+ ## 0.1.6 ([#23](https://git.mobcastdev.com/TEST/cucumber-rest/pull/23) 2014-12-08 16:18:09)
36
+
37
+ Made the item specified that is being create non capturing
38
+
39
+ A patch to make the user specified resource being created in the step's regex non-capturing.
40
+
41
+ ## 0.1.5 ([#22](https://git.mobcastdev.com/TEST/cucumber-rest/pull/22) 2014-12-01 14:58:07)
42
+
43
+ adding step for checking 201 Created response
44
+
45
+ patch: adding step for checking 201 Created response
46
+
47
+ ## 0.1.4 ([#21](https://git.mobcastdev.com/TEST/cucumber-rest/pull/21) 2014-10-14 16:38:44)
48
+
49
+ RSpec 3
50
+
51
+ ### Improvements
52
+
53
+ - Move to RSpec 3
54
+
55
+ ## 0.1.3 ([#20](https://git.mobcastdev.com/TEST/cucumber-rest/pull/20) 2014-10-13 10:21:36)
56
+
57
+ Fix where the version file is read from
58
+
59
+ Patch
60
+
61
+ VERSION file location was misdefined as one level too deep. If the VERSION file can't be found in the right folder, default to a more sane "0.0.0".
62
+
63
+ ## 0.1.2 ([#19](https://git.mobcastdev.com/TEST/cucumber-rest/pull/19) 2014-10-09 17:28:41)
64
+
65
+ 410 Gone step
66
+
67
+ Patch
68
+
69
+ ## 0.1.1 ([#18](https://git.mobcastdev.com/TEST/cucumber-rest/pull/18) 2014-07-15 10:52:31)
70
+
71
+ CP-1598 - Make Cucumber-Rest stricter about what it considers a valid Date in HTTP headers
72
+
73
+ Patch to make Cucumber-Rest fall back to using DateTime.httpdate as opposed to generic Date parsing; such that it's more in keeping with the HTTP RFCs
74
+
75
+ ## 0.1.0 ([#17](https://git.mobcastdev.com/TEST/cucumber-rest/pull/17) 2014-06-30 17:42:16)
76
+
77
+ Moved to artifactory
78
+
79
+ ### New Features
80
+
81
+ - Moved to Artifactory
82
+ - Moved to using VERSION file.
83
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org/"
2
+
3
+ gemspec
data/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 blinkbox Books Ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Cucumber::Rest
2
+
3
+ A set of cucumber step definitions and helpers for testing RESTful APIs
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :build => :test
8
+ task :test => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.10
@@ -0,0 +1,33 @@
1
+ ($LOAD_PATH << File.expand_path("../lib", __FILE__)).uniq!
2
+ require "cucumber/rest/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "cucumber-rest"
6
+ s.version = Cucumber::Rest::VERSION
7
+ s.summary = "Cucumber steps and support for testing RESTful services."
8
+ s.description = "A set of Cucumber step definitions and support functions which encapsulate common RESTful functionality."
9
+ s.author = "blinkbox books"
10
+ s.email = "greg@blinkbox.com"
11
+ s.homepage = "http://www.blinkboxbooks.com"
12
+ s.license = "MIT"
13
+
14
+ s.files = `git ls-files`.split($/)
15
+ s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+ s.require_paths = ["lib"]
18
+ s.extra_rdoc_files = ["README.md"]
19
+
20
+ s.post_install_message = ":: Coded for blinkbox books :: Love books, love code? Get in touch ::"
21
+
22
+ s.add_runtime_dependency "activesupport", ">= 3.2"
23
+ s.add_runtime_dependency "cucumber", "~> 1.3"
24
+ s.add_runtime_dependency "multi_json", "~> 1.7"
25
+ s.add_runtime_dependency "rspec", "~> 3.0"
26
+ s.add_runtime_dependency "rack", "~> 1.5"
27
+ s.add_runtime_dependency "http_capture", "~> 0.0", ">= 0.0.4"
28
+
29
+ s.add_development_dependency "bundler", "~> 1.3"
30
+ s.add_development_dependency "rake", "~> 10.1"
31
+
32
+ s.add_development_dependency "cucumber_spinner"
33
+ end
@@ -0,0 +1,12 @@
1
+ require 'http_capture'
2
+
3
+ module Cucumber
4
+ module Rest
5
+ # Helper functions for the handling of response bodies
6
+ module Body
7
+ def self.ensure_empty(response: HttpCapture::RESPONSES.last)
8
+ raise "Request body was not empty:\n #{response.body}" if response.body.to_s.size != 0
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,152 @@
1
+ require "date"
2
+ require "http_capture"
3
+
4
+ module Cucumber
5
+ module Rest
6
+ # Helper functions for checking the cacheability of responses.
7
+ module Caching
8
+
9
+ class EmptyHTTPDateError < RuntimeError; end
10
+
11
+ # Ensures that a response is privately cacheable.
12
+ #
13
+ # This function uses a strict interpretation of RFC 2616 to ensure the widest interoperability with
14
+ # implementations, including HTTP 1.0.
15
+ #
16
+ # @param response [HttpCapture::Response] The response to check. If not supplied defaults to the last response.
17
+ # @param min_duration [Integer] The minimum permitted cache duration, in seconds.
18
+ # @param max_duration [Integer] The maximum permitted cache duration, in seconds.
19
+ # @param duration [Integer] The required cache duration, in seconds. Convenient if min and max are the same.
20
+ # @return [nil]
21
+ def self.ensure_response_is_publicly_cacheable(args = {})
22
+ response, min_duration, max_duration = extract_args(args)
23
+ ensure_cache_headers(response, false)
24
+
25
+ cache_control = parse_cache_control(response["Cache-Control"])
26
+ ensure_cache_directives(cache_control, "public", "max-age")
27
+ prohibit_cache_directives(cache_control, "private", "no-cache", "no-store")
28
+
29
+ age = response["Age"].to_i
30
+ date = parse_httpdate(response["Date"])
31
+ expires = parse_expires_httpdate(response["Expires"])
32
+ max_age = cache_control["max-age"]
33
+ expected_max_age = age + ((expires - date) * 24 * 3600).to_i
34
+ unless (max_age - expected_max_age).abs <= 1 # 1 second leeway
35
+ raise "Age, Date, Expires and Cache-Control:max-age are inconsistent"
36
+ end
37
+
38
+ ensure_cache_duration(max_age, min_duration, max_duration)
39
+ end
40
+
41
+ # Ensures that a response is privately cacheable.
42
+ #
43
+ # This function uses a strict interpretation of RFC 2616, including precedence rules for Date, Expires and
44
+ # Cache-Control:max-age to ensure the widest interoperability with implementations, including HTTP 1.0.
45
+ #
46
+ # @param response [HttpCapture::Response] The response to check. If not supplied defaults to the last response.
47
+ # @param min_duration [Integer] The minimum permitted cache duration, in seconds.
48
+ # @param max_duration [Integer] The maximum permitted cache duration, in seconds.
49
+ # @param duration [Integer] The required cache duration, in seconds. Convenient if min and max are the same.
50
+ # @return [nil]
51
+ def self.ensure_response_is_privately_cacheable(args = {})
52
+ response, min_duration, max_duration = extract_args(args)
53
+ ensure_cache_headers(response, false)
54
+
55
+ cache_control = parse_cache_control(response["Cache-Control"])
56
+ ensure_cache_directives(cache_control, "private", "max-age")
57
+ prohibit_cache_directives(cache_control, "public", "no-cache", "no-store")
58
+
59
+ date = parse_httpdate(response["Date"])
60
+ expires = parse_expires_httpdate(response["Expires"])
61
+ raise "Expires should not be later than Date" if expires && expires > date
62
+
63
+ ensure_cache_duration(cache_control["max-age"], min_duration, max_duration)
64
+ end
65
+
66
+ # Ensures that a response is not cacheable.
67
+ #
68
+ # This function uses a strict interpretation of RFC 2616, to ensure the widest interoperability with
69
+ # implementations, including HTTP 1.0.
70
+ #
71
+ # @param response [HttpCapture::Response] The response to check. If not supplied defaults to the last response.
72
+ # @return [nil]
73
+ def self.ensure_response_is_not_cacheable(args = {})
74
+ response, * = extract_args(args)
75
+ ensure_cache_headers(response, true)
76
+
77
+ cache_control = parse_cache_control(response["Cache-Control"])
78
+ ensure_cache_directives(cache_control, "no-store")
79
+ prohibit_cache_directives(cache_control, "public", "private", "max-age") # TODO: prohibit no-cache?
80
+
81
+ date = parse_httpdate(response["Date"])
82
+ expires = parse_expires_httpdate(response["Expires"]) rescue nil # invalid values are treated as < now, which is fine
83
+ raise "Expires should not be later than Date" if expires && expires > date
84
+ end
85
+
86
+ private
87
+
88
+ def self.extract_args(args)
89
+ response = args[:response] || HttpCapture::RESPONSES.last
90
+ if response.nil?
91
+ raise "There is no response to check. Have you required the right capture file from HttpCapture?"
92
+ end
93
+
94
+ min_duration = args[:min_duration] || args[:duration]
95
+ max_duration = args[:max_duration] || args[:duration]
96
+
97
+ return response, min_duration, max_duration
98
+ end
99
+
100
+ def self.ensure_cache_headers(response, pragma_nocache)
101
+ ["Cache-Control", "Date", "Expires"].each { |h| raise "Required header '#{h}' is missing" if response[h].nil? }
102
+
103
+ unless (/\bno-cache\b/ === response["Pragma"]) == pragma_nocache
104
+ raise "Pragma should #{pragma_nocache ? "" : "not "}include the 'no-cache' directive"
105
+ end
106
+ end
107
+
108
+ def self.parse_cache_control(cache_control)
109
+ cache_control.split(",").each_with_object({}) do |entry, hash|
110
+ key, value = entry.split("=", 2).map(&:strip)
111
+ hash[key] = value =~ /^\d+$/ ? value.to_i : value
112
+ end
113
+ end
114
+
115
+ def self.ensure_cache_directives(cache_control, *directives)
116
+ directives.each do |directive|
117
+ raise "Cache-Control should include the '#{directive}' directive" unless cache_control.has_key?(directive)
118
+ end
119
+ end
120
+
121
+ def self.prohibit_cache_directives(cache_control, *directives)
122
+ directives.each do |directive|
123
+ raise "Cache-Control should not include the '#{directive}' directive" if cache_control.has_key?(directive)
124
+ end
125
+ end
126
+
127
+ def self.ensure_cache_duration(actual, min_expected, max_expected)
128
+ if min_expected && actual < min_expected
129
+ raise "Cache duration is #{actual}s but expected at least #{min_expected}s"
130
+ end
131
+ if max_expected && actual > max_expected
132
+ raise "Cache duration is #{actual}s but expected no more than #{max_expected}s"
133
+ end
134
+ end
135
+
136
+ def self.parse_httpdate(date)
137
+ raise EmptyHTTPDateError, "Empty date value" if (date.empty? || date.nil?)
138
+ DateTime.httpdate(date)
139
+ end
140
+
141
+ def self.parse_expires_httpdate(date)
142
+ begin
143
+ parse_httpdate(date)
144
+ rescue EmptyHTTPDateError, ArgumentError => e
145
+ warn "Invalid Expires header value, handling as a past value"
146
+ DateTime.httpdate()
147
+ end
148
+ end
149
+
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,37 @@
1
+ require "http_capture"
2
+ require "rack"
3
+
4
+ module Cucumber
5
+ module Rest
6
+ # Helper functions for checking the cacheability of responses.
7
+ module Status
8
+
9
+ def self.ensure_status(expected, response: HttpCapture::RESPONSES.last)
10
+ actual = response.status
11
+ unless expected == actual
12
+ actual_name = Rack::Utils::HTTP_STATUS_CODES[actual]
13
+ expected_name = Rack::Utils::HTTP_STATUS_CODES[expected]
14
+ message = "Request status was #{actual} #{actual_name}; expected #{expected} #{expected_name}"
15
+ raise message
16
+ end
17
+ end
18
+
19
+ def self.ensure_status_class(expected, response: HttpCapture::RESPONSES.last)
20
+ min = case expected
21
+ when :informational then 100
22
+ when :success then 200
23
+ when :redirection then 300
24
+ when :client_error then 400
25
+ when :server_error then 500
26
+ end
27
+ max = min + 99
28
+ expected_range = min..max
29
+ actual = response.status
30
+ unless expected_range === actual
31
+ message = "Request status was #{actual} #{Rack::Utils::HTTP_STATUS_CODES[actual]}; expected #{expected_range}"
32
+ raise message
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,20 @@
1
+ require "active_support/core_ext/numeric/time"
2
+ require "cucumber/rest/caching"
3
+
4
+ Then(/^(?:the response|it)? is publicly cacheable$/) do
5
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable
6
+ end
7
+
8
+ Then(/^(?:the response|it)? is publicly cacheable for (a|\d+(?:\.\d+)?) (week|day|hour|minute|second)s?$/) do |num, unit|
9
+ num = num == "a" ? 1 : num.to_f
10
+ duration = num.send(unit.to_sym).to_i
11
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(duration: duration)
12
+ end
13
+
14
+ Then(/^(?:the response|it)? is privately cacheable$/) do
15
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable
16
+ end
17
+
18
+ Then(/^(?:the response|it)? is not cacheable$/) do
19
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable
20
+ end
@@ -0,0 +1,47 @@
1
+ require "cucumber/rest/status"
2
+ require "cucumber/rest/body"
3
+
4
+ Then(/^the request (?:is|was) successful$/) do
5
+ Cucumber::Rest::Status.ensure_status_class(:success)
6
+ end
7
+
8
+ Then(/^the request (?:is|was) successful and (?:a resource|.+) was created$/) do
9
+ Cucumber::Rest::Status.ensure_status(201)
10
+ end
11
+
12
+ Then(/^the request (?:is|was) successfully accepted$/) do
13
+ Cucumber::Rest::Status.ensure_status(202)
14
+ end
15
+
16
+ Then(/^the request (?:is|was) successful and (?:no|an empty) response body is returned$/) do
17
+ Cucumber::Rest::Status.ensure_status(204)
18
+ Cucumber::Rest::Body.ensure_empty
19
+ end
20
+
21
+ Then(/^(?:it|the request) fails because it (?:is|was) invalid$/) do
22
+ Cucumber::Rest::Status.ensure_status(400)
23
+ end
24
+
25
+ Then(/^(?:it|the request) fails because (?:.+) (?:is|was|am|are) unauthori[sz]ed$/) do
26
+ Cucumber::Rest::Status.ensure_status(401)
27
+ end
28
+
29
+ Then(/^(?:it|the request) fails because (?:.+) (?:is|was) forbidden$/) do
30
+ Cucumber::Rest::Status.ensure_status(403)
31
+ end
32
+
33
+ Then(/^(?:it|the request) fails because the (?:.+) (?:is|was) not found$/) do
34
+ Cucumber::Rest::Status.ensure_status(404)
35
+ end
36
+
37
+ Then(/^(?:it|the request) fails because there (?:is|was) a conflict(?: with .+)?$/) do
38
+ Cucumber::Rest::Status.ensure_status(409)
39
+ end
40
+
41
+ Then(/^(?:it|the request) fails because the (?:.+) (?:is|was|has) gone$/) do
42
+ Cucumber::Rest::Status.ensure_status(410)
43
+ end
44
+
45
+ Then(/^(?:it|the request) fails because the (?:.+) (?:is|was) not implemented$/) do
46
+ Cucumber::Rest::Status.ensure_status(501)
47
+ end
@@ -0,0 +1,2 @@
1
+ require "cucumber/rest/steps/caching"
2
+ require "cucumber/rest/steps/status"
@@ -0,0 +1,5 @@
1
+ module Cucumber
2
+ module Rest
3
+ VERSION = File.read(File.join(__dir__,"../../../VERSION")) rescue "0.0.0"
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ module Cucumber
2
+ module Rest
3
+ end
4
+ end
5
+
6
+ require "cucumber/rest/steps"
@@ -0,0 +1,21 @@
1
+ require "cucumber/rest/body"
2
+
3
+ describe Cucumber::Rest::Body, :body do
4
+ context "#ensure_empty" do
5
+ def generate_response(body: nil)
6
+ response = MockResponse.new
7
+ response.body = body
8
+ response
9
+ end
10
+
11
+ it "does not raise an error when the response body is empty" do
12
+ response = generate_response(body: nil)
13
+ expect { Cucumber::Rest::Body.ensure_empty(response: response) }.to_not raise_error
14
+ end
15
+
16
+ it "raises an error when the response body is non-empty" do
17
+ response = generate_response(body: "something")
18
+ expect { Cucumber::Rest::Body.ensure_empty(response: response) }.to raise_error
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,304 @@
1
+ require "cucumber/rest/caching"
2
+
3
+ shared_examples "a cache header inspector" do |method, *header_names|
4
+
5
+ ["Cache-Control", "Date", "Expires"].each do |header_name|
6
+ it "raises an error when the #{header_name} header is missing" do
7
+ response = generate_response
8
+ response[header_name] = nil
9
+ expect {
10
+ Cucumber::Rest::Caching.send(method, { response: response })
11
+ }.to raise_error "Required header '#{header_name}' is missing"
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ describe Cucumber::Rest::Caching, :caching do
18
+
19
+ context "#ensure_response_is_publicly_cacheable" do
20
+
21
+ def generate_response(duration = 3600, date = DateTime.now, age = nil)
22
+ response = MockResponse.new
23
+ response["Cache-Control"] = "public, max-age=#{duration}"
24
+ response["Expires"] = (date + (duration / (24.0 * 3600))).strftime(RFC822_DATE_FORMAT)
25
+ if age
26
+ date += age / 86400.0
27
+ response["Age"] = age.to_s if age
28
+ end
29
+ response["Date"] = date.strftime(RFC822_DATE_FORMAT)
30
+ response.body = "test"
31
+ response
32
+ end
33
+
34
+ context "with cacheable responses" do
35
+ it_behaves_like "a cache header inspector", :ensure_response_is_publicly_cacheable
36
+
37
+ it "does not raise an error when the public cache headers are set correctly" do
38
+ duration = 3600
39
+ response = generate_response(duration)
40
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response, duration: duration)
41
+ end
42
+
43
+ it "does not raise an error when the public cache headers are set correctly, including an Age header" do
44
+ duration = 3600
45
+ response = generate_response(duration, DateTime.now, 243)
46
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response, duration: duration)
47
+ end
48
+
49
+ ["max-age"].each do |directive| # TODO: Should include "public"
50
+ it "raises an error when the Cache-Control header does not include the #{directive} directive" do
51
+ response = generate_response
52
+ response["Cache-Control"] = response["Cache-Control"].split(/\s*,\s*/).reject { |d| d =~ /^#{directive}($|=)/ }.join(", ")
53
+ expect {
54
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response)
55
+ }.to raise_error "Cache-Control should include the '#{directive}' directive"
56
+ end
57
+ end
58
+
59
+ ["private", "no-cache", "no-store"].each do |directive|
60
+ it "raises an error when the Cache-Control header includes the #{directive} directive" do
61
+ response = generate_response
62
+ response["Cache-Control"] << ", #{directive}"
63
+ expect {
64
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response)
65
+ }.to raise_error "Cache-Control should not include the '#{directive}' directive"
66
+ end
67
+ end
68
+
69
+ it "raises an error when Date, Expires and Cache-Control:max-age are inconsistent" do
70
+ response = generate_response
71
+ response["Expires"] = DateTime.now.strftime(RFC822_DATE_FORMAT)
72
+ expect {
73
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response)
74
+ }.to raise_error "Age, Date, Expires and Cache-Control:max-age are inconsistent"
75
+ end
76
+
77
+ it "raises an error when Age, Date, Expires and Cache-Control:max-age are inconsistent" do
78
+ response = generate_response
79
+ response["Age"] = "5"
80
+ expect {
81
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response)
82
+ }.to raise_error "Age, Date, Expires and Cache-Control:max-age are inconsistent"
83
+ end
84
+
85
+ it "raises an error when the Pragma header includes the no-cache directive" do
86
+ response = generate_response
87
+ response["Pragma"] = "no-cache"
88
+ expect {
89
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response)
90
+ }.to raise_error "Pragma should not include the 'no-cache' directive"
91
+ end
92
+
93
+ end
94
+
95
+ context "with application-level requirements" do
96
+
97
+ it "raises an error when the cache duration is higher than the expected duration" do
98
+ response = generate_response(3600)
99
+ expect {
100
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response, duration: 1800)
101
+ }.to raise_error "Cache duration is 3600s but expected no more than 1800s"
102
+ end
103
+
104
+ it "raises an error when the cache duration is lower than the expected duration" do
105
+ response = generate_response(900)
106
+ expect {
107
+ Cucumber::Rest::Caching.ensure_response_is_publicly_cacheable(response: response, duration: 1800)
108
+ }.to raise_error "Cache duration is 900s but expected at least 1800s"
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+
115
+ context "#ensure_response_is_privately_cacheable" do
116
+
117
+ def generate_response(duration = 3600, date = DateTime.now)
118
+ response = MockResponse.new
119
+ response["Cache-Control"] = "private, max-age=#{duration}"
120
+ response["Date"] = date.strftime(RFC822_DATE_FORMAT)
121
+ response["Expires"] = date.strftime(RFC822_DATE_FORMAT)
122
+ response.body = "test"
123
+ response
124
+ end
125
+
126
+ context "with non-cacheable responses" do
127
+ it_behaves_like "a cache header inspector", :ensure_response_is_privately_cacheable
128
+
129
+ it "does not raise an error when the private cache headers are set correctly" do
130
+ duration = 3600
131
+ response = generate_response(duration)
132
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response, duration: duration)
133
+ end
134
+
135
+ it "does not raise an error when the private cache headers are set correctly, with Expires as -1" do
136
+ duration = 3600
137
+ response = generate_response(duration)
138
+ response["Expires"] = "-1" # invalid, but should be treated as in the past (i.e. already expired)
139
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response, duration: duration)
140
+ end
141
+
142
+ ["private", "max-age"].each do |directive|
143
+ it "raises an error when the Cache-Control header does not include the #{directive} directive" do
144
+ response = generate_response
145
+ response["Cache-Control"] = response["Cache-Control"].split(/\s*,\s*/).reject { |d| d =~ /^#{directive}($|=)/ }.join(", ")
146
+ expect {
147
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response)
148
+ }.to raise_error "Cache-Control should include the '#{directive}' directive"
149
+ end
150
+ end
151
+
152
+ ["public", "no-cache", "no-store"].each do |directive|
153
+ it "raises an error when the Cache-Control header includes the #{directive} directive" do
154
+ response = generate_response
155
+ response["Cache-Control"] << ", #{directive}"
156
+ expect {
157
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response)
158
+ }.to raise_error "Cache-Control should not include the '#{directive}' directive"
159
+ end
160
+ end
161
+
162
+ it "raises an error when Expires is a valid date later than Date" do
163
+ response = generate_response
164
+ response["Expires"] = (DateTime.now + 10).strftime(RFC822_DATE_FORMAT)
165
+ expect {
166
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response)
167
+ }.to raise_error "Expires should not be later than Date"
168
+ end
169
+
170
+ it "raises an error when the Pragma header includes the no-cache directive" do
171
+ response = generate_response
172
+ response["Pragma"] = "no-cache"
173
+ expect {
174
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response)
175
+ }.to raise_error "Pragma should not include the 'no-cache' directive"
176
+ end
177
+
178
+ end
179
+
180
+ context "with application-level requirements" do
181
+
182
+ it "raises an error when the cache duration is higher than the expected duration" do
183
+ response = generate_response(3600)
184
+ expect {
185
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response, duration: 1800)
186
+ }.to raise_error "Cache duration is 3600s but expected no more than 1800s"
187
+ end
188
+
189
+ it "raises an error when the cache duration is lower than the expected duration" do
190
+ response = generate_response(900)
191
+ expect {
192
+ Cucumber::Rest::Caching.ensure_response_is_privately_cacheable(response: response, duration: 1800)
193
+ }.to raise_error "Cache duration is 900s but expected at least 1800s"
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+
200
+ context "#ensure_response_is_not_cacheable" do
201
+
202
+ def generate_response(date = DateTime.now)
203
+ response = MockResponse.new
204
+ response["Cache-Control"] = "no-store"
205
+ response["Date"] = date.strftime(RFC822_DATE_FORMAT)
206
+ response["Expires"] = date.strftime(RFC822_DATE_FORMAT)
207
+ response["Pragma"] = "no-cache"
208
+ response.body = "test"
209
+ response
210
+ end
211
+
212
+ context "with non-cacheable responses" do
213
+ it_behaves_like "a cache header inspector", :ensure_response_is_not_cacheable
214
+
215
+ it "does not raise an error when the prevent cache headers are set correctly" do
216
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable(response: generate_response)
217
+ end
218
+
219
+ it "does not raise an error when the public cache headers are set correctly, with Expires as -1" do
220
+ response = generate_response
221
+ response["Expires"] = "-1" # invalid, but should be treated as in the past (i.e. already expired)
222
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable(response: response)
223
+ end
224
+
225
+ ["no-store"].each do |directive|
226
+ it "raises an error when the Cache-Control header does not include the #{directive} directive" do
227
+ response = generate_response
228
+ response["Cache-Control"] = response["Cache-Control"].split(/\s*,\s*/).reject { |d| d =~ /^#{directive}($|=)/ }.join(", ")
229
+ expect {
230
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable(response: response)
231
+ }.to raise_error "Cache-Control should include the '#{directive}' directive"
232
+ end
233
+ end
234
+
235
+ ["public", "private", "max-age"].each do |directive|
236
+ it "raises an error when the Cache-Control header includes the #{directive} directive" do
237
+ response = generate_response
238
+ response["Cache-Control"] << ", #{directive}"
239
+ expect {
240
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable(response: response)
241
+ }.to raise_error "Cache-Control should not include the '#{directive}' directive"
242
+ end
243
+ end
244
+
245
+ it "raises an error when Expires is a valid date later than Date" do
246
+ response = generate_response
247
+ response["Expires"] = (DateTime.now + 10).strftime(RFC822_DATE_FORMAT)
248
+ expect {
249
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable(response: response)
250
+ }.to raise_error "Expires should not be later than Date"
251
+ end
252
+
253
+ it "raises an error when the Pragma header does not include the no-cache directive" do
254
+ response = generate_response
255
+ response["Pragma"] = nil
256
+ expect {
257
+ Cucumber::Rest::Caching.ensure_response_is_not_cacheable(response: response)
258
+ }.to raise_error "Pragma should include the 'no-cache' directive"
259
+ end
260
+
261
+ end
262
+
263
+ end
264
+
265
+ context "#parse_httpdate" do
266
+
267
+ it "doesn't raise an error when an RFC822 date is passed" do
268
+ date = DateTime.now.strftime(RFC822_DATE_FORMAT)
269
+ Cucumber::Rest::Caching.parse_httpdate(date)
270
+ end
271
+
272
+ it "doesn't raise an error when an ANCI C asctime format date is passed" do
273
+ date = DateTime.now.strftime(ANSI_C_DATE_FORMAT)
274
+ Cucumber::Rest::Caching.parse_httpdate(date)
275
+ end
276
+
277
+ it "doesn't raise an error when an RFC850 date is passed" do
278
+ date = DateTime.now.strftime(RFC850_DATE_FORMAT)
279
+ Cucumber::Rest::Caching.parse_httpdate(date)
280
+ end
281
+
282
+ it "raises an error when a non-English date is passed" do
283
+ date = "ma, 14 heinä 2014 16:08:33 GMT"
284
+ expect {
285
+ Cucumber::Rest::Caching.parse_httpdate(date)
286
+ }.to raise_error("invalid date")
287
+ end
288
+
289
+ it "raises an error when a non-GMT date is passed" do
290
+ date = "Mon, 14 Jul 2014 16:33:04 +0100"
291
+ expect {
292
+ Cucumber::Rest::Caching.parse_httpdate(date)
293
+ }.to raise_error("invalid date")
294
+ end
295
+
296
+ it "raises an error when empty date values are passed" do
297
+ expect {
298
+ Cucumber::Rest::Caching.parse_httpdate("")
299
+ }.to raise_error("Empty date value")
300
+ end
301
+
302
+ end
303
+
304
+ end
@@ -0,0 +1,40 @@
1
+ require "cucumber/rest/status"
2
+
3
+ shared_examples "a status class inspector" do |status_class, min, max|
4
+ def generate_response(status_code)
5
+ response = MockResponse.new
6
+ response.status = status_code
7
+ response
8
+ end
9
+
10
+ Rack::Utils::HTTP_STATUS_CODES.keys.keep_if { |code| code >= min && code <= max }.each do |status_code|
11
+ it "does not raise an error for status code #{status_code}" do
12
+ Cucumber::Rest::Status.ensure_status_class(status_class, response: generate_response(status_code))
13
+ end
14
+ end
15
+ Rack::Utils::HTTP_STATUS_CODES.keys.keep_if { |code| code < min || code > max }.each do |status_code|
16
+ it "raises an error for status code #{status_code}" do
17
+ expect {
18
+ Cucumber::Rest::Status.ensure_status_class(status_class, response: generate_response(status_code))
19
+ }.to raise_error
20
+ end
21
+ end
22
+ end
23
+
24
+ describe Cucumber::Rest::Status, :status do
25
+ context "#ensure_status_class(:informational)" do
26
+ it_behaves_like "a status class inspector", :informational, 100, 199
27
+ end
28
+ context "#ensure_status_class(:success)" do
29
+ it_behaves_like "a status class inspector", :success, 200, 299
30
+ end
31
+ context "#ensure_status_class(:redirection)" do
32
+ it_behaves_like "a status class inspector", :redirection, 300, 399
33
+ end
34
+ context "#ensure_status_class(:client_error)" do
35
+ it_behaves_like "a status class inspector", :client_error, 400, 499
36
+ end
37
+ context "#ensure_status_class(:server_error)" do
38
+ it_behaves_like "a status class inspector", :server_error, 500, 599
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ require "cucumber"
2
+
3
+ RFC822_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
4
+ RFC850_DATE_FORMAT = "%A, %d-%b-%y %H:%M:%S GMT"
5
+ ANSI_C_DATE_FORMAT = "%a %b %e %H:%M:%S %Y"
6
+
7
+ # A mock response class that looks like HttpCapture::Response
8
+ class MockResponse
9
+ include Enumerable
10
+
11
+ attr_accessor :status
12
+ attr_accessor :body
13
+
14
+ def initialize
15
+ @header = {}
16
+ end
17
+
18
+ # The default header accessor
19
+ def [](key)
20
+ @header[key]
21
+ end
22
+
23
+ # The default header accessor
24
+ def []=(key, value)
25
+ @header[key] = value
26
+ end
27
+
28
+ def each(&block)
29
+ @header.each(&block)
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,204 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cucumber-rest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - blinkbox books
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cucumber
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: multi_json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '1.5'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '1.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: http_capture
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0.0'
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: 0.0.4
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ version: '0.0'
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: 0.0.4
103
+ - !ruby/object:Gem::Dependency
104
+ name: bundler
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '1.3'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ~>
115
+ - !ruby/object:Gem::Version
116
+ version: '1.3'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rake
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ~>
122
+ - !ruby/object:Gem::Version
123
+ version: '10.1'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ~>
129
+ - !ruby/object:Gem::Version
130
+ version: '10.1'
131
+ - !ruby/object:Gem::Dependency
132
+ name: cucumber_spinner
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ description: A set of Cucumber step definitions and support functions which encapsulate
146
+ common RESTful functionality.
147
+ email: greg@blinkbox.com
148
+ executables: []
149
+ extensions: []
150
+ extra_rdoc_files:
151
+ - README.md
152
+ files:
153
+ - .gitignore
154
+ - .rspec
155
+ - .travis.yml
156
+ - CHANGELOG.md
157
+ - Gemfile
158
+ - LICENCE
159
+ - README.md
160
+ - Rakefile
161
+ - VERSION
162
+ - cucumber-rest.gemspec
163
+ - lib/cucumber/rest.rb
164
+ - lib/cucumber/rest/body.rb
165
+ - lib/cucumber/rest/caching.rb
166
+ - lib/cucumber/rest/status.rb
167
+ - lib/cucumber/rest/steps.rb
168
+ - lib/cucumber/rest/steps/caching.rb
169
+ - lib/cucumber/rest/steps/status.rb
170
+ - lib/cucumber/rest/version.rb
171
+ - spec/cucumber/rest/body_spec.rb
172
+ - spec/cucumber/rest/caching_spec.rb
173
+ - spec/cucumber/rest/status_spec.rb
174
+ - spec/spec_helper.rb
175
+ homepage: http://www.blinkboxbooks.com
176
+ licenses:
177
+ - MIT
178
+ metadata: {}
179
+ post_install_message: ':: Coded for blinkbox books :: Love books, love code? Get in
180
+ touch ::'
181
+ rdoc_options: []
182
+ require_paths:
183
+ - lib
184
+ required_ruby_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ! '>='
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ requirements: []
195
+ rubyforge_project:
196
+ rubygems_version: 2.4.5
197
+ signing_key:
198
+ specification_version: 4
199
+ summary: Cucumber steps and support for testing RESTful services.
200
+ test_files:
201
+ - spec/cucumber/rest/body_spec.rb
202
+ - spec/cucumber/rest/caching_spec.rb
203
+ - spec/cucumber/rest/status_spec.rb
204
+ - spec/spec_helper.rb