amazon_associate 0.7.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.
@@ -0,0 +1,4 @@
1
+ module AmazonAssociate
2
+ class RequestError < RuntimeError
3
+ end
4
+ end
@@ -0,0 +1,74 @@
1
+ module AmazonAssociate
2
+ # Response object returned after a REST call to Amazon service.
3
+ class Response
4
+
5
+ attr_accessor :request_url, :unsigned_url
6
+ # XML input is in string format
7
+ def initialize(xml, request_url)
8
+ @doc = Hpricot(xml)
9
+ @items = nil
10
+ @item_page = nil
11
+ @total_results = nil
12
+ @total_pages = nil
13
+
14
+ self.request_url = request_url
15
+ end
16
+
17
+ # Return Hpricot object.
18
+ def doc
19
+ @doc
20
+ end
21
+
22
+ # Return true if request is valid.
23
+ def is_valid_request?
24
+ (@doc/"isvalid").inner_html == "True"
25
+ end
26
+
27
+ # Return true if response has an error.
28
+ def has_error?
29
+ !(error.nil? || error.empty?)
30
+ end
31
+
32
+ # Return error message.
33
+ def error
34
+ Element.get(@doc, "error/message")
35
+ end
36
+
37
+ # Return an array of AmazonAssociate::Element item objects.
38
+ def items
39
+ unless @items
40
+ @items = (@doc/"item").collect {|item| Element.new(item)}
41
+ end
42
+ @items
43
+ end
44
+
45
+ # Return the first item (AmazonAssociate::Element)
46
+ def first_item
47
+ items.first
48
+ end
49
+
50
+ # Return current page no if :item_page option is when initiating the request.
51
+ def item_page
52
+ unless @item_page
53
+ @item_page = (@doc/"itemsearchrequest/itempage").inner_html.to_i
54
+ end
55
+ @item_page
56
+ end
57
+
58
+ # Return total results.
59
+ def total_results
60
+ unless @total_results
61
+ @total_results = (@doc/"totalresults").inner_html.to_i
62
+ end
63
+ @total_results
64
+ end
65
+
66
+ # Return total pages.
67
+ def total_pages
68
+ unless @total_pages
69
+ @total_pages = (@doc/"totalpages").inner_html.to_i
70
+ end
71
+ @total_pages
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + "/../test_helper"
2
+
3
+ class AmazonAssociate::BrowseNodeLookupTest < Test::Unit::TestCase
4
+
5
+ ## Test browse_node_lookup
6
+ def test_browse_node_lookup
7
+ resp = AmazonAssociate::Request.browse_node_lookup("5", :response_group => "TopSellers")
8
+ assert resp.is_valid_request?
9
+ browse_node_tags = resp.doc.get_elements_by_tag_name("browsenodeid")
10
+ browse_node_tags.each { |node| assert_equal("5", node.inner_text) }
11
+ assert_equal "TopSellers", resp.doc.get_elements_by_tag_name("responsegroup").inner_text
12
+ end
13
+
14
+ def test_browse_node_lookup_with_browse_node_info_response
15
+ resp = AmazonAssociate::Request.browse_node_lookup("5", :response_group => "BrowseNodeInfo")
16
+ assert resp.is_valid_request?
17
+ assert_equal "BrowseNodeInfo", resp.doc.get_elements_by_tag_name("responsegroup").inner_text
18
+ end
19
+
20
+ def test_browse_node_lookup_with_new_releases_response
21
+ resp = AmazonAssociate::Request.browse_node_lookup("5", :response_group => "NewReleases")
22
+ assert resp.is_valid_request?
23
+ assert_equal "NewReleases", resp.doc.get_elements_by_tag_name("responsegroup").inner_text
24
+ end
25
+
26
+ def test_browse_node_lookup_with_invalid_request
27
+ resp = AmazonAssociate::Request.browse_node_lookup(nil)
28
+ assert resp.has_error?
29
+ assert resp.error
30
+ end
31
+
32
+ def test_browse_node_lookup_with_no_result
33
+ resp = AmazonAssociate::Request.browse_node_lookup("abc")
34
+
35
+ assert resp.is_valid_request?
36
+ assert_match(/abc is not a valid value for BrowseNodeId/, resp.error)
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + "/../test_helper"
2
+
3
+ class AmazonAssociate::CacheTest < Test::Unit::TestCase
4
+ include FilesystemTestHelper
5
+ context "caching get" do
6
+ setup do
7
+ get_cache_directory
8
+ get_valid_caching_options
9
+ end
10
+
11
+ teardown do
12
+ destroy_cache_directory
13
+ destroy_caching_options
14
+ end
15
+
16
+ should "optionally allow for a caching strategy in configuration" do
17
+ assert_nothing_raised do
18
+ AmazonAssociate::Request.configure do |options|
19
+ options[:caching_strategy] = :filesystem
20
+ end
21
+ end
22
+ end
23
+
24
+ should "raise an exception if a caching strategy is specified that is not found" do
25
+ assert_raises(AmazonAssociate::ConfigurationError) do
26
+ AmazonAssociate::Request.configure do |options|
27
+ options[:caching_strategy] = "foo"
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,193 @@
1
+ require File.dirname(__FILE__) + "/../../test_helper"
2
+
3
+ class AmazonAssociate::CachingStrategy::FilesystemTest < Test::Unit::TestCase
4
+ include FilesystemTestHelper
5
+ context "setting up filesystem caching" do
6
+ teardown do
7
+ AmazonAssociate::Request.configure do |options|
8
+ options[:caching_strategy] = nil
9
+ options[:caching_options] = nil
10
+ end
11
+ end
12
+
13
+ should "require a caching options hash with a cache_path key" do
14
+ assert_raises(AmazonAssociate::ConfigurationError) do
15
+ AmazonAssociate::Request.configure do |options|
16
+ options[:caching_strategy] = :filesystem
17
+ options[:caching_options] = nil
18
+ end
19
+ end
20
+ end
21
+
22
+ should "raise an exception when a cache_path is specified that doesn't exist" do
23
+ assert_raises(AmazonAssociate::ConfigurationError) do
24
+ AmazonAssociate::Request.configure do |options|
25
+ options[:caching_strategy] = :filesystem
26
+ options[:caching_options] = {:cache_path => "foo123"}
27
+ end
28
+ end
29
+ end
30
+
31
+ should "raise an exception when a cache_path is nil" do
32
+ assert_raise(AmazonAssociate::ConfigurationError) do
33
+ AmazonAssociate::Request.configure do |options|
34
+ options[:caching_strategy] = :filesystem
35
+ options[:caching_options] = {}
36
+ end
37
+ end
38
+ end
39
+
40
+ should "set default values for disk_quota and sweep_frequency" do
41
+ AmazonAssociate::Request.configure do |options|
42
+ options[:caching_strategy] = :filesystem
43
+ options[:caching_options] = {:cache_path => "."}
44
+ end
45
+
46
+ assert_equal AmazonAssociate::CachingStrategy::Filesystem.disk_quota, AmazonAssociate::CachingStrategy::Filesystem.disk_quota
47
+ assert_equal AmazonAssociate::CachingStrategy::Filesystem.sweep_frequency, AmazonAssociate::CachingStrategy::Filesystem.sweep_frequency
48
+ end
49
+
50
+ should "override the default value for disk quota if I specify one" do
51
+ quota = 400
52
+ AmazonAssociate::Request.configure do |options|
53
+ options[:caching_strategy] = :filesystem
54
+ options[:caching_options] = {:cache_path => ".", :disk_quota => quota}
55
+ end
56
+
57
+ assert_equal quota, AmazonAssociate::CachingStrategy::Filesystem.disk_quota
58
+ end
59
+
60
+ should "override the default value for cache_frequency if I specify one" do
61
+ frequency = 4
62
+ AmazonAssociate::Request.configure do |options|
63
+ options[:caching_strategy] = :filesystem
64
+ options[:caching_options] = {:cache_path => ".", :sweep_frequency => frequency}
65
+ end
66
+
67
+ assert_equal frequency, AmazonAssociate::CachingStrategy::Filesystem.sweep_frequency
68
+ end
69
+ end
70
+
71
+ context "caching a request" do
72
+
73
+ setup do
74
+ get_cache_directory
75
+ get_valid_caching_options
76
+ @resp = AmazonAssociate::Request.item_lookup("0974514055")
77
+ @filename = Digest::SHA1.hexdigest(@resp.unsigned_url)
78
+ end
79
+
80
+ teardown do
81
+ destroy_cache_directory
82
+ destroy_caching_options
83
+ end
84
+
85
+ should "create a folder in the cache path with the first three letters of the digested filename" do
86
+ filename = Digest::SHA1.hexdigest(@resp.request_url)
87
+ FileTest.exists?(File.join(@@cache_path, @filename[0..2]))
88
+ end
89
+
90
+ should "create a file in the cache path with a digested version of the url " do
91
+
92
+ filename = Digest::SHA1.hexdigest(@resp.request_url)
93
+ assert FileTest.exists?(File.join(@@cache_path, @filename[0..2], @filename))
94
+ end
95
+
96
+ should "create a file in the cache path with the response inside it" do
97
+ assert FileTest.exists?(File.join(@@cache_path + @filename[0..2], @filename))
98
+ assert_equal @resp.doc.to_s, File.read(File.join(@@cache_path + @filename[0..2], @filename)).chomp
99
+ end
100
+
101
+ should "not send cache parameters in the request" do
102
+ [:caching_strategy].each do |param|
103
+ assert_no_match /#{param.to_s}/, @resp.request_url
104
+ end
105
+ end
106
+ end
107
+
108
+ context "getting a cached request" do
109
+ setup do
110
+ get_cache_directory
111
+ get_valid_caching_options
112
+ do_request
113
+ end
114
+
115
+ teardown do
116
+ destroy_cache_directory
117
+ destroy_caching_options
118
+ end
119
+
120
+ should "not do an http request the second time the lookup is performed due a cached copy" do
121
+ Net::HTTP.expects(:get_response).never
122
+ do_request
123
+ end
124
+
125
+ end
126
+
127
+ context "sweeping cached requests" do
128
+ setup do
129
+ get_cache_directory
130
+ get_valid_caching_options
131
+ do_request
132
+ end
133
+
134
+ teardown do
135
+ destroy_cache_directory
136
+ destroy_caching_options
137
+ end
138
+
139
+ should "not perform the sweep if the timestamp is within the range of the sweep frequency and quota is not exceeded" do
140
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:sweep_time_expired?).returns(false)
141
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:disk_quota_exceeded?).returns(false)
142
+
143
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:perform_sweep).never
144
+
145
+ do_request
146
+ end
147
+
148
+ should "perform a sweep if the quota is exceeded" do
149
+ AmazonAssociate::CachingStrategy::Filesystem.stubs(:sweep_time_expired?).returns(false)
150
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:disk_quota_exceeded?).once.returns(true)
151
+
152
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:perform_sweep).once
153
+
154
+ do_request
155
+ end
156
+
157
+ should "perform a sweep if the sweep time is expired" do
158
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:sweep_time_expired?).once.returns(true)
159
+ AmazonAssociate::CachingStrategy::Filesystem.stubs(:disk_quota_exceeded?).returns(false)
160
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:perform_sweep).once
161
+
162
+ do_request
163
+ end
164
+
165
+ should "create a timestamp file after performing a sweep" do
166
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:sweep_time_expired?).once.returns(true)
167
+
168
+ do_request
169
+ assert FileTest.exists?(File.join(@@cache_path, ".amz_timestamp"))
170
+ end
171
+
172
+ should "purge the cache when performing a sweep" do
173
+ (0..9).each do |n|
174
+ test = File.open(File.join(@@cache_path, "test_file_#{n}"), "w")
175
+ test.puts Time.now
176
+ test.close
177
+ end
178
+
179
+ AmazonAssociate::CachingStrategy::Filesystem.expects(:sweep_time_expired?).once.returns(true)
180
+ do_request
181
+
182
+ (0..9).each do |n|
183
+ assert !FileTest.exists?(File.join(@@cache_path, "test_file_#{n}"))
184
+ end
185
+ end
186
+
187
+ end
188
+
189
+ protected
190
+ def do_request
191
+ @resp = AmazonAssociate::Request.item_lookup("0974514055")
192
+ end
193
+ end
@@ -0,0 +1,90 @@
1
+ require File.dirname(__FILE__) + "/../test_helper"
2
+
3
+ class AmazonAssociate::CartTest < Test::Unit::TestCase
4
+
5
+ # create a cart to store cart_id and hmac for add, get, modify, and clear tests
6
+ def setup
7
+ sleep(2)
8
+ @asin = "0672328844"
9
+ resp = AmazonAssociate::Request.cart_create(@asin)
10
+ @cart_id = resp.doc.get_elements_by_tag_name("cartid").inner_text
11
+ @hmac = resp.doc.get_elements_by_tag_name("hmac").inner_text
12
+ item = resp.first_item
13
+ # run tests for cart_create with default quantity while we"re at it
14
+ assert resp.is_valid_request?
15
+ assert_equal @asin, item.get("asin")
16
+ assert_equal "1", item.get("quantity")
17
+ assert_not_nil @cart_id
18
+ assert_not_nil @hmac
19
+ end
20
+
21
+ # Test cart_get
22
+ def test_cart_get
23
+ resp = AmazonAssociate::Request.cart_get(@cart_id, @hmac)
24
+ assert resp.is_valid_request?
25
+ assert_not_nil resp.doc.get_elements_by_tag_name("purchaseurl").inner_text
26
+ end
27
+
28
+ # Test cart_modify
29
+ def test_cart_modify
30
+ resp = AmazonAssociate::Request.cart_get(@cart_id, @hmac)
31
+ cart_item_id = resp.doc.get_elements_by_tag_name("cartitemid").inner_text
32
+ resp = AmazonAssociate::Request.cart_modify(cart_item_id, @cart_id, @hmac, 2)
33
+ item = resp.first_item
34
+
35
+ assert resp.is_valid_request?
36
+ assert_equal "2", item.get("quantity")
37
+ assert_not_nil resp.doc.get_elements_by_tag_name("purchaseurl").inner_text
38
+ end
39
+
40
+ # Test cart_clear
41
+ def test_cart_clear
42
+ resp = AmazonAssociate::Request.cart_clear(@cart_id, @hmac)
43
+ assert resp.is_valid_request?
44
+ end
45
+
46
+ ## Test cart_create with a specified quantity
47
+ ## note this will create a separate cart
48
+ def test_cart_create_with_quantity
49
+ asin = "0672328844"
50
+ resp = AmazonAssociate::Request.cart_create(asin, :quantity => 2)
51
+ assert resp.is_valid_request?
52
+ item = resp.first_item
53
+ assert_equal asin, item.get("asin")
54
+ assert_equal "2", item.get("quantity")
55
+ assert_not_nil resp.doc.get_elements_by_tag_name("cartid").inner_text
56
+ assert_not_nil resp.doc.get_elements_by_tag_name("hmac").inner_text
57
+ end
58
+
59
+ # Test cart_create with an array of hashes representing multiple items
60
+ def test_cart_create_with_multiple_items
61
+ items = [ { :asin => "0974514055", :quantity => 2 }, { :asin => "0672328844", :quantity => 3 } ]
62
+ resp = AmazonAssociate::Request.cart_create(items)
63
+ assert resp.is_valid_request?
64
+ first_item, second_item = resp.items.reverse[0], resp.items.reverse[1]
65
+
66
+ assert_equal items[0][:asin], first_item.get("asin")
67
+ assert_equal items[0][:quantity].to_s, first_item.get("quantity")
68
+
69
+ assert_equal items[1][:asin], second_item.get("asin")
70
+ assert_equal items[1][:quantity].to_s, second_item.get("quantity")
71
+
72
+ assert_not_nil resp.doc.get_elements_by_tag_name("cartid").inner_text
73
+ assert_not_nil resp.doc.get_elements_by_tag_name("hmac").inner_text
74
+ end
75
+
76
+ # Test cart_create with offer_listing_id instead of asin
77
+ def test_cart_create_with_offer_listing_id
78
+ items = [ { :offer_listing_id => "MCK%2FnCXIges8tpX%2B222nOYEqeZ4AzbrFyiHuP6pFf45N3vZHTm8hFTytRF%2FLRONNkVmt182%2BmeX72n%2BbtUcGEtpLN92Oy9Y7", :quantity => 2 } ]
79
+ resp = AmazonAssociate::Request.cart_create(items)
80
+ assert resp.is_valid_request?
81
+ first_item = resp.items.first
82
+
83
+ assert_equal items[0][:offer_listing_id], first_item.get("offerlistingid")
84
+ assert_equal items[0][:quantity].to_s, first_item.get("quantity")
85
+
86
+ assert_not_nil resp.doc.get_elements_by_tag_name("cartid").inner_text
87
+ assert_not_nil resp.doc.get_elements_by_tag_name("hmac").inner_text
88
+ end
89
+
90
+ end
@@ -0,0 +1,108 @@
1
+ require File.dirname(__FILE__) + "/../test_helper"
2
+
3
+ class AmazonAssociate::RequestTest < Test::Unit::TestCase
4
+ def setup
5
+ sleep(1)
6
+ AmazonAssociate::Request.configure do |options|
7
+ options[:response_group] = "Large"
8
+ end
9
+ end
10
+
11
+ ## Test item_search
12
+ def test_item_search
13
+ resp = AmazonAssociate::Request.item_search("ruby")
14
+ assert(resp.is_valid_request?)
15
+ assert(resp.total_results >= 3600)
16
+ assert(resp.total_pages >= 360)
17
+ end
18
+
19
+ def test_item_search_with_paging
20
+ resp = AmazonAssociate::Request.item_search("ruby", :item_page => 2)
21
+ assert resp.is_valid_request?
22
+ assert 2, resp.item_page
23
+ end
24
+
25
+ def test_item_search_with_invalid_request
26
+ resp = AmazonAssociate::Request.item_search(nil)
27
+ assert !resp.is_valid_request?
28
+ end
29
+
30
+ def test_item_search_with_no_result
31
+ resp = AmazonAssociate::Request.item_search("afdsafds")
32
+
33
+ assert resp.is_valid_request?
34
+ assert_equal "We did not find any matches for your request.",
35
+ resp.error
36
+ end
37
+
38
+ def test_item_search_uk
39
+ resp = AmazonAssociate::Request.item_search("ruby", :country => :uk)
40
+ assert resp.is_valid_request?
41
+ end
42
+
43
+ def test_item_search_by_author
44
+ resp = AmazonAssociate::Request.item_search("dave", :type => :author)
45
+ assert resp.is_valid_request?
46
+ end
47
+
48
+ def test_item_get
49
+ resp = AmazonAssociate::Request.item_search("0974514055")
50
+ item = resp.first_item
51
+
52
+ # test get
53
+ assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition",
54
+ item.get("itemattributes/title")
55
+
56
+ # test get_array
57
+ assert_equal ["Dave Thomas", "Chad Fowler", "Andy Hunt"],
58
+ item.get_array("author")
59
+
60
+ # test get_hash
61
+ small_image = item.get_hash("smallimage")
62
+
63
+ assert_equal 3, small_image.keys.size
64
+ assert small_image[:url] != nil
65
+ assert_equal "75", small_image[:height]
66
+ assert_equal "59", small_image[:width]
67
+
68
+ # test /
69
+ reviews = item/"editorialreview"
70
+ reviews.each do |review|
71
+ # returns unescaped HTML content, Hpricot escapes all text values
72
+ assert AmazonAssociate::Element.get_unescaped(review, "source")
73
+ assert AmazonAssociate::Element.get_unescaped(review, "content")
74
+ end
75
+ end
76
+
77
+ ## Test item_lookup
78
+ def test_item_lookup
79
+ resp = AmazonAssociate::Request.item_lookup("0974514055")
80
+ assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition",
81
+ resp.first_item.get("itemattributes/title")
82
+ end
83
+
84
+ def test_item_lookup_with_invalid_request
85
+ resp = AmazonAssociate::Request.item_lookup(nil)
86
+ assert resp.has_error?
87
+ assert resp.error
88
+ end
89
+
90
+ def test_item_lookup_with_no_result
91
+ resp = AmazonAssociate::Request.item_lookup("abc")
92
+
93
+ assert resp.is_valid_request?
94
+ assert_match(/ABC is not a valid value for ItemId/, resp.error)
95
+ end
96
+
97
+ def test_search_and_convert
98
+ resp = AmazonAssociate::Request.item_lookup("0974514055")
99
+ title = resp.first_item.get("itemattributes/title")
100
+ authors = resp.first_item.search_and_convert("author")
101
+
102
+ assert_equal "Programming Ruby: The Pragmatic Programmers' Guide, Second Edition", title
103
+ assert authors.is_a?(Array)
104
+ assert 3, authors.size
105
+ assert_equal "Dave Thomas", authors.first.get
106
+ end
107
+
108
+ end