songkick-transport 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,76 @@
1
+ require "spec_helper"
2
+
3
+ describe Songkick::Transport::Response do
4
+ def process(*args)
5
+ Songkick::Transport::Response.process(*args)
6
+ end
7
+
8
+ describe "200 with a body" do
9
+ let(:response) { process("", 200, {}, '{"hello":"world"}') }
10
+
11
+ it "is an OK" do
12
+ response.should be_a(Songkick::Transport::Response::OK)
13
+ end
14
+
15
+ it "exposes its data" do
16
+ response.data.should == {"hello" => "world"}
17
+ end
18
+ end
19
+
20
+ describe "200 with a body with an empty line in it" do
21
+ let(:response) { process("", 200, {}, "{\"hello\":\"world\"\n\n}") }
22
+
23
+ it "is an OK" do
24
+ response.should be_a(Songkick::Transport::Response::OK)
25
+ end
26
+
27
+ it "exposes its data" do
28
+ response.data.should == {"hello" => "world"}
29
+ end
30
+ end
31
+
32
+ describe "200 with a parsed body" do
33
+ let(:response) { process("", 200, {}, {"hello" => "world"}) }
34
+
35
+ it "exposes its data" do
36
+ response.data.should == {"hello" => "world"}
37
+ end
38
+ end
39
+
40
+ describe "200 with an empty body" do
41
+ let(:response) { process("", 200, {}, "") }
42
+
43
+ it "exposes its data" do
44
+ response.data.should be_nil
45
+ end
46
+ end
47
+
48
+ describe "201 with an empty body" do
49
+ let(:response) { process("", 201, {}, "") }
50
+
51
+ it "is a Created" do
52
+ response.should be_a(Songkick::Transport::Response::Created)
53
+ end
54
+ end
55
+
56
+ describe "204 with an empty body" do
57
+ let(:response) { process("", 204, {}, "") }
58
+
59
+ it "is a NoContent" do
60
+ response.should be_a(Songkick::Transport::Response::NoContent)
61
+ end
62
+ end
63
+
64
+ describe "409 with a body" do
65
+ let(:response) { process("", 409, {}, '{"errors":[]}') }
66
+
67
+ it "is a UserError" do
68
+ response.should be_a(Songkick::Transport::Response::UserError)
69
+ end
70
+
71
+ it "exposes the errors" do
72
+ response.errors.should == []
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,189 @@
1
+ require "spec_helper"
2
+
3
+ describe Songkick::Transport do
4
+ shared_examples_for "transport" do
5
+ before { TestApp.listen(4567) }
6
+ after { TestApp.stop }
7
+
8
+ describe :get do
9
+ it "retrieves data using GET" do
10
+ transport.get("/artists/99").data.should == {"id" => 99}
11
+ end
12
+
13
+ it "exposes the response headers" do
14
+ transport.get("/artists/99").headers["content-type"].should == "application/json"
15
+ end
16
+
17
+ it "can send array params" do
18
+ transport.get("/", :list => %w[a b]).data.should == {"list" => ["a", "b"]}
19
+ end
20
+
21
+ it "can send hash params" do
22
+ transport.get("/", :list => {:a => "b"}).data.should == {"list" => {"a" => "b"}}
23
+ end
24
+
25
+ it "can send nested data structures" do
26
+ structure = {
27
+ "hash" => {"a" => {"b" => ["c", "d"], "e" => "f"}},
28
+ "lisp" => ["define", {"square" => ["x", "y"]}, "*", "x", "x"]
29
+ }
30
+ transport.get("/", structure).data.should == structure
31
+ end
32
+
33
+ it "raises an UpstreamError for a nonexistent resource" do
34
+ lambda { transport.get("/nothing") }.should raise_error(Songkick::Transport::UpstreamError)
35
+ end
36
+
37
+ it "raises an UpstreamError for a POST resource" do
38
+ lambda { transport.get("/artists") }.should raise_error(Songkick::Transport::UpstreamError)
39
+ end
40
+
41
+ it "raises an UpstreamError for invalid JSON" do
42
+ lambda { transport.get("/invalid") }.should raise_error(Songkick::Transport::InvalidJSONError)
43
+ end
44
+ end
45
+
46
+ describe :with_headers do
47
+ it "adds the given headers to requests" do
48
+ data = transport.with_headers("Authorization" => "correct password").get("/authenticate").data
49
+ data.should == {"successful" => true}
50
+ end
51
+
52
+ it "reformats Rack-style headers" do
53
+ data = transport.with_headers("HTTP_AUTHORIZATION" => "correct password").get("/authenticate").data
54
+ data.should == {"successful" => true}
55
+ end
56
+
57
+ it "does not affect requests made directly on the transport object" do
58
+ transport.with_headers("Authorization" => "correct password").get("/authenticate")
59
+ data = transport.get("/authenticate").data
60
+ data.should == {"successful" => false}
61
+ end
62
+ end
63
+
64
+ describe :post do
65
+ it "sends data using POST" do
66
+ data = transport.post("/artists", :name => "Amon Tobin").data
67
+ data.should == {"id" => "new", "name" => "AMON TOBIN"}
68
+ end
69
+
70
+ it "can send array params" do
71
+ transport.post("/", :list => %w[a b]).data.should == {"list" => ["a", "b"]}
72
+ end
73
+
74
+ it "can send hash params" do
75
+ transport.post("/", :list => {:a => "b"}).data.should == {"list" => {"a" => "b"}}
76
+ end
77
+
78
+ it "raises an UpstreamError for a PUT resource" do
79
+ lambda { transport.post("/artists/64") }.should raise_error(Songkick::Transport::UpstreamError)
80
+ end
81
+ end
82
+
83
+ describe :put do
84
+ it "sends data using PUT" do
85
+ data = transport.put("/artists/64", :name => "Amon Tobin").data
86
+ data.should == {"id" => 64, "name" => "amon tobin"}
87
+ end
88
+
89
+ it "raises an UpstreamError for a POST resource" do
90
+ lambda{transport.put("/artists")}.should raise_error(Songkick::Transport::UpstreamError)
91
+ end
92
+ end
93
+
94
+ describe "file uploads" do
95
+ before do
96
+ pending if Songkick::Transport::RackTest === transport
97
+ end
98
+
99
+ after { file.close }
100
+
101
+ let(:file) { File.open(File.expand_path("../../songkick.png", __FILE__)) }
102
+ let(:upload) { Songkick::Transport::IO.new(file, "image/jpeg", "songkick.png") }
103
+
104
+ let :params do
105
+ {:concert => {:file => upload, :foo => :bar}}
106
+ end
107
+
108
+ let :expected_response do
109
+ {
110
+ "filename" => "songkick.png",
111
+ "method" => @http_method,
112
+ "size" => 6694,
113
+ "foo" => "bar"
114
+ }
115
+ end
116
+
117
+ it "uploads files using POST" do
118
+ @http_method = "post"
119
+ response = transport.post('/upload', params)
120
+ response.status.should == 200
121
+ response.data.should == expected_response
122
+ end
123
+
124
+ it "uploads files using PUT" do
125
+ @http_method = "put"
126
+ response = transport.put('/upload', params)
127
+ response.status.should == 200
128
+ response.data.should == expected_response
129
+ end
130
+ end
131
+
132
+ describe "reporting" do
133
+ before do
134
+ @report = Songkick::Transport.report
135
+ end
136
+
137
+ it "executes a block and returns its value" do
138
+ @report.execute { transport.get("/artists/99").data }.should == {"id" => 99}
139
+ end
140
+
141
+ it "reports a successful request" do
142
+ @report.execute { transport.get("/artists/99") }
143
+ @report.size.should == 1
144
+
145
+ request = @report.first
146
+ request.endpoint.should == endpoint
147
+ request.http_method.should == "get"
148
+ request.path.should == "/artists/99"
149
+ request.response.data.should == {"id" => 99}
150
+ end
151
+
152
+ it "reports a failed request" do
153
+ @report.execute { transport.get("/invalid") } rescue nil
154
+ @report.size.should == 1
155
+
156
+ request = @report.first
157
+ request.http_method.should == "get"
158
+ request.path.should == "/invalid"
159
+ request.response.should == nil
160
+ request.error.should be_a(Songkick::Transport::InvalidJSONError)
161
+ end
162
+ it "reports the total duration" do
163
+ @report.execute { transport.get("/artists/99") }
164
+ request = @report.first
165
+ request.stub(:duration).and_return 3.14
166
+ @report.total_duration.should == 3.14
167
+ end
168
+ end
169
+ end
170
+
171
+ describe Songkick::Transport::Curb do
172
+ let(:endpoint) { "http://localhost:4567" }
173
+ let(:transport) { Songkick::Transport::Curb.new(endpoint) }
174
+ it_should_behave_like "transport"
175
+ end
176
+
177
+ describe Songkick::Transport::HttParty do
178
+ let(:endpoint) { "http://localhost:4567" }
179
+ let(:transport) { Songkick::Transport::HttParty.new(endpoint) }
180
+ it_should_behave_like "transport"
181
+ end
182
+
183
+ describe Songkick::Transport::RackTest do
184
+ let(:endpoint) { TestApp }
185
+ let(:transport) { Songkick::Transport::RackTest.new(endpoint) }
186
+ it_should_behave_like "transport"
187
+ end
188
+ end
189
+
@@ -0,0 +1,72 @@
1
+ require 'rubygems'
2
+ require File.expand_path('../../lib/songkick/transport', __FILE__)
3
+
4
+ require 'logger'
5
+ require 'sinatra'
6
+ require 'thin'
7
+
8
+ Thin::Logging.silent = true
9
+ Songkick::Transport.logger = Logger.new(StringIO.new)
10
+
11
+ class TestApp < Sinatra::Base
12
+ before do
13
+ headers 'Content-Type' => 'application/json'
14
+ end
15
+
16
+ get('/') { Yajl::Encoder.encode(params) }
17
+ post('/') { Yajl::Encoder.encode(params) }
18
+
19
+ get '/invalid' do
20
+ '}'
21
+ end
22
+
23
+ get '/authenticate' do
24
+ env['HTTP_AUTHORIZATION'] ?
25
+ Yajl::Encoder.encode('successful' => true) :
26
+ Yajl::Encoder.encode('successful' => false)
27
+ end
28
+
29
+ get '/artists/:id' do
30
+ Yajl::Encoder.encode('id' => params[:id].to_i)
31
+ end
32
+
33
+ post '/artists' do
34
+ Yajl::Encoder.encode('id' => 'new', 'name' => params[:name].upcase)
35
+ end
36
+
37
+ put '/artists/:id' do
38
+ name = params[:name] || CGI.parse(env['rack.input'].read)['name'].first || ''
39
+ Yajl::Encoder.encode('id' => params[:id].to_i, 'name' => name.downcase)
40
+ end
41
+
42
+ %w[post put].each do |verb|
43
+ __send__(verb, '/upload') do
44
+ c = params[:concert]
45
+ Yajl::Encoder.encode(
46
+ "filename" => c[:file][:filename],
47
+ "method" => verb,
48
+ "size" => c[:file][:tempfile].size,
49
+ "foo" => c[:foo]
50
+ )
51
+ end
52
+ end
53
+
54
+ def self.ensure_reactor_running
55
+ Thread.new { EM.run } unless EM.reactor_running?
56
+ Thread.pass until EM.reactor_running?
57
+ end
58
+
59
+ def self.listen(port)
60
+ ensure_reactor_running
61
+ thin = Rack::Handler.get('thin')
62
+ app = Rack::Lint.new(self)
63
+ thin.run(app, :Port => port) { |s| @server = s }
64
+ end
65
+
66
+ def self.stop
67
+ @server.stop
68
+ sleep 0.1
69
+ rescue
70
+ end
71
+ end
72
+
metadata ADDED
@@ -0,0 +1,217 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: songkick-transport
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James Coglan
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: multipart-post
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: yajl-ruby
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.1.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.1.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: curb
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.3.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.3.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: httparty
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.4.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.4.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rack-test
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.4.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.4.0
94
+ - !ruby/object:Gem::Dependency
95
+ name: guard-rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: sinatra
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: thin
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description:
159
+ email: james@songkick.com
160
+ executables: []
161
+ extensions: []
162
+ extra_rdoc_files:
163
+ - README.rdoc
164
+ files:
165
+ - README.rdoc
166
+ - examples/tcp_server.rb
167
+ - examples/loop.rb
168
+ - examples/thread_safety.rb
169
+ - examples/server.rb
170
+ - examples/example.rb
171
+ - lib/songkick/transport/request.rb
172
+ - lib/songkick/transport/rack_test.rb
173
+ - lib/songkick/transport/timeout_decorator.rb
174
+ - lib/songkick/transport/httparty.rb
175
+ - lib/songkick/transport/serialization.rb
176
+ - lib/songkick/transport/http_error.rb
177
+ - lib/songkick/transport/headers.rb
178
+ - lib/songkick/transport/response.rb
179
+ - lib/songkick/transport/upstream_error.rb
180
+ - lib/songkick/transport/curb.rb
181
+ - lib/songkick/transport/header_decorator.rb
182
+ - lib/songkick/transport/reporting.rb
183
+ - lib/songkick/transport/base.rb
184
+ - lib/songkick/transport.rb
185
+ - spec/songkick/transport_spec.rb
186
+ - spec/songkick/transport/response_spec.rb
187
+ - spec/songkick/transport/httparty_spec.rb
188
+ - spec/songkick/transport/request_spec.rb
189
+ - spec/songkick/transport/curb_spec.rb
190
+ - spec/spec_helper.rb
191
+ homepage: http://github.com/songkick/transport
192
+ licenses: []
193
+ post_install_message:
194
+ rdoc_options:
195
+ - --main
196
+ - README.rdoc
197
+ require_paths:
198
+ - lib
199
+ required_ruby_version: !ruby/object:Gem::Requirement
200
+ none: false
201
+ requirements:
202
+ - - ! '>='
203
+ - !ruby/object:Gem::Version
204
+ version: '0'
205
+ required_rubygems_version: !ruby/object:Gem::Requirement
206
+ none: false
207
+ requirements:
208
+ - - ! '>='
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ requirements: []
212
+ rubyforge_project:
213
+ rubygems_version: 1.8.23
214
+ signing_key:
215
+ specification_version: 3
216
+ summary: HTTP client abstraction for service clients
217
+ test_files: []