jeff 0.4.3 → 0.5.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.
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