jeff 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -7,14 +7,6 @@
7
7
 
8
8
  ![jeff][jeff]
9
9
 
10
- ## Usage
11
-
12
- ```ruby
13
- class Service
14
- include Jeff
15
- end
16
- ```
17
-
18
10
  [stat]: https://secure.travis-ci.org/papercavalier/jeff.png
19
11
  [trav]: http://travis-ci.org/papercavalier/jeff
20
12
  [aws]: http://aws.amazon.com/
data/jeff.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ['lib']
17
17
  gem.version = Jeff::VERSION
18
18
 
19
- gem.add_dependency 'excon', '~> 0.16.0'
19
+ gem.add_dependency 'excon', '~> 0.18.0'
20
20
  gem.add_development_dependency 'rake'
21
21
  gem.add_development_dependency 'rspec'
22
22
 
data/lib/jeff.rb CHANGED
@@ -1,7 +1,180 @@
1
- require 'jeff/serviceable'
1
+ require 'base64'
2
+ require 'digest/md5'
3
+ require 'excon'
4
+ require 'time'
2
5
 
6
+ require 'jeff/secret'
7
+ require 'jeff/version'
8
+
9
+ # Mixes in Amazon Web Services (AWS) client behaviour.
3
10
  module Jeff
11
+ MissingEndpoint = Class.new ArgumentError
12
+ MissingKey = Class.new ArgumentError
13
+ MissingSecret = Class.new ArgumentError
14
+
15
+ UNRESERVED = /([^\w.~-]+)/
16
+
17
+ # A User-Agent header that identifies the application, its version number,
18
+ # and programming language.
19
+ #
20
+ # Amazon recommends to include one in requests to AWS endpoints.
21
+ USER_AGENT = "Jeff/#{VERSION} (Language=Ruby; #{`hostname`.chomp})"
22
+
4
23
  def self.included(base)
5
- base.send :include, Serviceable
24
+ base.extend ClassMethods
25
+
26
+ base.headers 'User-Agent' => USER_AGENT
27
+
28
+ base.params 'AWSAccessKeyId' => -> { key },
29
+ 'SignatureVersion' => '2',
30
+ 'SignatureMethod' => 'HmacSHA256',
31
+ 'Timestamp' => -> { Time.now.utc.iso8601 }
32
+ end
33
+
34
+ # Internal: Builds a sorted query.
35
+ #
36
+ # hsh - A hash of query parameters specific to the request.
37
+ #
38
+ # Returns a query String.
39
+ def build_query(hsh)
40
+ params
41
+ .merge(hsh)
42
+ .sort
43
+ .map { |k, v| "#{k}=#{ escape v }" }
44
+ .join('&')
45
+ end
46
+
47
+ # Internal: Returns an Excon::Connection.
48
+ def connection
49
+ @connection ||= Excon.new(endpoint, headers: headers, expects: 200)
50
+ end
51
+
52
+ # Internal: Gets the String AWS endpoint.
53
+ #
54
+ # Raises a MissingEndpoint error if endpoint is missing.
55
+ def endpoint
56
+ @endpoint or raise MissingEndpoint
57
+ end
58
+
59
+ # Sets the String AWS endpoint.
60
+ attr_writer :endpoint
61
+
62
+ # Internal: Returns the Hash default headers.
63
+ def headers
64
+ self.class.headers
65
+ end
66
+
67
+ # Internal: Gets the String AWS access key id.
68
+ #
69
+ # Raises a MissingKey error if key is missing.
70
+ def key
71
+ @key or raise MissingKey
72
+ end
73
+
74
+ # Sets the String AWS access key id.
75
+ attr_writer :key
76
+
77
+ # Internal: Returns the Hash default request parameters.
78
+ def params
79
+ self.class.params.reduce({}) do |a, (k, v)|
80
+ a.update k => (v.respond_to?(:call) ? instance_exec(&v) : v)
81
+ end
82
+ end
83
+
84
+ # Internal: Gets the Jeff::Secret.
85
+ #
86
+ # Raises a MissingSecret error if secret is missing.
87
+ def secret
88
+ @secret or raise MissingSecret
89
+ end
90
+
91
+ # Sets the AWS secret key.
92
+ #
93
+ # key - A String secret.
94
+ #
95
+ # Returns a Jeff::Secret.
96
+ def secret=(key)
97
+ @secret = Secret.new(key)
98
+ end
99
+
100
+ # Generate HTTP request verb methods.
101
+ Excon::HTTP_VERBS.each do |method|
102
+ eval <<-DEF
103
+ def #{method}(opts = {})
104
+ opts.update :method => :#{method}
105
+ if opts[:body]
106
+ opts[:headers] ||= {}
107
+ opts[:headers].update 'Content-MD5' => calculate_md5(opts[:body])
108
+ end
109
+
110
+ connection.request(sign(opts))
111
+ end
112
+ DEF
113
+ end
114
+
115
+ private
116
+
117
+ def calculate_md5(body)
118
+ Base64.encode64(Digest::MD5.digest(body)).strip
119
+ end
120
+
121
+ def connection_host
122
+ [
123
+ connection.data[:host],
124
+ connection.data[:port]
125
+ ].join ':'
126
+ end
127
+
128
+ def connection_path
129
+ connection.data[:path]
130
+ end
131
+
132
+ def escape(val)
133
+ val.to_s.gsub(UNRESERVED) do
134
+ '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
135
+ end
136
+ end
137
+
138
+ def sign(opts)
139
+ query = build_query(opts[:query] || {})
140
+
141
+ string_to_sign = [
142
+ opts[:method].upcase,
143
+ connection_host,
144
+ opts[:path] || connection_path,
145
+ query
146
+ ].join "\n"
147
+ signature = secret.sign(string_to_sign)
148
+
149
+ opts.update query: [
150
+ query,
151
+ "Signature=#{escape(signature)}"
152
+ ].join('&')
153
+ end
154
+
155
+ module ClassMethods
156
+ # Gets/Updates the default headers.
157
+ #
158
+ # hsh - A Hash of headers.
159
+ #
160
+ # Returns the Hash headers.
161
+ def headers(hsh = nil)
162
+ @headers ||= {}
163
+ @headers.update(hsh) if hsh
164
+
165
+ @headers
166
+ end
167
+
168
+ # Gets/Updates the default request parameters.
169
+ #
170
+ # hsh - A Hash of parameters (default: nil).
171
+ #
172
+ # Returns the Hash parameters.
173
+ def params(hsh = nil)
174
+ @params ||= {}
175
+ @params.update(hsh) if hsh
176
+
177
+ @params
178
+ end
6
179
  end
7
180
  end
data/lib/jeff/secret.rb CHANGED
@@ -16,7 +16,7 @@ module Jeff
16
16
  private
17
17
 
18
18
  def digest(message)
19
- OpenSSL::HMAC.digest SHA256, @key, message
19
+ OpenSSL::HMAC.digest(SHA256, @key, message)
20
20
  end
21
21
  end
22
22
  end
data/lib/jeff/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jeff
2
- VERSION = '0.4.3'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -11,6 +11,7 @@ module Jeff
11
11
  secret.should_receive(:digest)
12
12
  .and_return double.as_null_object
13
13
  end
14
+
14
15
  it 'should Base64-encode' do
15
16
  Base64.should_receive(:encode64)
16
17
  .and_return double.as_null_object
data/spec/jeff_spec.rb ADDED
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jeff do
4
+ let(:klass) { Class.new { include Jeff } }
5
+ let(:client) { klass.new }
6
+
7
+ describe '.headers' do
8
+ subject { klass.headers }
9
+
10
+ it { should have_key 'User-Agent' }
11
+
12
+ it 'should be configurable' do
13
+ klass.instance_eval do
14
+ headers 'Foo' => 'bar'
15
+ end
16
+
17
+ should have_key 'Foo'
18
+ end
19
+ end
20
+
21
+ describe '.params' do
22
+ subject { klass.params }
23
+
24
+ it { should have_key 'AWSAccessKeyId' }
25
+
26
+ it { should have_key 'SignatureMethod' }
27
+
28
+ it { should have_key 'SignatureVersion' }
29
+
30
+ it { should have_key 'Timestamp' }
31
+
32
+ it 'should be configurable' do
33
+ klass.instance_eval do
34
+ params 'Foo' => 'bar'
35
+ end
36
+
37
+ should have_key 'Foo'
38
+ end
39
+ end
40
+
41
+ describe '#endpoint' do
42
+ it 'should require a value' do
43
+ expect { client.endpoint }.to raise_error Jeff::MissingEndpoint
44
+ end
45
+ end
46
+
47
+ describe '#key' do
48
+ it 'should require a value' do
49
+ expect { client.key }.to raise_error Jeff::MissingKey
50
+ end
51
+ end
52
+
53
+ describe '#secret' do
54
+ it 'should require a value' do
55
+ expect { client.secret }.to raise_error Jeff::MissingSecret
56
+ end
57
+ end
58
+
59
+ context 'given a key' do
60
+ before do
61
+ client.key = 'key'
62
+ end
63
+
64
+ describe '#params' do
65
+ subject { client.params }
66
+
67
+ it 'should include the key' do
68
+ subject['AWSAccessKeyId'].should eql client.key
69
+ end
70
+
71
+ it 'should generate a timestamp' do
72
+ subject['Timestamp'].should be_a String
73
+ end
74
+ end
75
+
76
+ describe '#build_query' do
77
+ subject { client.build_query 'A10' => 1, 'A1' => 1 }
78
+
79
+ it 'should include default parameters' do
80
+ should match(/Timestamp/)
81
+ end
82
+
83
+ it 'should sort lexicographically' do
84
+ should match(/^A1=1&A10=/)
85
+ end
86
+ end
87
+ end
88
+
89
+ context 'given an endpoint' do
90
+ before do
91
+ client.endpoint = 'http://slowapi.com/delay/0'
92
+ end
93
+
94
+ describe "#connection" do
95
+ subject { client.connection }
96
+ let(:headers) { subject.data[:headers] }
97
+
98
+ it { should be_an Excon::Connection }
99
+
100
+ it 'should set default headers' do
101
+ headers.should eq klass.headers
102
+ end
103
+
104
+ it 'should cache itself' do
105
+ subject.should be client.connection
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'given an endpoint, key, and secret' do
111
+ before do
112
+ client.endpoint = 'http://slowapi.com/delay/0'
113
+ client.key = 'key'
114
+ client.secret = 'secret'
115
+ end
116
+
117
+ Excon::HTTP_VERBS.each do |method|
118
+ describe "##{method}" do
119
+ subject { client.send(method, mock: true).body }
120
+
121
+ before do
122
+ Excon.stub({ method: method.to_sym }) do
123
+ { body: method, status: 200 }
124
+ end
125
+ end
126
+
127
+ after { Excon.stubs.clear }
128
+
129
+ it "should make a #{method.upcase} request" do
130
+ should eql method
131
+ end
132
+ end
133
+ end
134
+
135
+ context 'given a request body' do
136
+ subject { client.get(mock: true, body: 'foo').body }
137
+
138
+ before do
139
+ Excon.stub({ method: :get }) do |params|
140
+ { status: 200, body: params[:headers]['Content-MD5'] }
141
+ end
142
+ end
143
+
144
+ after { Excon.stubs.clear }
145
+
146
+ it "should add an Content-MD5 header" do
147
+ should_not be_empty
148
+ end
149
+ end
150
+
151
+ context 'given an HTTP status error' do
152
+ before do
153
+ Excon.stub({ method: :get }) do
154
+ { status: 503 }
155
+ end
156
+ end
157
+
158
+ after { Excon.stubs.clear }
159
+
160
+ it "should raise an error" do
161
+ expect {
162
+ client.get mock: true
163
+ }.to raise_error Excon::Errors::HTTPStatusError
164
+ end
165
+ end
166
+ end
167
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,4 @@
1
- begin
2
- require 'pry'
3
- require 'pry-doc'
4
- rescue LoadError
5
- end
6
1
  require 'rspec'
7
-
8
2
  require 'jeff'
9
3
 
10
4
  RSpec.configure do |c|
metadata CHANGED
@@ -1,64 +1,64 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jeff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
5
4
  prerelease:
5
+ version: 0.5.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Hakan Ensari
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-19 00:00:00.000000000 Z
12
+ date: 2013-02-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: excon
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
15
+ version_requirements: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
- version: 0.16.0
19
+ version: 0.18.0
20
+ none: false
21
+ name: excon
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
24
+ requirement: !ruby/object:Gem::Requirement
26
25
  requirements:
27
26
  - - ~>
28
27
  - !ruby/object:Gem::Version
29
- version: 0.16.0
30
- - !ruby/object:Gem::Dependency
31
- name: rake
32
- requirement: !ruby/object:Gem::Requirement
28
+ version: 0.18.0
33
29
  none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
34
32
  requirements:
35
33
  - - ! '>='
36
34
  - !ruby/object:Gem::Version
37
35
  version: '0'
36
+ none: false
37
+ name: rake
38
38
  type: :development
39
39
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
40
+ requirement: !ruby/object:Gem::Requirement
42
41
  requirements:
43
42
  - - ! '>='
44
43
  - !ruby/object:Gem::Version
45
44
  version: '0'
46
- - !ruby/object:Gem::Dependency
47
- name: rspec
48
- requirement: !ruby/object:Gem::Requirement
49
45
  none: false
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
50
48
  requirements:
51
49
  - - ! '>='
52
50
  - !ruby/object:Gem::Version
53
51
  version: '0'
52
+ none: false
53
+ name: rspec
54
54
  type: :development
55
55
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
56
+ requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
58
  - - ! '>='
60
59
  - !ruby/object:Gem::Version
61
60
  version: '0'
61
+ none: false
62
62
  description: Minimum-viable Amazon Web Services (AWS) client
63
63
  email:
64
64
  - hakan.ensari@papercavalier.com
@@ -75,10 +75,9 @@ files:
75
75
  - jeff.gemspec
76
76
  - lib/jeff.rb
77
77
  - lib/jeff/secret.rb
78
- - lib/jeff/serviceable.rb
79
78
  - lib/jeff/version.rb
80
79
  - spec/jeff/secret_spec.rb
81
- - spec/jeff/serviceable_spec.rb
80
+ - spec/jeff_spec.rb
82
81
  - spec/spec_helper.rb
83
82
  homepage: https://github.com/papercavalier/jeff
84
83
  licenses: []
@@ -87,17 +86,17 @@ rdoc_options: []
87
86
  require_paths:
88
87
  - lib
89
88
  required_ruby_version: !ruby/object:Gem::Requirement
90
- none: false
91
89
  requirements:
92
90
  - - ! '>='
93
91
  - !ruby/object:Gem::Version
94
92
  version: '1.9'
95
- required_rubygems_version: !ruby/object:Gem::Requirement
96
93
  none: false
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
95
  requirements:
98
96
  - - ! '>='
99
97
  - !ruby/object:Gem::Version
100
98
  version: '0'
99
+ none: false
101
100
  requirements: []
102
101
  rubyforge_project:
103
102
  rubygems_version: 1.8.23
@@ -106,6 +105,5 @@ specification_version: 3
106
105
  summary: AWS client
107
106
  test_files:
108
107
  - spec/jeff/secret_spec.rb
109
- - spec/jeff/serviceable_spec.rb
108
+ - spec/jeff_spec.rb
110
109
  - spec/spec_helper.rb
111
- has_rdoc:
@@ -1,184 +0,0 @@
1
- require 'base64'
2
- require 'digest/md5'
3
- require 'excon'
4
- require 'time'
5
-
6
- require 'jeff/secret'
7
- require 'jeff/version'
8
-
9
- module Jeff
10
- # Mixes in Amazon Web Services (AWS) client behaviour.
11
- module Serviceable
12
- MissingEndpoint = Class.new ArgumentError
13
- MissingKey = Class.new ArgumentError
14
- MissingSecret = Class.new ArgumentError
15
-
16
- UNRESERVED = /([^\w.~-]+)/
17
-
18
- # A User-Agent header that identifies the application, its version number,
19
- # and programming language.
20
- #
21
- # Amazon recommends to include one in requests to AWS endpoints.
22
- USER_AGENT = "Jeff/#{VERSION} (Language=Ruby; #{`hostname`.chomp})"
23
-
24
- def self.included(base)
25
- base.extend ClassMethods
26
-
27
- base.headers 'User-Agent' => USER_AGENT
28
-
29
- base.params 'AWSAccessKeyId' => -> { key },
30
- 'SignatureVersion' => '2',
31
- 'SignatureMethod' => 'HmacSHA256',
32
- 'Timestamp' => -> { Time.now.utc.iso8601 }
33
- end
34
-
35
- # Internal: Builds a sorted query.
36
- #
37
- # hsh - A hash of query parameters specific to the request.
38
- #
39
- # Returns a query String.
40
- def build_query(hsh)
41
- params
42
- .merge(hsh)
43
- .sort
44
- .map { |k, v| "#{k}=#{ escape v }" }
45
- .join '&'
46
- end
47
-
48
- # Internal: Returns an Excon::Connection.
49
- def connection
50
- @connection ||= Excon.new endpoint,
51
- headers: headers,
52
- expects: 200
53
- end
54
-
55
- # Internal: Gets the String AWS endpoint.
56
- #
57
- # Raises a MissingEndpoint error if endpoint is missing.
58
- def endpoint
59
- @endpoint or raise MissingEndpoint
60
- end
61
-
62
- # Sets the String AWS endpoint.
63
- attr_writer :endpoint
64
-
65
- # Internal: Returns the Hash default headers.
66
- def headers
67
- self.class.headers
68
- end
69
-
70
- # Internal: Gets the String AWS access key id.
71
- #
72
- # Raises a MissingKey error if key is missing.
73
- def key
74
- @key or raise MissingKey
75
- end
76
-
77
- # Sets the String AWS access key id.
78
- attr_writer :key
79
-
80
- # Internal: Returns the Hash default request parameters.
81
- def params
82
- self.class.params.reduce({}) do |a, (k, v)|
83
- a.update k => (v.respond_to?(:call) ? instance_exec(&v) : v)
84
- end
85
- end
86
-
87
- # Internal: Gets the Jeff::Secret.
88
- #
89
- # Raises a MissingSecret error if secret is missing.
90
- def secret
91
- @secret or raise MissingSecret
92
- end
93
-
94
- # Sets the AWS secret key.
95
- #
96
- # key - A String secret.
97
- #
98
- # Returns a Jeff::Secret.
99
- def secret=(key)
100
- @secret = Secret.new key
101
- end
102
-
103
- # Generate HTTP request verb methods.
104
- Excon::HTTP_VERBS.each do |method|
105
- eval <<-DEF
106
- def #{method}(opts = {})
107
- opts.update method: :#{method}
108
- if opts[:body]
109
- opts[:headers] ||= {}
110
- opts[:headers].update 'Content-MD5' => calculate_md5(opts[:body])
111
- end
112
-
113
- connection.request sign opts
114
- end
115
- DEF
116
- end
117
-
118
- private
119
-
120
- def calculate_md5(body)
121
- Base64.encode64(Digest::MD5.digest(body)).strip
122
- end
123
-
124
- def connection_host
125
- [
126
- connection.connection[:host],
127
- connection.connection[:port]
128
- ].join ':'
129
- end
130
-
131
- def connection_path
132
- connection.connection[:path]
133
- end
134
-
135
- def escape(val)
136
- val.to_s.gsub(UNRESERVED) do
137
- '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
138
- end
139
- end
140
-
141
- def sign(opts)
142
- query = build_query opts[:query] || {}
143
-
144
- string_to_sign = [
145
- opts[:method].upcase,
146
- connection_host,
147
- opts[:path] || connection_path,
148
- query
149
- ].join "\n"
150
- signature = secret.sign string_to_sign
151
-
152
- opts.update query: [
153
- query,
154
- "Signature=#{escape signature}"
155
- ].join('&')
156
- end
157
-
158
- module ClassMethods
159
- # Gets/Updates the default headers.
160
- #
161
- # hsh - A Hash of headers.
162
- #
163
- # Returns the Hash headers.
164
- def headers(hsh = nil)
165
- @headers ||= {}
166
- @headers.update hsh if hsh
167
-
168
- @headers
169
- end
170
-
171
- # Gets/Updates the default request parameters.
172
- #
173
- # hsh - A Hash of parameters (default: nil).
174
- #
175
- # Returns the Hash parameters.
176
- def params(hsh = nil)
177
- @params ||= {}
178
- @params.update hsh if hsh
179
-
180
- @params
181
- end
182
- end
183
- end
184
- end
@@ -1,169 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Jeff
4
- describe Serviceable do
5
- let(:klass) { Class.new { include Jeff } }
6
- let(:client) { klass.new }
7
-
8
- describe '.headers' do
9
- subject { klass.headers }
10
-
11
- it { should have_key 'User-Agent' }
12
-
13
- it 'should be configurable' do
14
- klass.instance_eval do
15
- headers 'Foo' => 'bar'
16
- end
17
-
18
- should have_key 'Foo'
19
- end
20
- end
21
-
22
- describe '.params' do
23
- subject { klass.params }
24
-
25
- it { should have_key 'AWSAccessKeyId' }
26
-
27
- it { should have_key 'SignatureMethod' }
28
-
29
- it { should have_key 'SignatureVersion' }
30
-
31
- it { should have_key 'Timestamp' }
32
-
33
- it 'should be configurable' do
34
- klass.instance_eval do
35
- params 'Foo' => 'bar'
36
- end
37
-
38
- should have_key 'Foo'
39
- end
40
- end
41
-
42
- describe '#endpoint' do
43
- it 'should require a value' do
44
- expect { client.endpoint }.to raise_error Serviceable::MissingEndpoint
45
- end
46
- end
47
-
48
- describe '#key' do
49
- it 'should require a value' do
50
- expect { client.key }.to raise_error Serviceable::MissingKey
51
- end
52
- end
53
-
54
- describe '#secret' do
55
- it 'should require a value' do
56
- expect { client.secret }.to raise_error Serviceable::MissingSecret
57
- end
58
- end
59
-
60
- context 'given a key' do
61
- before do
62
- client.key = 'key'
63
- end
64
-
65
- describe '#params' do
66
- subject { client.params }
67
-
68
- it 'should include the key' do
69
- subject['AWSAccessKeyId'].should eql client.key
70
- end
71
-
72
- it 'should generate a timestamp' do
73
- subject['Timestamp'].should be_a String
74
- end
75
- end
76
-
77
- describe '#build_query' do
78
- subject { client.build_query 'A10' => 1, 'A1' => 1 }
79
-
80
- it 'should include default parameters' do
81
- should match(/Timestamp/)
82
- end
83
-
84
- it 'should sort lexicographically' do
85
- should match(/^A1=1&A10=/)
86
- end
87
- end
88
- end
89
-
90
- context 'given an endpoint' do
91
- before do
92
- client.endpoint = 'http://slowapi.com/delay/0'
93
- end
94
-
95
- describe "#connection" do
96
- subject { client.connection }
97
- let(:headers) { subject.connection[:headers] }
98
-
99
- it { should be_an Excon::Connection }
100
-
101
- it 'should set default headers' do
102
- headers.should eq klass.headers
103
- end
104
-
105
- it 'should cache itself' do
106
- subject.should be client.connection
107
- end
108
- end
109
- end
110
-
111
- context 'given an endpoint, key, and secret' do
112
- before do
113
- client.endpoint = 'http://slowapi.com/delay/0'
114
- client.key = 'key'
115
- client.secret = 'secret'
116
- end
117
-
118
- Excon::HTTP_VERBS.each do |method|
119
- describe "##{method}" do
120
- subject { client.send(method, mock: true).body }
121
-
122
- before do
123
- Excon.stub({ method: method.to_sym }) do
124
- { body: method, status: 200 }
125
- end
126
- end
127
-
128
- after { Excon.stubs.clear }
129
-
130
- it "should make a #{method.upcase} request" do
131
- should eql method
132
- end
133
- end
134
- end
135
-
136
- context 'given a request body' do
137
- subject { client.get(mock: true, body: 'foo').body }
138
-
139
- before do
140
- Excon.stub({ method: :get }) do |params|
141
- { status: 200, body: params[:headers]['Content-MD5'] }
142
- end
143
- end
144
-
145
- after { Excon.stubs.clear }
146
-
147
- it "should add an Content-MD5 header" do
148
- should_not be_empty
149
- end
150
- end
151
-
152
- context 'given an HTTP status error' do
153
- before do
154
- Excon.stub({ method: :get }) do
155
- { status: 503 }
156
- end
157
- end
158
-
159
- after { Excon.stubs.clear }
160
-
161
- it "should raise an error" do
162
- expect {
163
- client.get mock: true
164
- }.to raise_error Excon::Errors::HTTPStatusError
165
- end
166
- end
167
- end
168
- end
169
- end