sucker 1.1.4 → 1.2.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.
Files changed (47) hide show
  1. data/README.md +13 -19
  2. data/lib/sucker.rb +4 -4
  3. data/lib/sucker/parameters.rb +28 -0
  4. data/lib/sucker/request.rb +100 -88
  5. data/lib/sucker/response.rb +10 -0
  6. data/lib/sucker/version.rb +1 -1
  7. data/spec/fixtures/cassette_library/{unit → spec}/sucker/request.yml +4 -4
  8. data/spec/fixtures/cassette_library/spec/sucker/response.yml +26 -0
  9. data/spec/sucker/parameters_spec.rb +61 -0
  10. data/spec/sucker/request_spec.rb +276 -0
  11. data/spec/{unit/sucker → sucker}/response_spec.rb +39 -60
  12. data/spec/{unit/sucker_spec.rb → sucker_spec.rb} +0 -4
  13. data/spec/support/vcr.rb +0 -1
  14. metadata +69 -114
  15. data/spec/fixtures/cassette_library/integration/alternate_versions.yml +0 -26
  16. data/spec/fixtures/cassette_library/integration/errors.yml +0 -26
  17. data/spec/fixtures/cassette_library/integration/france.yml +0 -26
  18. data/spec/fixtures/cassette_library/integration/images.yml +0 -26
  19. data/spec/fixtures/cassette_library/integration/item_lookup/multiple.yml +0 -26
  20. data/spec/fixtures/cassette_library/integration/item_lookup/single.yml +0 -26
  21. data/spec/fixtures/cassette_library/integration/item_search.yml +0 -26
  22. data/spec/fixtures/cassette_library/integration/japan.yml +0 -26
  23. data/spec/fixtures/cassette_library/integration/keyword_search.yml +0 -26
  24. data/spec/fixtures/cassette_library/integration/kindle.yml +0 -26
  25. data/spec/fixtures/cassette_library/integration/kindle_2.yml +0 -26
  26. data/spec/fixtures/cassette_library/integration/multiple_locales.yml +0 -151
  27. data/spec/fixtures/cassette_library/integration/power_search.yml +0 -26
  28. data/spec/fixtures/cassette_library/integration/related_items/child.yml +0 -26
  29. data/spec/fixtures/cassette_library/integration/related_items/parent.yml +0 -26
  30. data/spec/fixtures/cassette_library/integration/seller_listings_search.yml +0 -26
  31. data/spec/fixtures/cassette_library/integration/twenty_items.yml +0 -26
  32. data/spec/fixtures/cassette_library/unit/sucker/response.yml +0 -26
  33. data/spec/integration/alternate_versions_spec.rb +0 -35
  34. data/spec/integration/errors_spec.rb +0 -40
  35. data/spec/integration/france_spec.rb +0 -42
  36. data/spec/integration/images_spec.rb +0 -41
  37. data/spec/integration/item_lookup_spec.rb +0 -71
  38. data/spec/integration/item_search_spec.rb +0 -41
  39. data/spec/integration/japan_spec.rb +0 -35
  40. data/spec/integration/keyword_search_spec.rb +0 -33
  41. data/spec/integration/kindle_spec.rb +0 -55
  42. data/spec/integration/multiple_locales_spec.rb +0 -70
  43. data/spec/integration/power_search_spec.rb +0 -41
  44. data/spec/integration/related_items_spec.rb +0 -53
  45. data/spec/integration/seller_listing_search_spec.rb +0 -32
  46. data/spec/integration/twenty_items_spec.rb +0 -49
  47. data/spec/unit/sucker/request_spec.rb +0 -282
@@ -1,3 +1,3 @@
1
1
  module Sucker #:nodoc:
2
- VERSION = "1.1.4"
2
+ VERSION = '1.2.0'
3
3
  end
@@ -2,7 +2,7 @@
2
2
  - !ruby/struct:VCR::HTTPInteraction
3
3
  request: !ruby/struct:VCR::Request
4
4
  method: :get
5
- uri: http://ecs.amazonaws.com:80/onca/xml?AWSAccessKeyId=key&AssociateTag=&Service=AWSECommerceService&Signature=KhbhltMa2lob8mGADxdK7GjHHWHAe4sLkbs7h7Xto3M=&Timestamp=2010-12-15T07:14:37Z&Version=2010-09-01
5
+ uri: http://ecs.amazonaws.com:80/onca/xml?AWSAccessKeyId=key&AssociateTag=&Service=AWSECommerceService&Signature=5sbmkIO1PqtCCUH0Ih9Fx/3q9ER1FuXk2UPFLlISrIQ=&Timestamp=2011-01-17T15:20:03Z&Version=2010-11-01
6
6
  body:
7
7
  headers:
8
8
  response: !ruby/struct:VCR::Response
@@ -11,13 +11,13 @@
11
11
  message: OK
12
12
  headers:
13
13
  date:
14
- - Wed, 15 Dec 2010 07:14:37 GMT
14
+ - Mon, 17 Jan 2011 15:20:05 GMT
15
15
  server:
16
16
  - Server
17
17
  x-amz-id-1:
18
- - 0E8S4ENZ8Q9WBHRS7211
18
+ - 1218DXHJBXZ5J480J8QM
19
19
  x-amz-id-2:
20
- - 4r6BnL5Ix6csd4KWILexsqszhPU99B63G0hcnRHvPww=
20
+ - SJpoXux3oMHHhZyjDxwhVVy8JrcptMTdzE1nK8SxSm0=
21
21
  nncoection:
22
22
  - close
23
23
  - close
@@ -0,0 +1,26 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://ecs.amazonaws.com:80/onca/xml?AWSAccessKeyId=0ZVSQ33MDFPQS8H2PM02&AssociateTag=&IdType=ASIN&ItemId=0816614024,0143105825&Operation=ItemLookup&Service=AWSECommerceService&Signature=mb2SRZUnxqPRrI4SdOLZqkCmm5HjXE2Urd0PIr/vzWU=&Timestamp=2011-01-17T15:20:11Z&Version=2010-11-01
6
+ body:
7
+ headers:
8
+ response: !ruby/struct:VCR::Response
9
+ status: !ruby/struct:VCR::ResponseStatus
10
+ code: 200
11
+ message: OK
12
+ headers:
13
+ date:
14
+ - Mon, 17 Jan 2011 15:20:12 GMT
15
+ server:
16
+ - Server
17
+ content-type:
18
+ - text/xml;charset=UTF-8
19
+ vary:
20
+ - Accept-Encoding,User-Agent
21
+ nncoection:
22
+ - close
23
+ transfer-encoding:
24
+ - chunked
25
+ body: "<?xml version=\"1.0\" ?><ItemLookupResponse xmlns=\"http://webservices.amazon.com/AWSECommerceService/2010-11-01\"><OperationRequest><RequestId>c5f72c52-3206-4a9f-a30f-a58e1a5f4445</RequestId><Arguments><Argument Name=\"Operation\" Value=\"ItemLookup\"></Argument><Argument Name=\"Service\" Value=\"AWSECommerceService\"></Argument><Argument Name=\"Signature\" Value=\"mb2SRZUnxqPRrI4SdOLZqkCmm5HjXE2Urd0PIr/vzWU=\"></Argument><Argument Name=\"AssociateTag\"></Argument><Argument Name=\"Version\" Value=\"2010-11-01\"></Argument><Argument Name=\"ItemId\" Value=\"0816614024,0143105825\"></Argument><Argument Name=\"IdType\" Value=\"ASIN\"></Argument><Argument Name=\"AWSAccessKeyId\" Value=\"0ZVSQ33MDFPQS8H2PM02\"></Argument><Argument Name=\"Timestamp\" Value=\"2011-01-17T15:20:11Z\"></Argument></Arguments><RequestProcessingTime>0.0103990000000000</RequestProcessingTime></OperationRequest><Items><Request><IsValid>True</IsValid><ItemLookupRequest><Condition>New</Condition><DeliveryMethod>Ship</DeliveryMethod><IdType>ASIN</IdType><MerchantId>Amazon</MerchantId><OfferPage>1</OfferPage><ItemId>0816614024</ItemId><ItemId>0143105825</ItemId><ResponseGroup>Small</ResponseGroup><ReviewPage>1</ReviewPage><ReviewSort>-SubmissionDate</ReviewSort><VariationPage>All</VariationPage></ItemLookupRequest></Request><Item><ASIN>0816614024</ASIN><DetailPageURL>http://www.amazon.com/Thousand-Plateaus-Schizophrenia-Gilles-Deleuze/dp/0816614024%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0816614024</DetailPageURL><ItemLinks><ItemLink><Description>Technical Details</Description><URL>http://www.amazon.com/Thousand-Plateaus-Schizophrenia-Gilles-Deleuze/dp/tech-data/0816614024%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink><ItemLink><Description>Add To Baby Registry</Description><URL>http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3D0816614024%26SubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink><ItemLink><Description>Add To Wedding Registry</Description><URL>http://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3D0816614024%26SubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink><ItemLink><Description>Add To Wishlist</Description><URL>http://www.amazon.com/gp/registry/wishlist/add-item.html%3Fasin.0%3D0816614024%26SubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink><ItemLink><Description>Tell A Friend</Description><URL>http://www.amazon.com/gp/pdp/taf/0816614024%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink><ItemLink><Description>All Customer Reviews</Description><URL>http://www.amazon.com/review/product/0816614024%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink><ItemLink><Description>All Offers</Description><URL>http://www.amazon.com/gp/offer-listing/0816614024%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0816614024</URL></ItemLink></ItemLinks><ItemAttributes><Author>Gilles Deleuze</Author><Creator Role=\"Contributor\">Felix Guattari</Creator><Manufacturer>Univ Of Minnesota Press</Manufacturer><ProductGroup>Book</ProductGroup><Title>Thousand Plateaus: Capitalism and Schizophrenia</Title></ItemAttributes></Item><Item><ASIN>0143105825</ASIN><DetailPageURL>http://www.amazon.com/Anti-Oedipus-Capitalism-Schizophrenia-Penguin-Classics/dp/0143105825%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D0143105825</DetailPageURL><ItemLinks><ItemLink><Description>Technical Details</Description><URL>http://www.amazon.com/Anti-Oedipus-Capitalism-Schizophrenia-Penguin-Classics/dp/tech-data/0143105825%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink><ItemLink><Description>Add To Baby Registry</Description><URL>http://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3D0143105825%26SubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink><ItemLink><Description>Add To Wedding Registry</Description><URL>http://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3D0143105825%26SubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink><ItemLink><Description>Add To Wishlist</Description><URL>http://www.amazon.com/gp/registry/wishlist/add-item.html%3Fasin.0%3D0143105825%26SubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink><ItemLink><Description>Tell A Friend</Description><URL>http://www.amazon.com/gp/pdp/taf/0143105825%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink><ItemLink><Description>All Customer Reviews</Description><URL>http://www.amazon.com/review/product/0143105825%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink><ItemLink><Description>All Offers</Description><URL>http://www.amazon.com/gp/offer-listing/0143105825%3FSubscriptionId%3D0ZVSQ33MDFPQS8H2PM02%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D0143105825</URL></ItemLink></ItemLinks><ItemAttributes><Author>Gilles Deleuze</Author><Author>Felix Guattari</Author><Creator Role=\"Translator\">Robert Hurley</Creator><Creator Role=\"Translator\">Mark Seem</Creator><Creator Role=\"Introduction\">Mark Seem</Creator><Creator Role=\"Translator\">Helen Lane</Creator><Creator Role=\"Preface\">Michel Foucault</Creator><Manufacturer>Penguin Classics</Manufacturer><ProductGroup>Book</ProductGroup><Title>Anti-Oedipus: Capitalism and Schizophrenia (Penguin Classics)</Title></ItemAttributes></Item></Items></ItemLookupResponse>"
26
+ http_version: "1.1"
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ module Sucker
4
+ describe Parameters do
5
+ let(:parameters) do
6
+ Parameters.new
7
+ end
8
+
9
+ it "is a Hash" do
10
+ Parameters.ancestors.should include Hash
11
+ end
12
+
13
+ context "when initialized" do
14
+ it "sets `Service`" do
15
+ parameters.should have_key "Service"
16
+ end
17
+
18
+ it "sets `Version`" do
19
+ parameters.should have_key "Version"
20
+ end
21
+
22
+ it "set `Timestamp`" do
23
+ parameters.should have_key "Timestamp"
24
+ end
25
+ end
26
+
27
+ describe "#normalize" do
28
+ it "casts keys to string" do
29
+ parameters[:Foo] = 0
30
+ normalized = parameters.normalize
31
+
32
+ normalized.should have_key "Foo"
33
+ normalized.should_not have_key :foo
34
+ end
35
+
36
+ it "camelizes keys" do
37
+ parameters["foo_bar"] = 0
38
+ normalized = parameters.normalize
39
+
40
+ normalized.should have_key "FooBar"
41
+ normalized.should_not have_key "foo_bar"
42
+ end
43
+
44
+ it "casts numeric values to string" do
45
+ parameters["Foo"] = 1
46
+ parameters.normalize["Foo"].should eql "1"
47
+ end
48
+
49
+ it "converts array values to string" do
50
+ parameters["Foo"] = ['bar', 'baz']
51
+ parameters.normalize["Foo"].should eql 'bar,baz'
52
+ end
53
+ end
54
+
55
+ describe "#timestamp" do
56
+ it "generates a timestamp" do
57
+ parameters.send(:timestamp).should match /^\d+-\d+-\d+T\d+:\d+:\d+Z$/
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,276 @@
1
+ require "spec_helper"
2
+
3
+ module Sucker
4
+ describe Request do
5
+ use_vcr_cassette "spec/sucker/request", :record => :new_episodes
6
+
7
+ let(:worker) do
8
+ Sucker.new(
9
+ :locale => :us,
10
+ :key => "key",
11
+ :secret => "secret")
12
+ end
13
+
14
+ describe "#<<" do
15
+ it "merges a hash into the existing parameters" do
16
+ worker << { "foo" => "bar" }
17
+ worker.parameters["foo"].should eql "bar"
18
+ end
19
+ end
20
+
21
+ describe "#version=" do
22
+ it "sets the Amazon API version" do
23
+ worker.version = "foo"
24
+ worker.parameters["Version"].should eql "foo"
25
+ end
26
+ end
27
+
28
+ describe "#associate_tag" do
29
+ it "returns the associate tag for the current locale" do
30
+ worker.instance_variable_set(:@associate_tags, { :us => 'foo-bar'})
31
+
32
+ worker.associate_tag.should eql 'foo-bar'
33
+ end
34
+
35
+ it "returns blank if no associate tag is set for the current locale" do
36
+ worker.associate_tag.should be_blank
37
+ end
38
+ end
39
+
40
+ describe "#associate_tag=" do
41
+ it "sets the associate tag for the current locale" do
42
+ worker.associate_tag = "foo-bar"
43
+ associate_tags = worker.instance_variable_get(:@associate_tags)
44
+
45
+ associate_tags.keys.size.should eql 1
46
+ associate_tags[:us].should eql 'foo-bar'
47
+ worker.associate_tag.should eql 'foo-bar'
48
+ end
49
+ end
50
+
51
+ describe "#associate_tags=" do
52
+ it "sets associate tags for the locales" do
53
+ tags = {
54
+ :us => 'foo',
55
+ :uk => 'bar',
56
+ :de => 'baz',
57
+ :ca => 'foo',
58
+ :fr => 'bar',
59
+ :jp => 'baz' }
60
+ worker.associate_tags = tags
61
+
62
+ worker.instance_variable_get(:@associate_tags).should eql tags
63
+ end
64
+ end
65
+
66
+ describe "#curl_opts" do
67
+ it "returns options for curl" do
68
+ worker.curl_opts.should be_an_instance_of Hash
69
+ end
70
+
71
+ context "when given a block" do
72
+
73
+ it "yields options for curl" do
74
+ worker.curl_opts { |c| c.interface = "eth1" }
75
+
76
+ worker.curl_opts[:interface].should eql "eth1"
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "#get" do
82
+ context "when no argument is passed" do
83
+ it "returns a response" do
84
+ worker.get.class.ancestors.should include Response
85
+ end
86
+
87
+ it "sets options on curl" do
88
+ easy = mock
89
+ easy.should_receive(:interface=).once.with("eth1")
90
+ Curl::Easy.stub!(:perform).and_yield(easy)
91
+ Response.should_receive(:new).once
92
+
93
+ worker.curl_opts { |c| c.interface = 'eth1' }
94
+ worker.get
95
+ end
96
+ end
97
+
98
+ context "when one argument is passed" do
99
+ context "when the argument is is `:all`" do
100
+ it "queries all locales" do
101
+ locales = worker.send(:locales)
102
+ worker.should_receive(:get_multi).with(locales)
103
+ worker.get(:all)
104
+ end
105
+ end
106
+
107
+ context "when the argument is not `:all`" do
108
+ it "sets current locale to to the argument" do
109
+ worker.should_receive(:locale=).with(:uk)
110
+ worker.get(:uk)
111
+ end
112
+
113
+ it "calls itself with no argument" do
114
+ worker.class.send :alias_method, :get_original, :get
115
+ worker.should_receive(:get).with(no_args())
116
+ worker.get_original(:uk)
117
+ end
118
+ end
119
+ end
120
+
121
+ context "when multiple arguments are passed" do
122
+ it "queries multiple locales" do
123
+ worker.should_receive(:get_multi).with([:us, :uk])
124
+ worker.get(:us, :uk)
125
+ end
126
+ end
127
+ end
128
+
129
+ describe "#key" do
130
+ it "returns the Amazon AWS access key for the current locale" do
131
+ worker.instance_variable_set(:@keys, { :us => 'foo' })
132
+
133
+ worker.key.should eql 'foo'
134
+ end
135
+
136
+ it "raises an argument error if key is missing" do
137
+ worker.instance_variable_get(:@keys)[:us] = nil
138
+ expect do
139
+ worker.key
140
+ end.to raise_error 'AWS access key missing'
141
+ end
142
+ end
143
+
144
+ describe "#key=" do
145
+ it "sets a global Amazon AWS access key" do
146
+ worker.key = "foo"
147
+ keys = worker.instance_variable_get(:@keys)
148
+
149
+ keys.size.should eql Request::HOSTS.size
150
+ keys.values.uniq.should eql ["foo"]
151
+ end
152
+ end
153
+
154
+ describe "#keys=" do
155
+ it "sets distinct Amazon AWS access keys for the locales" do
156
+ keys = {
157
+ :us => 'foo',
158
+ :uk => 'bar',
159
+ :de => 'baz',
160
+ :ca => 'foo',
161
+ :fr => 'bar',
162
+ :jp => 'baz' }
163
+ worker.keys = keys
164
+
165
+ worker.instance_variable_get(:@keys).should eql keys
166
+ end
167
+ end
168
+
169
+ describe "#locale" do
170
+ it "returns the current locale" do
171
+ worker.locale.should eql :us
172
+ end
173
+
174
+ it "raises an argument error if locale is not set" do
175
+ worker.instance_variable_set(:@locale, nil)
176
+ expect do
177
+ worker.locale
178
+ end.to raise_error 'Locale not set'
179
+ end
180
+ end
181
+
182
+ describe "#locale=" do
183
+ it "sets the current locale" do
184
+ worker.locale= :uk
185
+ worker.locale.should eql :uk
186
+ end
187
+
188
+ it "raises an argument error if locale is invalid" do
189
+ expect do
190
+ worker.locale= :br
191
+ end.to raise_error 'Invalid locale'
192
+ end
193
+ end
194
+
195
+ # private
196
+
197
+ describe "#build_query" do
198
+ let(:query) do
199
+ worker.send(:build_query)
200
+ end
201
+
202
+ it "canonicalizes query" do
203
+ query.should match /Service=([^&]+)&Timestamp=([^&]+)&Version=([^&]+)/
204
+ end
205
+
206
+ it "includes a timestamp" do
207
+ query.should include 'Timestamp'
208
+ end
209
+
210
+ it "sorts query" do
211
+ worker.parameters["A"] = "foo"
212
+ query.should match /^A=foo/
213
+ end
214
+ end
215
+
216
+ describe "#build_signed_query" do
217
+ let(:query) { worker.send(:build_signed_query) }
218
+
219
+ it "includes the key for the current locale" do
220
+ worker.key = 'foo'
221
+ query.should include 'AWSAccessKeyId=foo'
222
+ end
223
+
224
+ it "includes the associate tag for the current locale" do
225
+ worker.associate_tag = 'foo'
226
+ query.should include 'AssociateTag=foo'
227
+ end
228
+
229
+ it "returns a signed query string" do
230
+ query = worker.send :build_signed_query
231
+ query.should include 'Signature='
232
+ end
233
+ end
234
+
235
+ describe "#escape" do
236
+ it "URL-encodes a string" do
237
+ worker.send(:escape, 'foo,bar').should eql "foo%2Cbar"
238
+ end
239
+ end
240
+
241
+ describe "#get_multi" do
242
+ it "returns an array of responses" do
243
+ responses = worker.send(:get_multi, [:us, :uk])
244
+
245
+ responses.should be_an_instance_of Array
246
+ responses.each { |resp| resp.should be_an_instance_of Response }
247
+ end
248
+
249
+ context "when given a block" do
250
+ it "yields responses" do
251
+ locales = worker.send(:locales)
252
+ count = 0
253
+ worker.send(:get_multi, locales) do |resp|
254
+ resp.should be_an_instance_of Response
255
+ count += 1
256
+ end
257
+
258
+ count.should eql locales.size
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "#host" do
264
+ it "returns a host" do
265
+ worker.locale = "fr"
266
+ worker.send(:host).should eql "ecs.amazonaws.fr"
267
+ end
268
+ end
269
+
270
+ describe "#uri" do
271
+ it "returns the URI with which to query Amazon" do
272
+ worker.send(:uri).should be_an_instance_of URI::HTTP
273
+ end
274
+ end
275
+ end
276
+ end
@@ -2,10 +2,8 @@
2
2
  require "spec_helper"
3
3
 
4
4
  module Sucker
5
-
6
5
  describe Response do
7
-
8
- use_vcr_cassette "unit/sucker/response", :record => :new_episodes
6
+ use_vcr_cassette "spec/sucker/response", :record => :new_episodes
9
7
 
10
8
  let(:asins) { ["0816614024", "0143105825"] }
11
9
 
@@ -17,13 +15,11 @@ module Sucker
17
15
  worker << {
18
16
  "Operation" => "ItemLookup",
19
17
  "IdType" => "ASIN",
20
- "ResponseGroup" => ["ItemAttributes", "OfferFull"],
21
18
  "ItemId" => asins }
22
19
  worker.get
23
20
  end
24
21
 
25
22
  describe ".new" do
26
-
27
23
  it "initializes the response body" do
28
24
  response.body.should be_an_instance_of String
29
25
  end
@@ -39,43 +35,10 @@ module Sucker
39
35
  it "initializes the request URI" do
40
36
  response.uri.should be_an_instance_of String
41
37
  end
42
-
43
- end
44
-
45
- describe "#xml" do
46
-
47
- it "returns a Nokogiri document" do
48
- response.xml.should be_an_instance_of Nokogiri::XML::Document
49
- end
50
-
51
- end
52
-
53
- describe "#find" do
54
-
55
- context "when there are matches" do
56
-
57
- it "returns an array of matching nodes" do
58
- node = response.find("ItemAttributes")
59
- node.map { |book| book["ISBN"] }.should eql asins
60
- end
61
-
62
- end
63
-
64
- context "when there are no matches" do
65
-
66
- it "returns an empty array" do
67
- node = response.find("Foo")
68
- node.should eql []
69
- end
70
-
71
- end
72
-
73
38
  end
74
39
 
75
40
  describe "#each" do
76
-
77
41
  context "when a block is given" do
78
-
79
42
  it "yields each match to a block" do
80
43
  has_yielded = false
81
44
 
@@ -86,51 +49,70 @@ module Sucker
86
49
 
87
50
  has_yielded.should be_true
88
51
  end
89
-
90
52
  end
91
53
 
92
54
  context "when no block is given" do
93
-
94
55
  it "raises error" do
95
56
  expect do
96
57
  response.each("ItemAttributes")
97
58
  end.to raise_error(LocalJumpError)
98
59
  end
60
+ end
61
+ end
62
+
63
+ context "when response contains errors" do
64
+ before do
65
+ response.body = "<?xml version=\"1.0\" ?><ItemLookupResponse xmlns=\"http://webservices.amazon.com/AWSECommerceService/2010-09-01\"><Errors><Error>foo</Error><Error>bar</Error></Errors>"
66
+ end
99
67
 
68
+ describe "#errors" do
69
+ it "returns an array of errors" do
70
+ response.errors.count.should eql 2
71
+ end
100
72
  end
101
73
 
74
+ describe "#has_errors?" do
75
+ it "returns true if the response has errors" do
76
+ response.should have_errors
77
+ end
78
+ end
102
79
  end
103
80
 
81
+ describe "#find" do
82
+ context "when there are matches" do
104
83
 
105
- describe "#map" do
84
+ it "returns an array of matching nodes" do
85
+ response.find("ASIN").should eql asins
86
+ end
87
+ end
106
88
 
107
- context "when a block is given" do
89
+ context "when there are no matches" do
90
+ it "returns an empty array" do
91
+ node = response.find("Foo")
92
+ node.should eql []
93
+ end
94
+ end
95
+ end
108
96
 
97
+ describe "#map" do
98
+ context "when a block is given" do
109
99
  it "yields each match to a block and maps returned values" do
110
- # Collect EANs
111
- eans = response.map("ItemAttributes") { |item| item["EAN"] }
100
+ titles = response.map("ItemAttributes") { |item| item["Title"] }
112
101
 
113
- eans.should be_an_instance_of Array
114
- eans.each { |ean| ean.match(/^[0-9]{13}$/).should be_true }
102
+ titles.count.should eql 2
115
103
  end
116
-
117
104
  end
118
105
 
119
106
  context "when no block is given" do
120
-
121
107
  it "raises error" do
122
108
  expect do
123
109
  response.map("ItemAttributes")
124
110
  end.to raise_error(LocalJumpError)
125
111
  end
126
-
127
112
  end
128
-
129
113
  end
130
114
 
131
-
132
115
  describe "#to_hash" do
133
-
134
116
  it "returns a hash" do
135
117
  response.to_hash.should be_an_instance_of Hash
136
118
  end
@@ -154,30 +136,27 @@ module Sucker
154
136
  response.body = "<Title>スティーブ・ジョブズ 驚異のプレゼン―人々を惹きつける18の法則</Title>"
155
137
  response.to_hash["Title"].should eql "スティーブ・ジョブズ 驚異のプレゼン―人々を惹きつける18の法則"
156
138
  end
157
-
158
139
  end
159
140
 
160
141
  describe "#valid?" do
161
-
162
142
  context "when HTTP status is OK" do
163
-
164
143
  it "returns true" do
165
144
  response.should be_valid
166
145
  end
167
-
168
146
  end
169
147
 
170
148
  context "when HTTP status is not OK" do
171
-
172
149
  it "returns false" do
173
150
  response.code = 403
174
151
  response.should_not be_valid
175
152
  end
176
-
177
153
  end
178
-
179
154
  end
180
155
 
156
+ describe "#xml" do
157
+ it "returns a Nokogiri document" do
158
+ response.xml.should be_an_instance_of Nokogiri::XML::Document
159
+ end
160
+ end
181
161
  end
182
-
183
162
  end