akamai_rspec 0.4.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb49bd44abd110079985c0b84fa3dcd5c5c792c0
4
- data.tar.gz: 27e1ebf081198f4a3cd07401ea6c8850a901ef90
3
+ metadata.gz: b6ca676a572b65fda62bd771e10b8472cdcb1704
4
+ data.tar.gz: e782f1e731582d5feb4270fbace5d6fef0eb4b46
5
5
  SHA512:
6
- metadata.gz: 66a85bda03be573909a293684fbd98cf41b221a2b3201ddbda984bc02801cda7915fa12b9f95aa89db5dd605f9c77f37427abb057e8016d0de25a470ea1f5571
7
- data.tar.gz: 34c2714e8b34e4ef63e3795133fd5e9df6e9c44559a521d19410e7cb5f9f088f1fd38bc371f1aa28f36d5ab5ea877f03ecec3d80483aff82cc7025d6f63e812a
6
+ metadata.gz: 6f58ba2ed53d2f7edcb81f449b9e658940f69c6a7aa1cd9e938d34ad6ab3a48597325791f2b95fe157c57b62b5913668fc4e5091bc619a65b7bf4fa24b8d140f
7
+ data.tar.gz: 371fbd6072b3a66a3e2db22d1b6186558a898f2448e799bfad310a9983939c79e7956e9d28ab37b14ef830276c3bbc8de2443da9d52efc5d1e692343c6049f82
data/lib/akamai_rspec.rb CHANGED
@@ -1,4 +1,6 @@
1
- require 'akamai_rspec/akamai_headers'
2
- require 'akamai_rspec/matchers/matchers'
1
+ require 'akamai_rspec/matchers'
3
2
  require 'akamai_rspec/request'
4
3
  require 'akamai_rspec/response'
4
+
5
+ module AkamaiRSpec
6
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module AkamaiRSpec
3
+ module Helpers
4
+ module CacheHeaders
5
+ X_CACHE_HEADERS = [:x_true_cache_key, :x_cache_key]
6
+
7
+ def x_cache_headers
8
+ X_CACHE_HEADERS
9
+ end
10
+
11
+ def cache_headers
12
+ x_cache_headers.map {|key| @response.headers[key] }
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module AkamaiRSpec
3
+ module Helpers
4
+ module ChainableRedirect
5
+ def self.included(other)
6
+ other.chain :then do |matcher|
7
+ (@and_then_matchers ||= []).push(matcher)
8
+ end
9
+ end
10
+
11
+ def with_and_without_tls(url)
12
+ url = "http://#{url}" unless URI(url).scheme
13
+ url = url.gsub(/^https:/i, 'http:')
14
+ secure = url.gsub(/^http:/i, 'https:')
15
+ return secure, url
16
+ end
17
+
18
+ def redirect(url, expected_location, expected_response_code)
19
+ response = AkamaiRSpec::Request.get(url)
20
+ fail "Response was #{response.inspect}, expected code #{expected_response_code}" unless response.code == expected_response_code
21
+ unless expected_location === response.headers[:location]
22
+ fail "redirect location was #{response.headers[:location]} (expected #{expected_location})"
23
+ end
24
+
25
+ if @and_then_matchers
26
+ begin
27
+ @and_then_matchers.each {|matcher| expect(response.headers[:location]).to matcher}
28
+ rescue Exception => e
29
+ @and_then_error = e
30
+ return false
31
+ end
32
+ end
33
+
34
+ true
35
+ end
36
+
37
+ def failure_message
38
+ @and_then_error || super
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,15 @@
1
+ require 'securerandom'
2
+ require 'rspec'
3
+
4
+ module AkamaiRSpec
5
+ module Matchers
6
+ extend RSpec::Matchers::DSL
7
+ end
8
+ end
9
+
10
+ require_relative 'matchers/redirects'
11
+ require_relative 'matchers/caching'
12
+ require_relative 'matchers/non_akamai'
13
+ require_relative 'matchers/honour_origin_headers'
14
+ require_relative 'matchers/x_cache_headers'
15
+ require_relative 'matchers/forward_to_index'
@@ -1,44 +1,68 @@
1
1
  require 'rspec'
2
2
  require 'securerandom'
3
- require 'akamai_rspec/akamai_headers'
4
-
5
- RSpec::Matchers.define :be_cacheable do |opts={request_count: 10}|
6
- match do |url|
7
- responses = [1..opts[:request_count]].map { AkamaiRSpec::Request.get_with_debug_headers url }
8
-
9
- responses.any? do |response|
10
- fail("Error fetching #{url}: #{response}") if response.code != 200
11
- (
12
- (response.headers[:x_cache] =~ /TCP(\w+)?_HIT/) and not
13
- (response.headers[:x_cache] =~ /TCP_REFRESH/)
14
- )
3
+ require 'akamai_rspec/request'
4
+
5
+ module AkamaiRSpec
6
+ module Matchers
7
+ define :be_cacheable do |request_count: 4, headers: {}, allow_refresh: false|
8
+ match do |url|
9
+ @responses = (1..request_count).map {
10
+ AkamaiRSpec::Request.get url, headers
11
+ }
12
+
13
+ @responses.any? do |response|
14
+ fail("Error fetching #{url}: #{response}") if response.code != 200
15
+ return allow_refresh if refresh_hit? response
16
+ hit?(response)
17
+ end
18
+ end
19
+
20
+ def refresh_hit?(response)
21
+ response.headers[:x_cache] =~ /TCP_REFRESH/
22
+ end
23
+
24
+ def hit?(response)
25
+ response.headers[:x_cache] =~ /TCP(\w+)?_HIT/
26
+ end
27
+
28
+ def suggest_allow_refresh?(allow_refresh)
29
+ !allow_refresh &&
30
+ !@responses.any?(&method(:hit?)) &&
31
+ @responses.any?(&method(:refresh_hit?))
32
+ end
33
+
34
+ description do
35
+ msg = "to be cacheable (got #{cache_headers} from #{@responses.length} requests)"
36
+ msg += ". Try setting 'allow_refresh: true'." if suggest_allow_refresh?(allow_refresh)
37
+ msg
38
+ end
39
+
40
+ def cache_headers
41
+ @responses.map {|response| response.headers[:x_cache] }
42
+ end
15
43
  end
16
- end
17
- end
18
44
 
19
- module RSpec::Matchers
20
- alias_method :be_cachable, :be_cacheable
21
- alias_method :be_cached, :be_cacheable
22
- define_negated_matcher :not_be_cached, :be_cached
23
- end
45
+ alias_method :be_cachable, :be_cacheable
46
+ alias_method :be_cached, :be_cacheable
24
47
 
25
- RSpec::Matchers.define :have_no_cache_set do
26
- match do |url|
27
- response = AkamaiRSpec::Request.get url
28
- cache_control = response.headers[:cache_control]
29
- return cache_control == 'no-cache'
30
- end
31
- end
48
+ define :have_no_cache_set do
49
+ match do |url|
50
+ response = AkamaiRSpec::Request.get url
51
+ cache_control = response.headers[:cache_control]
52
+ return cache_control == 'no-cache'
53
+ end
54
+ end
32
55
 
56
+ define :be_tier_distributed do
57
+ match do |url|
58
+ response = AkamaiRSpec::Request.get_cache_miss(url)
59
+ @tiered = !response.headers[:x_cache_remote].empty?
60
+ response.code == 200 && @tiered
61
+ end
33
62
 
34
- RSpec::Matchers.define :be_tier_distributed do
35
- match do |url|
36
- response = AkamaiRSpec::Request.get_cache_miss(url)
37
- @tiered = !response.headers[:x_cache_remote].nil?
38
- response.code == 200 && @tiered
39
- end
40
- description do
41
- "be tier distributed (as indicated by the presence of an X-Cache-Remote header in response)"
63
+ description do
64
+ "be tier distributed (as indicated by the presence of an X-Cache-Remote header in response)"
65
+ end
66
+ end
42
67
  end
43
68
  end
44
-
@@ -0,0 +1,20 @@
1
+
2
+ module AkamaiRSpec
3
+ module Matchers
4
+ define :be_forwarded_to_index do |channel|
5
+ match do |url|
6
+ response = Request.get(url)
7
+ session_info = response.headers[:x_akamai_session_info]
8
+ if session_info.nil?
9
+ fail("x-akamai-session-info not found in the headers '#{response.headers}'")
10
+ end
11
+ outcome_attribute = session_info.find { |header| header.include? 'AKA_PM_FWD_URL' }
12
+ if outcome_attribute.nil?
13
+ fail("AKA_PM_FWD_URL not found in the x-akamai-session-info header '#{session_info}'")
14
+ end
15
+ outcome_url = outcome_attribute.split('value=')[1]
16
+ response.code == 200 && outcome_url == "#{channel}"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,122 +3,130 @@ require 'set'
3
3
  require 'time'
4
4
  require 'uri'
5
5
 
6
- RSpec::Matchers.define :honour_origin_cache_headers do |origin, headers|
7
- header_options = [:cache_control, :expires, :both]
8
- headers ||= :both
9
- fail("Headers must be one of: #{header_options}") unless header_options.include? headers
10
-
11
- match do |url|
12
- akamai_response = AkamaiRSpec::Request.get url
13
- origin_response = origin_response(origin)
14
- check_cache_control(origin_response, akamai_response, headers)
15
- check_expires(origin_response, akamai_response, headers)
16
- true
6
+ module AkamaiRSpec
7
+ module Matchers
8
+
9
+ define :honour_origin_cache_headers do |origin, headers=:both|
10
+ header_options = [:cache_control, :expires, :both]
11
+ fail("Headers must be one of: #{header_options}") unless header_options.include? headers
12
+
13
+ match do |url|
14
+ akamai_response = AkamaiRSpec::Request.get url
15
+ url = "http://" + url unless url =~ /^http/
16
+ origin_url = URI(url)
17
+ origin_url.host = URI(origin).hostname || origin
18
+ origin_response = origin_response(origin_url.to_s)
19
+ check_cache_control(origin_response, akamai_response, headers)
20
+ check_expires(origin_response, akamai_response, headers)
21
+ true
22
+ end
23
+
24
+ def fix_date_header(origin_response)
25
+ origin_response.headers[:date] ||= Time.now.httpdate
26
+ origin_response
27
+ end
28
+
29
+ def origin_response(origin)
30
+ fix_date_header(RestClient::Request.execute(method: :get, url: origin, verify_ssl: false))
31
+ end
32
+
33
+ def clean_cc_directives(origin_response, akamai_response)
34
+ origin_cc_directives = origin_response.headers[:cache_control].split(/[, ]+/).to_set
35
+ akamai_cc_directives = akamai_response.headers[:cache_control].split(/[, ]+/).to_set
36
+
37
+ origin_cc_directives.delete 'must-revalidate' # as Akamai does no pass it on
38
+ return origin_cc_directives, akamai_cc_directives
39
+ end
40
+
41
+ def cc_directives(origin_response, akamai_response)
42
+ origin_cc, akamai_cc = clean_cc_directives(origin_response, akamai_response)
43
+ check_cc(origin_cc, akamai_cc) unless (origin_cc & ['no-store', 'no-cache']).empty?
44
+ return origin_cc, akamai_cc
45
+ end
46
+
47
+ def check_and_clean_header(origin_cc, akamai_cc, expected)
48
+ unless akamai_cc.include? expected
49
+ fail "Akamai was expected to, but did not, add 'Cache-Control: #{expected}' as Origin sent 'no-store' or 'no-cache'"
50
+ end
51
+ akamai_cc.delete expected unless origin_cc.include? expected
52
+ akamai_cc
53
+ end
54
+
55
+ def check_cc(origin_cc, akamai_cc)
56
+ ['no-store', 'max-age=0'].each do |expected|
57
+ akamai_cc = check_and_clean_header(origin_cc, akamai_cc, expected)
58
+ end
59
+ return origin_cc, akamai_cc
60
+ end
61
+
62
+ def max_age(cc_directives)
63
+ cc_directives.detect { |d| d.start_with? 'max-age=' }
64
+ end
65
+
66
+ def max_age_to_num(max_age)
67
+ max_age.split('=').last.to_i
68
+ end
69
+
70
+ def clean_max_age(cc_directives)
71
+ max_age = max_age(cc_directives)
72
+ cc_directives.delete max_age if max_age
73
+ return max_age_to_num(max_age), cc_directives
74
+ end
75
+
76
+ def check_max_age(origin_cc_directives, akamai_cc_directives)
77
+ origin_max_age, origin_cc_directives = clean_max_age(origin_cc_directives)
78
+ akamai_max_age, akamai_cc_directives = clean_max_age(akamai_cc_directives)
79
+ if akamai_max_age > origin_max_age
80
+ fail "Akamai sent a max-age greater than Origin's: #{akamai_max_age} > #{origin_max_age}"
81
+ end
82
+ return origin_cc_directives, akamai_cc_directives
83
+ end
84
+
85
+ def validate_akamai_dropped(origin_cc, akamai_cc)
86
+ dropped = origin_cc - akamai_cc
87
+ unless dropped.empty?
88
+ fail "Origin sent 'Cache-Control: #{dropped.to_a.join ','}', but Akamai did not."
89
+ end
90
+ end
91
+
92
+ def validate_akamai_added(origin_cc, akamai_cc)
93
+ added = akamai_cc - origin_cc
94
+ unless added.empty?
95
+ fail "Akamai unexpectedly added 'Cache-Control: #{added.to_a.join ','}'"
96
+ end
97
+ end
98
+
99
+ def check_cache_control(origin_response, akamai_response, headers)
100
+ if [:both, :cache_control].include? headers
101
+ origin_cc, akamai_cc = cc_directives(origin_response, akamai_response)
102
+ origin_cc, akamai_cc = check_max_age(origin_cc, akamai_cc)
103
+ validate_akamai_dropped(origin_cc, akamai_cc)
104
+ validate_akamai_added(origin_cc, akamai_cc)
105
+ end
106
+ end
107
+
108
+ def check_expires(origin_response, akamai_response, headers)
109
+ if [:both, :expires].include? headers
110
+ origin_expires, akamai_expires = expires(origin_response, akamai_response)
111
+ validate_expires(origin_expires, akamai_expires)
112
+ end
113
+ end
114
+
115
+ def validate_expires(origin, akamai)
116
+ unless akamai.to_i == origin.to_i
117
+ fail "Origin sent 'Expires: #{origin}' but Akamai sent 'Expires: #{akamai}'"
118
+ end
119
+ end
120
+
121
+ def expires(origin_response, akamai_response)
122
+ return origin_expires(origin_response), Time.httpdate(akamai_response.headers[:expires])
123
+ end
124
+
125
+ def origin_expires(origin_response)
126
+ expires = origin_response.headers[:expires]
127
+ expires == '0' ? Time.httpdate(origin_response.headers[:date]) : Time.httpdate(expires)
128
+ end
129
+
130
+ end
17
131
  end
18
132
  end
19
-
20
- def fix_date_header(origin_response)
21
- origin_response.headers[:date] ||= Time.now.httpdate
22
- origin_response
23
- end
24
-
25
- def origin_response(origin)
26
- fix_date_header(RestClient::Request.execute(method: :get, url: origin, verify_ssl: false))
27
- end
28
-
29
- def clean_cc_directives(origin_response, akamai_response)
30
- origin_cc_directives = origin_response.headers[:cache_control].split(/[, ]+/).to_set
31
- akamai_cc_directives = akamai_response.headers[:cache_control].split(/[, ]+/).to_set
32
-
33
- origin_cc_directives.delete 'must-revalidate' # as Akamai does no pass it on
34
- return origin_cc_directives, akamai_cc_directives
35
- end
36
-
37
- def cc_directives(origin_response, akamai_response)
38
- origin_cc, akamai_cc = clean_cc_directives(origin_response, akamai_response)
39
- check_cc(origin_cc, akamai_cc) unless (origin_cc & ['no-store', 'no-cache']).empty?
40
- return origin_cc, akamai_cc
41
- end
42
-
43
- def check_and_clean_header(origin_cc, akamai_cc, expected)
44
- unless akamai_cc.include? expected
45
- fail "Akamai was expected to, but did not, add 'Cache-Control: #{expected}' as Origin sent 'no-store' or 'no-cache'"
46
- end
47
- akamai_cc.delete expected unless origin_cc.include? expected
48
- akamai_cc
49
- end
50
-
51
- def check_cc(origin_cc, akamai_cc)
52
- ['no-store', 'max-age=0'].each do |expected|
53
- akamai_cc = check_and_clean_header(origin_cc, akamai_cc, expected)
54
- end
55
- return origin_cc, akamai_cc
56
- end
57
-
58
- def max_age(cc_directives)
59
- cc_directives.detect { |d| d.start_with? 'max-age=' }
60
- end
61
-
62
- def max_age_to_num(max_age)
63
- max_age.split('=').last.to_i
64
- end
65
-
66
- def clean_max_age(cc_directives)
67
- max_age = max_age(cc_directives)
68
- cc_directives.delete max_age if max_age
69
- return max_age_to_num(max_age), cc_directives
70
- end
71
-
72
- def check_max_age(origin_cc_directives, akamai_cc_directives)
73
- origin_max_age, origin_cc_directives = clean_max_age(origin_cc_directives)
74
- akamai_max_age, akamai_cc_directives = clean_max_age(akamai_cc_directives)
75
- if akamai_max_age > origin_max_age
76
- fail "Akamai sent a max-age greater than Origin's: #{akamai_max_age} > #{origin_max_age}"
77
- end
78
- return origin_cc_directives, akamai_cc_directives
79
- end
80
-
81
- def validate_akamai_dropped(origin_cc, akamai_cc)
82
- dropped = origin_cc - akamai_cc
83
- unless dropped.empty?
84
- fail "Origin sent 'Cache-Control: #{dropped.to_a.join ','}', but Akamai did not."
85
- end
86
- end
87
-
88
- def validate_akamai_added(origin_cc, akamai_cc)
89
- added = akamai_cc - origin_cc
90
- unless added.empty?
91
- fail "Akamai unexpectedly added 'Cache-Control: #{added.to_a.join ','}'"
92
- end
93
- end
94
-
95
- def check_cache_control(origin_response, akamai_response, headers)
96
- if [:both, :cache_control].include? headers
97
- origin_cc, akamai_cc = cc_directives(origin_response, akamai_response)
98
- origin_cc, akamai_cc = check_max_age(origin_cc, akamai_cc)
99
- validate_akamai_dropped(origin_cc, akamai_cc)
100
- validate_akamai_added(origin_cc, akamai_cc)
101
- end
102
- end
103
-
104
- def check_expires(origin_response, akamai_response, headers)
105
- if [:both, :expires].include? headers
106
- origin_expires, akamai_expires = expires(origin_response, akamai_response)
107
- validate_expires(origin_expires, akamai_expires)
108
- end
109
- end
110
-
111
- def validate_expires(origin, akamai)
112
- unless akamai.to_i == origin.to_i
113
- fail "Origin sent 'Expires: #{origin}' but Akamai sent 'Expires: #{akamai}'"
114
- end
115
- end
116
-
117
- def expires(origin_response, akamai_response)
118
- return origin_expires(origin_response), Time.httpdate(akamai_response.headers[:expires])
119
- end
120
-
121
- def origin_expires(origin_response)
122
- expires = origin_response.headers[:expires]
123
- expires == '0' ? Time.httpdate(origin_response.headers[:date]) : Time.httpdate(expires)
124
- end
@@ -3,76 +3,77 @@ require 'socket'
3
3
  require 'openssl'
4
4
  require 'uri'
5
5
 
6
- def check_ssl_serial(addr, port, url, serial)
7
- cert_serial = ssl_cert(addr, port, url).serial.to_s(16).upcase
8
- fail("Incorrect S/N of: #{cert_serial}") unless cert_serial == serial.upcase
9
- end
10
-
11
- def ssl_cert(addr, port, url)
12
- ssl_client = ssl_client_for_verify_cert(TCPSocket.new(addr, port), addr, url)
13
- # We get this after the request as we have layer 7 routing in Akamai
14
- cert = OpenSSL::X509::Certificate.new(ssl_client.peer_cert)
15
- ssl_client.sysclose
16
- cert
17
- end
6
+ module AkamaiRSpec
7
+ module Matchers
8
+ define :be_successful do |response_codes=(200..299)|
9
+ match do |url|
10
+ @response = AkamaiRSpec::Request.get url
11
+ response_codes === @response.code
12
+ end
18
13
 
19
- def dummy_request(url, addr)
20
- "GET #{url} HTTP/1.1\r\n" \
21
- 'User-Agent: Akamai-Regression-Framework\r\n' \
22
- "Host: #{addr}\r\n" \
23
- 'Accept: */*\r\n'
24
- end
14
+ failure_message do |url|
15
+ "Response #{@response} was not successful for #{url}"
16
+ end
17
+ end
25
18
 
26
- def ssl_client_for_verify_cert(tcp_client, addr, url)
27
- ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_client)
28
- ssl_client.sync_close = true
29
- ssl_client.connect
30
- ssl_client.puts(dummy_request(url, addr))
31
- ssl_client
32
- end
19
+ define :respond_with_headers do |headers|
20
+ match do |url|
21
+ @response = AkamaiRSpec::Request.get url
22
+ headers.each do |k, v|
23
+ fail "Expected header #{k} to be #{v}, got #{@response.headers[k]}" unless @response.headers[k] == v
24
+ end
25
+ true
26
+ end
27
+ end
33
28
 
34
- RSpec::Matchers.define :be_successful do
35
- match do |url|
36
- response = AkamaiRSpec::Request.get url
37
- fail('Response was not successful') unless response.code == 200
38
- true
39
- end
40
- end
29
+ define :be_verifiably_secure do
30
+ match do |url|
31
+ return false if URI(url).scheme == "http"
32
+ url = "https://#{url}" unless URI(url).scheme
33
+ begin
34
+ # Avoid AkamaiRspec::Request as it turns off SSL checking
35
+ @response = RestClient::Request.execute(method: :get, url: url, verify_ssl: OpenSSL::SSL::VERIFY_PEER)
36
+ return true
37
+ rescue => e
38
+ @error = e
39
+ return false
40
+ end
41
+ end
41
42
 
42
- RSpec::Matchers.define :be_verifiably_secure do (verify = OpenSSL::SSL::VERIFY_PEER)
43
- match do |url|
44
- return false if URI(url).scheme == "http"
45
- url = "https://#{url}" unless URI(url).scheme
46
- begin
47
- RestClient::Request.execute(method: :get, url: url, verify_ssl: verify)
48
- return true
49
- rescue => e
50
- return false
43
+ failure_message do |url|
44
+ "got error #{@error} fetching #{@response}"
45
+ end
51
46
  end
52
- end
53
- end
54
47
 
55
- RSpec::Matchers.define :be_gzipped do
56
- match do |response_or_url|
57
- response = AkamaiRSpec::Request.get_decode response_or_url
58
- response.headers[:content_encoding] == 'gzip'
59
- end
60
- end
48
+ define :be_gzipped do
49
+ match do |url|
50
+ @response = AkamaiRSpec::Request.get_decode url
51
+ @response.headers[:content_encoding] == 'gzip'
52
+ end
53
+ failure_message do |url|
54
+ "Expected #{url} to be gzipped (got #{@response})"
55
+ end
56
+ end
61
57
 
62
- RSpec::Matchers.define :have_cookie do |cookie|
63
- match do |response_or_url|
64
- response = AkamaiRSpec::Request.get response_or_url
65
- unless response.cookies[cookie]
66
- fail("Cookie #{cookie} not in #{response.cookies}")
58
+ define :have_cookie do |cookie, value=nil|
59
+ match do |response_or_url|
60
+ response = AkamaiRSpec::Request.get response_or_url
61
+ unless response.cookies[cookie]
62
+ fail("Cookie #{cookie} not in #{response.cookies}")
63
+ end
64
+ if value && response.cookies[cookie] != value
65
+ fail("Cookie #{cookie} was set to #{response.cookies[cookie]}, expected #{value}")
66
+ end
67
+ !!response.cookies[cookie]
68
+ end
67
69
  end
68
- response.cookies[cookie]
69
- end
70
- end
71
70
 
72
- RSpec::Matchers.define :be_forbidden do
73
- match do |url|
74
- response = AkamaiRSpec::Request.get url
75
- fail('Response was not forbidden') unless response.code == 403
76
- true
71
+ define :be_forbidden do
72
+ match do |url|
73
+ response = AkamaiRSpec::Request.get url
74
+ fail("Response #{response} was not forbidden") unless response.code == 403
75
+ true
76
+ end
77
+ end
77
78
  end
78
79
  end
@@ -1,28 +1,53 @@
1
1
  require 'rspec'
2
+ require 'uri'
3
+ require 'akamai_rspec/helpers/chainable_redirect'
2
4
 
3
- RSpec::Matchers.define :be_permanently_redirected_to do |expected_location|
4
- match do |url|
5
- redirect(url, expected_location, 301)
6
- end
7
- end
5
+ module AkamaiRSpec
6
+ module Matchers
7
+ define :be_permanently_redirected_to do |expected_location|
8
+ include AkamaiRSpec::Helpers::ChainableRedirect
9
+ match do |url|
10
+ redirect(url, expected_location, 301)
11
+ end
12
+ end
8
13
 
9
- RSpec::Matchers.define :be_temporarily_redirected_to do |expected_location|
10
- match do |url|
11
- redirect(url, expected_location, 302)
12
- end
13
- end
14
+ define :be_temporarily_redirected_to do |expected_location|
15
+ include AkamaiRSpec::Helpers::ChainableRedirect
16
+ match do |url|
17
+ redirect(url, expected_location, 302)
18
+ end
19
+ end
14
20
 
15
- RSpec::Matchers.define :be_temporarily_redirected_with_trailing_slash do
16
- match do |url|
17
- redirect(url, url + '/', 302)
18
- end
19
- end
21
+ define :redirect_http_to_https do |with: 301|
22
+ include AkamaiRSpec::Helpers::ChainableRedirect
23
+ match do |url|
24
+ secure, url = with_and_without_tls(url)
25
+ redirect(url, secure, with)
26
+ end
27
+ end
28
+
29
+ define :redirect_https_to_http do |with: 301|
30
+ include AkamaiRSpec::Helpers::ChainableRedirect
31
+ match do |url|
32
+ secure, url = with_and_without_tls(url)
33
+ redirect(secure, url, with)
34
+ end
35
+ end
36
+
37
+ define :redirect_to_add_trailing_slash do |with: 301|
38
+ include AkamaiRSpec::Helpers::ChainableRedirect
39
+ match do |url|
40
+ without_trailing_slash = url.gsub(/\/+$/, '')
41
+ with_trailing_slash = without_trailing_slash + "/"
42
+ redirect(without_trailing_slash, with_trailing_slash, with)
43
+ end
44
+ end
20
45
 
21
- def redirect(url, expected_location, expected_response_code)
22
- response = RestClient.get(url) { |response, _, _| response }
23
- fail "response was #{response.code}" unless response.code == expected_response_code
24
- unless response.headers[:location] == expected_location
25
- fail "redirect location was #{response.headers[:location]} (expected #{expected_location})"
46
+ define :be_temporarily_redirected_with_trailing_slash do
47
+ include AkamaiRSpec::Helpers::ChainableRedirect
48
+ match do |url|
49
+ redirect(url, url + '/', 302)
50
+ end
51
+ end
26
52
  end
27
- true
28
53
  end
@@ -1,29 +1,35 @@
1
1
  require 'rspec'
2
+ require 'akamai_rspec/helpers/cache_headers'
2
3
 
3
4
  module AkamaiRSpec
4
- module Helpers
5
- X_CACHE_HEADERS = [:x_true_cache_key, :x_cache_key]
5
+ module Matchers
6
+ define :be_served_from_origin do |contents|
7
+ include AkamaiRSpec::Helpers::CacheHeaders
6
8
 
7
- def x_cache_headers
8
- X_CACHE_HEADERS
9
+ match do |url|
10
+ @response = AkamaiRSpec::Request.get url
11
+ cache_headers.any? {|h| h.split("/").include? contents}
12
+ end
13
+
14
+ failure_message do |actual|
15
+ "expected \"#{actual}\" to be served from origin \"#{contents}\"; got #{@response} and the cache headers indicated '#{cache_headers.inspect}'"
16
+ end
9
17
  end
10
- end
11
- end
12
18
 
13
- RSpec::Matchers.define :be_served_from_origin do |contents|
14
- include AkamaiRSpec::Helpers
15
- match do |url|
16
- response = AkamaiRSpec::Request.get url
17
- response.headers.any? { |key, value| x_cache_headers.include?(key) && value =~ /\/#{contents}\// } && \
18
- response.code == 200
19
- end
20
- end
19
+ define :have_cp_code do |contents|
20
+ include AkamaiRSpec::Helpers::CacheHeaders
21
+ match do |url|
22
+ @response = AkamaiRSpec::Request.get url
23
+ @response.code == 200 && cache_headers.any? do |value|
24
+ value.to_s.split("/").include? contents
25
+ end
26
+ end
21
27
 
22
- RSpec::Matchers.define :have_cp_code do |contents|
23
- include AkamaiRSpec::Helpers
24
- match do |url|
25
- response = AkamaiRSpec::Request.get url
26
- response.headers.any? { |key, value| x_cache_headers.include?(key) && value == contents } && \
27
- response.code == 200
28
+ failure_message do |url|
29
+ headers = {}
30
+ x_cache_headers.each {|h| headers[h] = @response.headers[h] }
31
+ "Expected #{url} to have cp_code #{contents} but responded with #{headers.to_json}"
32
+ end
33
+ end
28
34
  end
29
35
  end
@@ -20,26 +20,42 @@ module AkamaiRSpec
20
20
  @@env = env
21
21
  end
22
22
 
23
- def self.get(url)
24
- new.get(url)
23
+ def self.get(url, headers={})
24
+ new.get(url, headers.merge(debug_headers))
25
25
  end
26
26
 
27
- def self.get_with_debug_headers(url)
28
- new.get(url, AkamaiHeaders.akamai_debug_headers)
27
+ def self.get_without_debug_headers(url, headers={})
28
+ new.get(url, headers)
29
29
  end
30
30
 
31
31
  def self.get_cache_miss(url)
32
32
  url += url.include?('?') ? '&' : '?'
33
33
  url += SecureRandom.hex
34
- new.get(url, AkamaiHeaders.akamai_debug_headers)
34
+ get(url)
35
35
  end
36
36
 
37
37
  def self.get_decode(url)
38
- response = new.get(url, AkamaiHeaders.akamai_debug_headers)
38
+ response = new.get(url, debug_headers.merge({'Accept-Encoding' => 'gzip'}))
39
39
  RestClient::Request.decode(response.headers[:content_encoding], response.body)
40
40
  response
41
41
  end
42
42
 
43
+ def self.debug_headers
44
+ {
45
+ pragma: [
46
+ 'akamai-x-cache-on',
47
+ 'akamai-x-cache-remote-on',
48
+ 'akamai-x-check-cacheable',
49
+ 'akamai-x-get-cache-key',
50
+ 'akamai-x-get-extracted-values',
51
+ 'akamai-x-get-nonces',
52
+ 'akamai-x-get-ssl-client-session-id',
53
+ 'akamai-x-get-true-cache-key',
54
+ 'akamai-x-serial-no'
55
+ ].join(", ")
56
+ }
57
+ end
58
+
43
59
  def initialize
44
60
  @@env ||= 'prod'
45
61
 
@@ -47,7 +63,7 @@ module AkamaiRSpec
47
63
  when 'staging'
48
64
  if @@akamai_stg_domain.nil?
49
65
  raise ArgumentError.new(
50
- "You must set the prod domain: AkamaiRSpec::Request.stg_domain = 'www.example.com.edgesuite.net'"
66
+ "You must set the staging domain: AkamaiRSpec::Request.stg_domain = 'www.example.com.edgesuite.net'"
51
67
  )
52
68
  end
53
69
 
@@ -70,12 +86,18 @@ module AkamaiRSpec
70
86
  delegate [:parse_url_with_auth, :stringify_headers] => :@rest_client
71
87
 
72
88
  def get(url, headers = {})
73
- if url.is_a? RestClient::Response
89
+ # DO NOT USE url.is_a? here - some versions of
90
+ # the JSON gem monkey patch String which causes it to match.
91
+
92
+ if url.class.ancestors.include? RestClient::Response
74
93
  return AkamaiRSpec::Response.new(url)
75
94
  end
76
95
 
77
- uri = parse_url_with_auth(url)
96
+ if url.class.ancestors.include? AkamaiRSpec::Response
97
+ return url
98
+ end
78
99
 
100
+ uri = parse_url_with_auth(url)
79
101
  req = build_request(uri, stringify_headers(headers))
80
102
 
81
103
  req['Host'] = uri.hostname
@@ -6,6 +6,7 @@ module AkamaiRSpec
6
6
 
7
7
  def headers
8
8
  headers = Hash[@response.to_hash.map{ |k, v| [k.gsub(/-/,'_').downcase.to_sym, v] }]
9
+ headers.default = ""
9
10
  headers.each do |k, v|
10
11
  if v.is_a?(Array) && v.size == 1
11
12
  headers[k] = v.first
@@ -20,26 +21,36 @@ module AkamaiRSpec
20
21
  end
21
22
 
22
23
  def cookies
23
- cookie_header = headers.to_hash[:set_cookie]
24
- if cookie_header
25
- if cookie_header.is_a?(Array)
26
- cookies_string = cookie_header.collect do |header_value|
27
- header_value.split('; ')
28
- end
29
- cookies_string.flatten!
30
- cookies_array = cookies_string.collect { |c| c.split('=') }
31
- else
32
- cookies_array = [cookie_header.split('=')]
33
- end
24
+ cookies = {}
34
25
 
35
- Hash[cookies_array]
36
- else
37
- {}
26
+ [headers[:set_cookie]].flatten.each do |cookie|
27
+ name, value = cookie.split(/=/, 2)
28
+ cookies[name] = value
38
29
  end
30
+ cookies
39
31
  end
40
32
 
41
33
  def method_missing(method, *args)
42
34
  @response.send(method, *args)
43
35
  end
36
+
37
+ def to_s
38
+ case code
39
+ when 0..99
40
+ "Invalid status code #{code}"
41
+ when 100..199
42
+ "Informational: #{code}"
43
+ when 200..299
44
+ "Success: #{code}"
45
+ when 300..399
46
+ "Redirect #{code} to #{headers[:location]}"
47
+ when 400..499
48
+ "Client error #{code}"
49
+ when 500..599
50
+ "Server error #{code}"
51
+ else
52
+ "Unknown status code #{code}"
53
+ end
54
+ end
44
55
  end
45
56
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: akamai_rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bianca Gibson
8
+ - Daniel Heath
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2016-10-21 00:00:00.000000000 Z
12
+ date: 2016-10-28 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rest-client
@@ -59,10 +60,12 @@ extensions: []
59
60
  extra_rdoc_files: []
60
61
  files:
61
62
  - lib/akamai_rspec.rb
62
- - lib/akamai_rspec/akamai_headers.rb
63
+ - lib/akamai_rspec/helpers/cache_headers.rb
64
+ - lib/akamai_rspec/helpers/chainable_redirect.rb
65
+ - lib/akamai_rspec/matchers.rb
63
66
  - lib/akamai_rspec/matchers/caching.rb
67
+ - lib/akamai_rspec/matchers/forward_to_index.rb
64
68
  - lib/akamai_rspec/matchers/honour_origin_headers.rb
65
- - lib/akamai_rspec/matchers/matchers.rb
66
69
  - lib/akamai_rspec/matchers/non_akamai.rb
67
70
  - lib/akamai_rspec/matchers/redirects.rb
68
71
  - lib/akamai_rspec/matchers/x_cache_headers.rb
@@ -1,7 +0,0 @@
1
- module AkamaiHeaders
2
- def self.akamai_debug_headers
3
- {
4
- pragma: 'akamai-x-cache-on, akamai-x-cache-remote-on, akamai-x-check-cacheable, akamai-x-get-cache-key, akamai-x-get-extracted-values, akamai-x-get-nonces, akamai-x-get-ssl-client-session-id, akamai-x-get-true-cache-key, akamai-x-serial-no'
5
- }
6
- end
7
- end
@@ -1,25 +0,0 @@
1
- require 'securerandom'
2
- require 'rspec'
3
- require_relative 'redirects'
4
- require_relative 'caching'
5
- require_relative 'non_akamai'
6
- require_relative 'honour_origin_headers'
7
- require_relative 'x_cache_headers'
8
- include AkamaiHeaders
9
-
10
- RSpec::Matchers.define :be_forwarded_to_index do |channel|
11
- match do |url|
12
- response = RestClient.get(url, akamai_debug_headers)
13
-
14
- session_info = response.raw_headers['x-akamai-session-info']
15
- if session_info.nil?
16
- fail("x-akamai-session-info not found in the headers '#{response.raw_headers}'")
17
- end
18
- outcome_attribute = session_info.find { |header| header.include? 'AKA_PM_FWD_URL' }
19
- if outcome_attribute.nil?
20
- fail("AKA_PM_FWD_URL not found in the x-akamai-session-info header '#{session_info}'")
21
- end
22
- outcome_url = outcome_attribute.split('value=')[1]
23
- response.code == 200 && outcome_url == "#{channel}"
24
- end
25
- end