reactive_shipping 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (247) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +33 -0
  4. data/.yardopts +13 -0
  5. data/CHANGELOG.md +225 -0
  6. data/CONTRIBUTING.md +23 -0
  7. data/Gemfile +3 -0
  8. data/MIT-LICENSE +21 -0
  9. data/README.md +158 -0
  10. data/Rakefile +35 -0
  11. data/dev.yml +17 -0
  12. data/gemfiles/activesupport42.gemfile +5 -0
  13. data/gemfiles/activesupport50.gemfile +6 -0
  14. data/gemfiles/activesupport51.gemfile +5 -0
  15. data/gemfiles/activesupport52.gemfile +5 -0
  16. data/gemfiles/activesupport_master.gemfile +5 -0
  17. data/lib/certs/eParcel.dtd +111 -0
  18. data/lib/reactive_shipping.rb +26 -0
  19. data/lib/reactive_shipping/address_validation_response.rb +30 -0
  20. data/lib/reactive_shipping/carrier.rb +184 -0
  21. data/lib/reactive_shipping/carriers.rb +35 -0
  22. data/lib/reactive_shipping/carriers/australia_post.rb +248 -0
  23. data/lib/reactive_shipping/carriers/benchmark_carrier.rb +31 -0
  24. data/lib/reactive_shipping/carriers/bogus_carrier.rb +12 -0
  25. data/lib/reactive_shipping/carriers/canada_post.rb +263 -0
  26. data/lib/reactive_shipping/carriers/canada_post_pws.rb +908 -0
  27. data/lib/reactive_shipping/carriers/fedex.rb +797 -0
  28. data/lib/reactive_shipping/carriers/kunaki.rb +155 -0
  29. data/lib/reactive_shipping/carriers/new_zealand_post.rb +260 -0
  30. data/lib/reactive_shipping/carriers/shipwire.rb +178 -0
  31. data/lib/reactive_shipping/carriers/stamps.rb +860 -0
  32. data/lib/reactive_shipping/carriers/ups.rb +1060 -0
  33. data/lib/reactive_shipping/carriers/usps.rb +708 -0
  34. data/lib/reactive_shipping/carriers/usps_returns.rb +86 -0
  35. data/lib/reactive_shipping/delivery_date_estimate.rb +20 -0
  36. data/lib/reactive_shipping/delivery_date_estimates_response.rb +11 -0
  37. data/lib/reactive_shipping/errors.rb +35 -0
  38. data/lib/reactive_shipping/external_return_label_request.rb +417 -0
  39. data/lib/reactive_shipping/external_return_label_response.rb +26 -0
  40. data/lib/reactive_shipping/label.rb +10 -0
  41. data/lib/reactive_shipping/label_response.rb +10 -0
  42. data/lib/reactive_shipping/location.rb +166 -0
  43. data/lib/reactive_shipping/package.rb +165 -0
  44. data/lib/reactive_shipping/package_item.rb +60 -0
  45. data/lib/reactive_shipping/rate_estimate.rb +197 -0
  46. data/lib/reactive_shipping/rate_response.rb +33 -0
  47. data/lib/reactive_shipping/response.rb +44 -0
  48. data/lib/reactive_shipping/shipment_event.rb +22 -0
  49. data/lib/reactive_shipping/shipment_packer.rb +108 -0
  50. data/lib/reactive_shipping/shipping_response.rb +34 -0
  51. data/lib/reactive_shipping/tracking_response.rb +120 -0
  52. data/lib/reactive_shipping/version.rb +3 -0
  53. data/reactive_shipping.gemspec +38 -0
  54. data/shipit.rubygems.yml +1 -0
  55. data/test/console.rb +39 -0
  56. data/test/credentials.yml +76 -0
  57. data/test/fixtures/files/label1.pdf +0 -0
  58. data/test/fixtures/files/ups-shipping-label.gif +0 -0
  59. data/test/fixtures/json/australia_post/calculate_domestic.json +13 -0
  60. data/test/fixtures/json/australia_post/calculate_domestic_2.json +19 -0
  61. data/test/fixtures/json/australia_post/calculate_international.json +12 -0
  62. data/test/fixtures/json/australia_post/calculate_international_2.json +15 -0
  63. data/test/fixtures/json/australia_post/error_message.json +5 -0
  64. data/test/fixtures/json/australia_post/service_domestic.json +117 -0
  65. data/test/fixtures/json/australia_post/service_domestic_2.json +117 -0
  66. data/test/fixtures/json/australia_post/service_international.json +76 -0
  67. data/test/fixtures/json/australia_post/service_international_2.json +59 -0
  68. data/test/fixtures/json/newzealandpost/domestic_book.json +1 -0
  69. data/test/fixtures/json/newzealandpost/domestic_default.json +1 -0
  70. data/test/fixtures/json/newzealandpost/domestic_error.json +1 -0
  71. data/test/fixtures/json/newzealandpost/domestic_poster.json +1 -0
  72. data/test/fixtures/json/newzealandpost/domestic_small_half_pound.json +1 -0
  73. data/test/fixtures/json/newzealandpost/international_book.json +1 -0
  74. data/test/fixtures/json/newzealandpost/international_new_zealand_wii.json +1 -0
  75. data/test/fixtures/json/newzealandpost/international_small_half_pound.json +1 -0
  76. data/test/fixtures/json/newzealandpost/international_wii.json +1 -0
  77. data/test/fixtures/xml/canadapost/example_request.xml +25 -0
  78. data/test/fixtures/xml/canadapost/example_response.xml +130 -0
  79. data/test/fixtures/xml/canadapost/example_response_error.xml +16 -0
  80. data/test/fixtures/xml/canadapost/example_response_french.xml +122 -0
  81. data/test/fixtures/xml/canadapost/example_response_with_nil_value.xml +164 -0
  82. data/test/fixtures/xml/canadapost/example_response_with_postal_outlet.xml +155 -0
  83. data/test/fixtures/xml/canadapost/example_response_with_postal_outlet_french.xml +274 -0
  84. data/test/fixtures/xml/canadapost/example_response_with_strange_delivery_date.xml +130 -0
  85. data/test/fixtures/xml/canadapost_pws/dnc_tracking_details_en.xml +112 -0
  86. data/test/fixtures/xml/canadapost_pws/merchant_details_error.xml +7 -0
  87. data/test/fixtures/xml/canadapost_pws/merchant_details_response.xml +7 -0
  88. data/test/fixtures/xml/canadapost_pws/option_response.xml +13 -0
  89. data/test/fixtures/xml/canadapost_pws/option_response_no_conflicts.xml +7 -0
  90. data/test/fixtures/xml/canadapost_pws/rates_info.xml +190 -0
  91. data/test/fixtures/xml/canadapost_pws/rates_info_error.xml +7 -0
  92. data/test/fixtures/xml/canadapost_pws/receipt_response.xml +42 -0
  93. data/test/fixtures/xml/canadapost_pws/receipt_response_no_priced_options.xml +36 -0
  94. data/test/fixtures/xml/canadapost_pws/register_token_error.xml +7 -0
  95. data/test/fixtures/xml/canadapost_pws/register_token_response.xml +3 -0
  96. data/test/fixtures/xml/canadapost_pws/service_options_response.xml +42 -0
  97. data/test/fixtures/xml/canadapost_pws/services_error.xml +6 -0
  98. data/test/fixtures/xml/canadapost_pws/services_response.xml +32 -0
  99. data/test/fixtures/xml/canadapost_pws/shipment_domestic.xml +69 -0
  100. data/test/fixtures/xml/canadapost_pws/shipment_response.xml +20 -0
  101. data/test/fixtures/xml/canadapost_pws/shipment_us.xml +69 -0
  102. data/test/fixtures/xml/canadapost_pws/tracking_details_en.xml +152 -0
  103. data/test/fixtures/xml/canadapost_pws/tracking_details_en_error.xml +7 -0
  104. data/test/fixtures/xml/canadapost_pws/tracking_details_en_undelivered.xml +116 -0
  105. data/test/fixtures/xml/canadapost_pws/tracking_details_fr.xml +156 -0
  106. data/test/fixtures/xml/canadapost_pws/tracking_details_no_expected_delivery_date.xml +40 -0
  107. data/test/fixtures/xml/fedex/create_shipment_response.xml +2 -0
  108. data/test/fixtures/xml/fedex/freight_rate_request.xml +82 -0
  109. data/test/fixtures/xml/fedex/freight_rate_response.xml +506 -0
  110. data/test/fixtures/xml/fedex/invalid_fedex_reply.xml +27 -0
  111. data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_commercial_rate_request.xml +79 -0
  112. data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_no_saturday_rate_request.xml +79 -0
  113. data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_request.xml +80 -0
  114. data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_response.xml +214 -0
  115. data/test/fixtures/xml/fedex/raterequest_reply.xml +213 -0
  116. data/test/fixtures/xml/fedex/raterequest_response_with_ground_home_delivery.xml +206 -0
  117. data/test/fixtures/xml/fedex/reply_without_notifications.xml +185 -0
  118. data/test/fixtures/xml/fedex/tracking_request.xml +29 -0
  119. data/test/fixtures/xml/fedex/tracking_response_bad_tracking_number.xml +20 -0
  120. data/test/fixtures/xml/fedex/tracking_response_delivered_at_door.xml +254 -0
  121. data/test/fixtures/xml/fedex/tracking_response_delivered_at_facility.xml +403 -0
  122. data/test/fixtures/xml/fedex/tracking_response_delivered_with_signature.xml +269 -0
  123. data/test/fixtures/xml/fedex/tracking_response_empty_status_detail.xml +84 -0
  124. data/test/fixtures/xml/fedex/tracking_response_failure_code_9045.xml +52 -0
  125. data/test/fixtures/xml/fedex/tracking_response_failure_code_9080.xml +51 -0
  126. data/test/fixtures/xml/fedex/tracking_response_in_transit.xml +127 -0
  127. data/test/fixtures/xml/fedex/tracking_response_invalid_tracking_number.xml +52 -0
  128. data/test/fixtures/xml/fedex/tracking_response_missing_status_code.xml +89 -0
  129. data/test/fixtures/xml/fedex/tracking_response_multiple_results.xml +100 -0
  130. data/test/fixtures/xml/fedex/tracking_response_not_found.xml +52 -0
  131. data/test/fixtures/xml/fedex/tracking_response_shipment_exception.xml +209 -0
  132. data/test/fixtures/xml/fedex/tracking_response_unable_to_process.xml +32 -0
  133. data/test/fixtures/xml/fedex/tracking_response_with_blank_state.xml +107 -0
  134. data/test/fixtures/xml/fedex/unknown_fedex_document_reply.xml +3 -0
  135. data/test/fixtures/xml/kunaki/invalid_state_response.xml +3 -0
  136. data/test/fixtures/xml/kunaki/no_valid_items_response.xml +3 -0
  137. data/test/fixtures/xml/kunaki/successful_rates_response.xml +3 -0
  138. data/test/fixtures/xml/kunaki/unsuccessful_rates_response.xml +9 -0
  139. data/test/fixtures/xml/shipwire/international_rates_response.xml +17 -0
  140. data/test/fixtures/xml/shipwire/new_carrier_rate_response.xml +18 -0
  141. data/test/fixtures/xml/shipwire/no_rates_response.xml +7 -0
  142. data/test/fixtures/xml/shipwire/rates_response.xml +36 -0
  143. data/test/fixtures/xml/shipwire/rates_response_no_estimate.xml +14 -0
  144. data/test/fixtures/xml/stamps/authenticate_user_request.xml +15 -0
  145. data/test/fixtures/xml/stamps/authenticate_user_response.xml +10 -0
  146. data/test/fixtures/xml/stamps/cleanse_address_request.xml +19 -0
  147. data/test/fixtures/xml/stamps/cleanse_address_response.xml +27 -0
  148. data/test/fixtures/xml/stamps/create_indicium_request.xml +69 -0
  149. data/test/fixtures/xml/stamps/create_indicium_response.xml +40 -0
  150. data/test/fixtures/xml/stamps/expired_authenticator_response.xml +15 -0
  151. data/test/fixtures/xml/stamps/get_account_info_request.xml +11 -0
  152. data/test/fixtures/xml/stamps/get_account_info_response.xml +36 -0
  153. data/test/fixtures/xml/stamps/get_purchase_status_request.xml +12 -0
  154. data/test/fixtures/xml/stamps/get_purchase_status_response.xml +16 -0
  155. data/test/fixtures/xml/stamps/get_rates_request.xml +19 -0
  156. data/test/fixtures/xml/stamps/get_rates_response.xml +351 -0
  157. data/test/fixtures/xml/stamps/purchase_postage_request.xml +13 -0
  158. data/test/fixtures/xml/stamps/purchase_postage_response.xml +17 -0
  159. data/test/fixtures/xml/stamps/track_shipment_request.xml +12 -0
  160. data/test/fixtures/xml/stamps/track_shipment_response.xml +45 -0
  161. data/test/fixtures/xml/ups/access_request.xml +6 -0
  162. data/test/fixtures/xml/ups/delivered_shipment_with_refund.xml +290 -0
  163. data/test/fixtures/xml/ups/delivered_shipment_without_events_tracking_response.xml +62 -0
  164. data/test/fixtures/xml/ups/delivery_dates_response.xml +140 -0
  165. data/test/fixtures/xml/ups/example_tracking_response.xml +53 -0
  166. data/test/fixtures/xml/ups/in_transit_shipment.xml +183 -0
  167. data/test/fixtures/xml/ups/out_for_delivery_shipment.xml +165 -0
  168. data/test/fixtures/xml/ups/package_exceeds_maximum_length.xml +12 -0
  169. data/test/fixtures/xml/ups/rate_single_service.xml +54 -0
  170. data/test/fixtures/xml/ups/rescheduled_shipment.xml +204 -0
  171. data/test/fixtures/xml/ups/shipment_accept_response.xml +42 -0
  172. data/test/fixtures/xml/ups/shipment_confirm_response.xml +33 -0
  173. data/test/fixtures/xml/ups/shipment_from_tiger_direct.xml +222 -0
  174. data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response.xml +290 -0
  175. data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response_with_insured.xml +289 -0
  176. data/test/fixtures/xml/ups/test_real_home_as_residential_destination_with_origin_account_response.xml +311 -0
  177. data/test/fixtures/xml/ups/tracking_request.xml +9 -0
  178. data/test/fixtures/xml/ups/triple_accept_response.xml +72 -0
  179. data/test/fixtures/xml/ups/triple_confirm_response.xml +32 -0
  180. data/test/fixtures/xml/ups/void_shipment_response.xml +11 -0
  181. data/test/fixtures/xml/usps/api_error_rate_response.xml +53 -0
  182. data/test/fixtures/xml/usps/beverly_hills_to_new_york_book_commercial_base_rate_response.xml +2 -0
  183. data/test/fixtures/xml/usps/beverly_hills_to_new_york_book_commercial_plus_rate_response.xml +258 -0
  184. data/test/fixtures/xml/usps/beverly_hills_to_new_york_book_rate_response.xml +108 -0
  185. data/test/fixtures/xml/usps/beverly_hills_to_ottawa_american_wii_commercial_base_rate_response.xml +84 -0
  186. data/test/fixtures/xml/usps/beverly_hills_to_ottawa_american_wii_commercial_plus_rate_response.xml +212 -0
  187. data/test/fixtures/xml/usps/beverly_hills_to_ottawa_american_wii_rate_response.xml +230 -0
  188. data/test/fixtures/xml/usps/first_class_packages_with_invalid_mail_type_response.xml +12 -0
  189. data/test/fixtures/xml/usps/first_class_packages_with_mail_type_response.xml +16 -0
  190. data/test/fixtures/xml/usps/first_class_packages_without_mail_type_response.xml +12 -0
  191. data/test/fixtures/xml/usps/invalid_xml_response.xml +10 -0
  192. data/test/fixtures/xml/usps/invalid_xml_tracking_response_error.xml +2 -0
  193. data/test/fixtures/xml/usps/tracking_request.xml +10 -0
  194. data/test/fixtures/xml/usps/tracking_request_batch.xml +12 -0
  195. data/test/fixtures/xml/usps/tracking_response.xml +162 -0
  196. data/test/fixtures/xml/usps/tracking_response_alt.xml +53 -0
  197. data/test/fixtures/xml/usps/tracking_response_batch.xml +231 -0
  198. data/test/fixtures/xml/usps/tracking_response_failure.xml +11 -0
  199. data/test/fixtures/xml/usps/tracking_response_not_available.xml +12 -0
  200. data/test/fixtures/xml/usps/tracking_response_test_error.xml +8 -0
  201. data/test/fixtures/xml/usps/us_rate_request.xml +18 -0
  202. data/test/fixtures/xml/usps/us_rate_request_large.xml +18 -0
  203. data/test/fixtures/xml/usps/world_rate_request_only_country.xml +22 -0
  204. data/test/fixtures/xml/usps/world_rate_request_with_value.xml +24 -0
  205. data/test/fixtures/xml/usps/world_rate_request_without_value.xml +24 -0
  206. data/test/fixtures/xml/usps_returns/external_return_label_response.xml +2 -0
  207. data/test/fixtures/xml/usps_returns/external_return_label_response_failure.xml +10 -0
  208. data/test/remote/australia_post_test.rb +140 -0
  209. data/test/remote/canada_post_pws_platform_test.rb +259 -0
  210. data/test/remote/canada_post_pws_test.rb +169 -0
  211. data/test/remote/canada_post_test.rb +55 -0
  212. data/test/remote/fedex_test.rb +400 -0
  213. data/test/remote/kunaki_test.rb +37 -0
  214. data/test/remote/new_zealand_post_test.rb +149 -0
  215. data/test/remote/shipwire_test.rb +84 -0
  216. data/test/remote/stamps_test.rb +396 -0
  217. data/test/remote/usps_returns_test.rb +72 -0
  218. data/test/remote/usps_test.rb +243 -0
  219. data/test/test_helper.rb +296 -0
  220. data/test/unit/carrier_test.rb +130 -0
  221. data/test/unit/carriers/australia_post_test.rb +181 -0
  222. data/test/unit/carriers/benchmark_test.rb +18 -0
  223. data/test/unit/carriers/canada_post_pws_rating_test.rb +379 -0
  224. data/test/unit/carriers/canada_post_pws_register_test.rb +76 -0
  225. data/test/unit/carriers/canada_post_pws_shipping_test.rb +258 -0
  226. data/test/unit/carriers/canada_post_pws_test.rb +59 -0
  227. data/test/unit/carriers/canada_post_pws_tracking_test.rb +154 -0
  228. data/test/unit/carriers/canada_post_test.rb +148 -0
  229. data/test/unit/carriers/fedex_test.rb +693 -0
  230. data/test/unit/carriers/kunaki_test.rb +56 -0
  231. data/test/unit/carriers/new_zealand_post_test.rb +177 -0
  232. data/test/unit/carriers/shipwire_test.rb +188 -0
  233. data/test/unit/carriers/stamps_test.rb +245 -0
  234. data/test/unit/carriers/ups_test.rb +580 -0
  235. data/test/unit/carriers/usps_returns_test.rb +45 -0
  236. data/test/unit/carriers/usps_test.rb +633 -0
  237. data/test/unit/carriers_test.rb +16 -0
  238. data/test/unit/external_return_label_request_test.rb +258 -0
  239. data/test/unit/location_test.rb +234 -0
  240. data/test/unit/package_item_test.rb +232 -0
  241. data/test/unit/package_test.rb +404 -0
  242. data/test/unit/rate_estimate_test.rb +93 -0
  243. data/test/unit/response_test.rb +38 -0
  244. data/test/unit/shipment_event_test.rb +20 -0
  245. data/test/unit/shipment_packer_test.rb +212 -0
  246. data/test/unit/tracking_response_test.rb +41 -0
  247. metadata +684 -0
@@ -0,0 +1,148 @@
1
+ require 'test_helper'
2
+
3
+ class CanadaPostTest < ActiveSupport::TestCase
4
+ include ReactiveShipping::Test::Fixtures
5
+
6
+ def setup
7
+ login = { login: 'CPC_DEMO_XML' }
8
+
9
+ @carrier = CanadaPost.new(login)
10
+ @french_carrier = CanadaPost.new(login.merge(french: true))
11
+
12
+ @request = xml_fixture('canadapost/example_request')
13
+ @response = xml_fixture('canadapost/example_response')
14
+ @response_french = xml_fixture('canadapost/example_response_french')
15
+ @bad_response = xml_fixture('canadapost/example_response_error')
16
+
17
+ @origin = {:address1 => "61A York St", :city => "Ottawa", :province => "ON", :country => "Canada", :postal_code => "K1N 5T2"}
18
+ @destination = {:city => "Beverly Hills", :state => "CA", :country => "United States", :postal_code => "90210"}
19
+ @line_items = [Package.new(500, [2, 3, 4], :description => "a box full of stuff", :value => 2500)]
20
+ end
21
+
22
+ def test_parse_rate_response_french
23
+ @french_carrier.expects(:ssl_post).returns(@response_french)
24
+ rate_estimates = @french_carrier.find_rates(@origin, @destination, @line_items)
25
+
26
+ rate_estimates.rates.each do |rate|
27
+ assert_instance_of RateEstimate, rate
28
+ assert_instance_of Date, rate.delivery_date
29
+ assert_instance_of Date, rate.shipping_date
30
+ assert_instance_of String, rate.service_name
31
+ assert_kind_of Integer, rate.total_price
32
+ end
33
+
34
+ rate_estimates.boxes.each do |box|
35
+ assert_instance_of CanadaPost::Box, box
36
+ assert_instance_of String, box.name
37
+ assert_instance_of Float, box.weight
38
+ assert_instance_of Float, box.expediter_weight
39
+ assert_instance_of Float, box.length
40
+ assert_instance_of Float, box.height
41
+ assert_instance_of Float, box.width
42
+
43
+ box.packedItems.each do |p|
44
+ assert_kind_of Integer, p.quantity
45
+ assert_instance_of String, p.description
46
+ end
47
+ end
48
+ end
49
+
50
+ def test_build_rate_request
51
+ @carrier.expects(:commit).with(@request, @origin, @destination, {})
52
+ @carrier.find_rates(@origin, @destination, @line_items)
53
+ end
54
+
55
+ def test_parse_rate_response
56
+ @carrier.expects(:ssl_post).returns(@response)
57
+ rate_estimates = @carrier.find_rates(@origin, @destination, @line_items)
58
+
59
+ rate_estimates.rates.each do |rate|
60
+ assert_instance_of RateEstimate, rate
61
+ assert_instance_of Date, rate.delivery_date
62
+ assert_instance_of Date, rate.shipping_date
63
+ assert_instance_of String, rate.service_name
64
+ assert_kind_of Integer, rate.total_price
65
+ end
66
+
67
+ rate_estimates.boxes.each do |box|
68
+ assert_instance_of CanadaPost::Box, box
69
+ assert_instance_of String, box.name
70
+ assert_instance_of Float, box.weight
71
+ assert_instance_of Float, box.expediter_weight
72
+ assert_instance_of Float, box.length
73
+ assert_instance_of Float, box.height
74
+ assert_instance_of Float, box.width
75
+
76
+ box.packedItems.each do |p|
77
+ assert_kind_of Integer, p.quantity
78
+ assert_instance_of String, p.description
79
+ end
80
+ end
81
+ end
82
+
83
+ def test_non_success_parse_rate_response
84
+ @carrier.expects(:ssl_post).returns(@bad_response)
85
+
86
+ error = assert_raises(ReactiveShipping::ResponseError) do
87
+ @carrier.find_rates(@origin, @destination, @line_items)
88
+ end
89
+
90
+ assert_equal 'Parcel too heavy to be shipped with CPC.', error.message
91
+ end
92
+
93
+ def test_turn_around_time
94
+ @carrier.expects(:commit).with do |request, _options|
95
+ parsed_request = Hash.from_xml(request)
96
+ parsed_request['eparcel']['ratesAndServicesRequest']['turnAroundTime'] == "0"
97
+ end
98
+ @carrier.find_rates(@origin, @destination, @line_items, :turn_around_time => 0)
99
+ end
100
+
101
+ def test_build_line_items
102
+ line_items_xml = Nokogiri::XML::Builder.new do |xml|
103
+ @carrier.send(:build_line_items, xml, @line_items)
104
+ end
105
+
106
+ assert_instance_of Nokogiri::XML::Builder, line_items_xml
107
+ assert_equal "a box full of stuff", line_items_xml.doc.at_xpath('//description').text
108
+ assert_equal "0.5", line_items_xml.doc.at_xpath('//weight').text
109
+ end
110
+
111
+ def test_non_iso_country_names
112
+ @destination[:country] = 'RU'
113
+ @carrier.expects(:ssl_post).with(anything, regexp_matches(%r{<country>Russia</country>})).returns(@response)
114
+ @carrier.find_rates(@origin, @destination, @line_items)
115
+ end
116
+
117
+ def test_delivery_range_based_on_delivery_date
118
+ @carrier.expects(:ssl_post).returns(@response)
119
+ rate_estimates = @carrier.find_rates(@origin, @destination, @line_items)
120
+
121
+ delivery_date = Date.new(2010, 8, 4)
122
+ assert_equal [delivery_date] * 2, rate_estimates.rates[0].delivery_range
123
+ assert_equal [delivery_date] * 2, rate_estimates.rates[1].delivery_range
124
+ assert_equal [delivery_date + 2.days] * 2, rate_estimates.rates[2].delivery_range
125
+ end
126
+
127
+ def test_delivery_range_with_invalid_date
128
+ @response = xml_fixture('canadapost/example_response_with_strange_delivery_date')
129
+ @carrier.expects(:ssl_post).returns(@response)
130
+ rate_estimates = @carrier.find_rates(@origin, @destination, @line_items)
131
+
132
+ assert_equal [], rate_estimates.rates[0].delivery_range
133
+ end
134
+
135
+ def test_line_items_with_nil_values
136
+ @response = xml_fixture('canadapost/example_response_with_nil_value')
137
+ @carrier.expects(:ssl_post).returns(@response)
138
+
139
+ @line_items << Package.new(500, [2, 3, 4], :description => "another box full of stuff", :value => nil)
140
+ rate_response = @carrier.find_rates(@origin, @destination, @line_items)
141
+
142
+ assert rate_response.rates.length > 0, "Expecting rateestimates even without a value specified."
143
+ end
144
+
145
+ def test_maximum_address_field_length
146
+ assert_equal 44, @carrier.maximum_address_field_length
147
+ end
148
+ end
@@ -0,0 +1,693 @@
1
+ require 'test_helper'
2
+
3
+ class FedExTest < ActiveSupport::TestCase
4
+ include ReactiveShipping::Test::Fixtures
5
+
6
+ def setup
7
+ @carrier = FedEx.new(:key => '1111', :password => '2222', :account => '3333', :login => '4444')
8
+ end
9
+
10
+ def test_initialize_options_requirements
11
+ assert_raises(ArgumentError) { FedEx.new }
12
+ assert_raises(ArgumentError) { FedEx.new(:login => '999999999') }
13
+ assert_raises(ArgumentError) { FedEx.new(:password => '7777777') }
14
+ FedEx.new(:key => '999999999', :password => '7777777', :account => '123', :login => '123')
15
+ end
16
+
17
+ def test_business_days
18
+ today = DateTime.parse("Tue 12 Mar 2013 00:00:00-0400")
19
+
20
+ Timecop.freeze(today) do
21
+ assert_equal DateTime.parse("Wed 13 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 1)
22
+ assert_equal DateTime.parse("Fri 15 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 3)
23
+ assert_equal DateTime.parse("Mon 18 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 4)
24
+ assert_equal DateTime.parse("Tue 19 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 5)
25
+ end
26
+ end
27
+
28
+ def test_home_delivery_business_days
29
+ today = DateTime.parse("Tue 12 Mar 2013 00:00:00-0400")
30
+
31
+ Timecop.freeze(today) do
32
+ assert_equal DateTime.parse("Wed 13 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 1, true)
33
+ assert_equal DateTime.parse("Fri 15 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 3, true)
34
+ assert_equal DateTime.parse("Sat 16 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 4, true)
35
+ assert_equal DateTime.parse("Tue 19 Mar 2013 00:00:00-0400"), @carrier.send(:business_days_from, today, 5, true)
36
+ end
37
+ end
38
+
39
+ def test_turn_around_time_default
40
+ mock_response = xml_fixture('fedex/ottawa_to_beverly_hills_rate_response').gsub('<v6:DeliveryTimestamp>2011-07-29</v6:DeliveryTimestamp>', '')
41
+
42
+ today = Date.parse("Mon 11 Mar 2013")
43
+
44
+ Timecop.freeze(today) do
45
+ delivery_date = Date.today + 7.days # FIVE_DAYS in fixture response, plus weekend
46
+ timestamp = Time.now.iso8601
47
+ @carrier.expects(:commit).with do |request, _options|
48
+ parsed_response = Hash.from_xml(request)
49
+ parsed_response['RateRequest']['RequestedShipment']['ShipTimestamp'] == timestamp
50
+ end.returns(mock_response)
51
+
52
+ destination = ReactiveShipping::Location.from(location_fixtures[:beverly_hills].to_hash, :address_type => :commercial)
53
+ response = @carrier.find_rates location_fixtures[:ottawa], destination, package_fixtures[:book], :test => true
54
+ assert_equal [delivery_date, delivery_date], response.rates.first.delivery_range
55
+ end
56
+ end
57
+
58
+ def test_turn_around_time
59
+ mock_response = xml_fixture('fedex/ottawa_to_beverly_hills_rate_response').gsub('<v6:DeliveryTimestamp>2011-07-29</v6:DeliveryTimestamp>', '')
60
+ Timecop.freeze(DateTime.new(2013, 3, 11)) do
61
+ delivery_date = Date.today + 8.days # FIVE_DAYS in fixture response, plus turn_around_time, plus weekend
62
+ timestamp = (Time.now + 1.day).iso8601
63
+ @carrier.expects(:commit).with do |request, _options|
64
+ parsed_response = Hash.from_xml(request)
65
+ parsed_response['RateRequest']['RequestedShipment']['ShipTimestamp'] == timestamp
66
+ end.returns(mock_response)
67
+
68
+ destination = ReactiveShipping::Location.from(location_fixtures[:beverly_hills].to_hash, :address_type => :commercial)
69
+ response = @carrier.find_rates location_fixtures[:ottawa], destination, package_fixtures[:book], :turn_around_time => 24, :test => true
70
+
71
+ assert_equal [delivery_date, delivery_date], response.rates.first.delivery_range
72
+ end
73
+ end
74
+
75
+ def test_transaction_id_sent_as_customer_transaction_id
76
+ transaction_id = '9999-test'
77
+ @carrier = FedEx.new(:key => '1111', :password => '2222', :account => '3333', :login => '4444', :transaction_id => transaction_id)
78
+ @carrier.expects(:commit).with do |request, _options|
79
+ parsed_request = Hash.from_xml(request)
80
+ parsed_request['RateRequest']['TransactionDetail']['CustomerTransactionId'] == transaction_id
81
+ end.returns(xml_fixture('fedex/ottawa_to_beverly_hills_rate_response'))
82
+
83
+ destination = ReactiveShipping::Location.from(location_fixtures[:beverly_hills].to_hash, :address_type => :commercial)
84
+ @carrier.find_rates location_fixtures[:ottawa], destination, package_fixtures[:book], :test => true
85
+ end
86
+
87
+ def test_building_request_with_address_type_commercial_should_not_include_residential
88
+ mock_response = xml_fixture('fedex/ottawa_to_beverly_hills_rate_response')
89
+ expected_request = xml_fixture('fedex/ottawa_to_beverly_hills_commercial_rate_request')
90
+
91
+ @carrier.expects(:ship_timestamp).returns(Time.parse("2009-07-20T12:01:55-04:00").in_time_zone('US/Eastern'))
92
+ @carrier.expects(:commit).with { |request, test_mode| Hash.from_xml(request) == Hash.from_xml(expected_request) && test_mode }.returns(mock_response)
93
+ destination = ReactiveShipping::Location.from(location_fixtures[:beverly_hills].to_hash, :address_type => :commercial)
94
+ @carrier.find_rates( location_fixtures[:ottawa],
95
+ destination,
96
+ package_fixtures.values_at(:book, :wii), test: true, saturday_delivery: true)
97
+ end
98
+
99
+ def test_building_request_with_no_saturday_delivery_should_not_have_saturday_option_set
100
+ mock_response = xml_fixture('fedex/ottawa_to_beverly_hills_rate_response')
101
+ expected_request = xml_fixture('fedex/ottawa_to_beverly_hills_no_saturday_rate_request')
102
+
103
+ @carrier.expects(:ship_timestamp).returns(Time.parse("2009-07-20T12:01:55-04:00").in_time_zone('US/Eastern'))
104
+ @carrier.expects(:commit).with { |request, test_mode| Hash.from_xml(request) == Hash.from_xml(expected_request) && test_mode }.returns(mock_response)
105
+ destination = ReactiveShipping::Location.from(location_fixtures[:beverly_hills].to_hash)
106
+ @carrier.find_rates( location_fixtures[:ottawa],
107
+ destination,
108
+ package_fixtures.values_at(:book, :wii), test: true)
109
+ end
110
+
111
+ def test_building_freight_request_and_parsing_response
112
+ expected_request = xml_fixture('fedex/freight_rate_request')
113
+ mock_response = xml_fixture('fedex/freight_rate_response')
114
+
115
+ @carrier.expects(:ship_timestamp).returns(Time.parse("2013-11-01T14:04:01-07:00").in_time_zone('US/Pacific'))
116
+ @carrier.expects(:commit).with { |request, test_mode| Hash.from_xml(request) == Hash.from_xml(expected_request) && test_mode }.returns(mock_response)
117
+
118
+ # shipping and billing addresses below are provided by fedex test credentials
119
+
120
+ shipping_location = Location.new( address1: '1202 Chalet Ln',
121
+ address2: 'Do Not Delete - Test Account',
122
+ city: 'Harrison',
123
+ state: 'AR',
124
+ postal_code: '72601',
125
+ country: 'US')
126
+
127
+ billing_location = Location.new( address1: '2000 Freight LTL Testing',
128
+ address2: 'Do Not Delete - Test Account',
129
+ city: 'Harrison',
130
+ state: 'AR',
131
+ postal_code: '72601',
132
+ country: 'US')
133
+
134
+ freight_options = {
135
+ account: '5555',
136
+ billing_location: billing_location,
137
+ payment_type: 'SENDER',
138
+ freight_class: 'CLASS_050',
139
+ packaging: 'PALLET',
140
+ role: 'SHIPPER'
141
+ }
142
+
143
+ response = @carrier.find_rates( shipping_location,
144
+ location_fixtures[:ottawa],
145
+ [package_fixtures[:wii]], freight: freight_options, test: true, saturday_delivery: true )
146
+
147
+ assert_equal ["FedEx Freight Economy", "FedEx Freight Priority"], response.rates.map(&:service_name)
148
+ assert_equal [66263, 68513], response.rates.map(&:price)
149
+
150
+ assert response.success?, response.message
151
+ assert_instance_of Hash, response.params
152
+ assert_instance_of String, response.xml
153
+ assert_instance_of Array, response.rates
154
+ assert response.rates.length > 0, "There should've been more than 0 rates returned"
155
+
156
+ rate = response.rates.first
157
+ assert_equal 'FedEx', rate.carrier
158
+ assert_equal 'USD', rate.currency
159
+ assert_kind_of Integer, rate.total_price
160
+ assert_kind_of Integer, rate.price
161
+ assert_instance_of String, rate.service_name
162
+ assert_instance_of String, rate.service_code
163
+ assert_instance_of Array, rate.package_rates
164
+ assert_equal [package_fixtures[:wii]], rate.packages
165
+
166
+ package_rate = rate.package_rates.first
167
+ assert_instance_of Hash, package_rate
168
+ assert_instance_of Package, package_rate[:package]
169
+ assert_nil package_rate[:rate]
170
+ end
171
+
172
+ def test_building_request_and_parsing_response
173
+ expected_request = xml_fixture('fedex/ottawa_to_beverly_hills_rate_request')
174
+ mock_response = xml_fixture('fedex/ottawa_to_beverly_hills_rate_response')
175
+ @carrier.expects(:ship_timestamp).returns(Time.parse("2009-07-20T12:01:55-04:00").in_time_zone('US/Eastern'))
176
+ @carrier.expects(:commit).with { |request, test_mode| Hash.from_xml(request) == Hash.from_xml(expected_request) && test_mode }.returns(mock_response)
177
+ response = @carrier.find_rates( location_fixtures[:ottawa],
178
+ location_fixtures[:beverly_hills],
179
+ package_fixtures.values_at(:book, :wii), test: true, saturday_delivery: true)
180
+ assert_equal ["FedEx Ground"], response.rates.map(&:service_name)
181
+ assert_equal [3836], response.rates.map(&:price)
182
+
183
+ assert response.success?, response.message
184
+ assert_instance_of Hash, response.params
185
+ assert_instance_of String, response.xml
186
+ assert_instance_of Array, response.rates
187
+ assert response.rates.length > 0, "There should've been more than 0 rates returned"
188
+
189
+ rate = response.rates.first
190
+ assert_equal 'FedEx', rate.carrier
191
+ assert_equal 'CAD', rate.currency
192
+ assert_kind_of Integer, rate.total_price
193
+ assert_kind_of Integer, rate.price
194
+ assert_instance_of String, rate.service_name
195
+ assert_instance_of String, rate.service_code
196
+ assert_instance_of Array, rate.package_rates
197
+ assert_equal package_fixtures.values_at(:book, :wii), rate.packages
198
+
199
+ package_rate = rate.package_rates.first
200
+ assert_instance_of Hash, package_rate
201
+ assert_instance_of Package, package_rate[:package]
202
+ assert_nil package_rate[:rate]
203
+ end
204
+
205
+ def test_parsing_response_with_no_rate_reply
206
+ expected_request = xml_fixture('fedex/ottawa_to_beverly_hills_rate_request')
207
+ mock_response = xml_fixture('fedex/unknown_fedex_document_reply')
208
+
209
+ @carrier.expects(:ship_timestamp).returns(Time.parse("2009-07-20T12:01:55-04:00").in_time_zone('US/Eastern'))
210
+ @carrier.expects(:commit).with { |request, test_mode| Hash.from_xml(request) == Hash.from_xml(expected_request) && test_mode }.returns(mock_response)
211
+ exception = assert_raises(ReactiveShipping::ResponseContentError) do
212
+ @carrier.find_rates( location_fixtures[:ottawa],
213
+ location_fixtures[:beverly_hills],
214
+ package_fixtures.values_at(:book, :wii), test: true, saturday_delivery: true)
215
+ end
216
+ message = "Invalid document \n\n#{mock_response}"
217
+ assert_equal message, exception.message
218
+ end
219
+
220
+ def test_parsing_response_with_no_system_code_for_one_rate_does_not_crash
221
+ mock_response = xml_fixture('fedex/freight_rate_response').gsub('<v13:ServiceType>FEDEX_FREIGHT_ECONOMY</v13:ServiceType>','')
222
+
223
+ @carrier.expects(:commit).returns(mock_response)
224
+ @carrier.logger = Logger.new(StringIO.new)
225
+ @carrier.logger.expects(:warn).once.with("[FedexParseRateError] Some fields where missing in the response: #{mock_response}")
226
+
227
+ rates = @carrier.find_rates( location_fixtures[:ottawa], location_fixtures[:beverly_hills], package_fixtures.values_at(:book, :wii), :test => true)
228
+ assert rates.rates.length == 1
229
+ assert_equal rates.rates[0].service_code, 'FEDEX_FREIGHT_PRIORITY'
230
+ end
231
+
232
+ def test_parsing_response_with_no_system_code_on_any_shipping_rate
233
+ mock_response = xml_fixture('fedex/freight_rate_response').gsub('<v13:ServiceType>FEDEX_FREIGHT_ECONOMY</v13:ServiceType>','').gsub('<v13:ServiceType>FEDEX_FREIGHT_PRIORITY</v13:ServiceType>','')
234
+
235
+ @carrier.expects(:commit).returns(mock_response)
236
+ @carrier.logger = Logger.new(StringIO.new)
237
+ @carrier.logger.expects(:warn).once.with("[FedexParseRateError] Some fields where missing in the response: #{mock_response}")
238
+
239
+ exception = assert_raises(ReactiveShipping::ResponseError) do
240
+ @carrier.find_rates( location_fixtures[:ottawa], location_fixtures[:beverly_hills], package_fixtures.values_at(:book, :wii), :test => true)
241
+ end
242
+ message = "The response from the carrier contained errors and could not be treated"
243
+ assert_equal exception.message, message
244
+ end
245
+
246
+ def test_service_name_for_code
247
+ FedEx::SERVICE_TYPES.each do |capitalized_name, readable_name|
248
+ assert_equal readable_name, FedEx.service_name_for_code(capitalized_name)
249
+ end
250
+ end
251
+
252
+ def test_service_name_for_code_handles_yet_unknown_codes
253
+ assert_equal "FedEx Express Saver Saturday Delivery", FedEx.service_name_for_code('FEDEX_EXPRESS_SAVER_SATURDAY_DELIVERY')
254
+ assert_equal "FedEx Some Weird Rate", FedEx.service_name_for_code('SOME_WEIRD_RATE')
255
+ end
256
+
257
+ def test_delivery_range_based_on_delivery_date
258
+ mock_response = xml_fixture('fedex/ottawa_to_beverly_hills_rate_response').gsub('CAD', 'UKL')
259
+
260
+ @carrier.expects(:commit).returns(mock_response)
261
+ rate_estimates = @carrier.find_rates( location_fixtures[:ottawa],
262
+ location_fixtures[:beverly_hills],
263
+ package_fixtures.values_at(:book, :wii), :test => true)
264
+
265
+ delivery_date = Date.new(2011, 7, 29)
266
+ assert_equal delivery_date, rate_estimates.rates[0].delivery_date
267
+ assert_equal [delivery_date] * 2, rate_estimates.rates[0].delivery_range
268
+ end
269
+
270
+ def test_delivery_date_from_transit_time
271
+ mock_response = xml_fixture('fedex/raterequest_reply').gsub('CAD', 'UKL')
272
+
273
+ @carrier.expects(:commit).returns(mock_response)
274
+
275
+ today = Date.parse("Fri 15 Mar 2013")
276
+
277
+ Timecop.freeze(today) do
278
+ rate_estimates = @carrier.find_rates( location_fixtures[:ottawa],
279
+ location_fixtures[:beverly_hills],
280
+ package_fixtures.values_at(:book, :wii), :test => true)
281
+
282
+ # the above fixture will specify a transit time of 5 days, with 2 weekend days accounted for
283
+ delivery_date = Date.today + 5 + 2
284
+ assert_equal delivery_date, rate_estimates.rates[0].delivery_date
285
+ end
286
+ end
287
+
288
+ def test_delivery_date_from_ground_home_transit_time
289
+ mock_response = xml_fixture('fedex/raterequest_response_with_ground_home_delivery')
290
+
291
+ @carrier.expects(:commit).returns(mock_response)
292
+
293
+ today = Date.parse("Thursday 04 Jun 2015")
294
+
295
+ Timecop.freeze(today) do
296
+ rate_estimates = @carrier.find_rates( location_fixtures[:ottawa],
297
+ location_fixtures[:beverly_hills],
298
+ package_fixtures.values_at(:book, :wii), :test => true)
299
+
300
+ # the above fixture will specify a transit time of 3 days
301
+ # for ground home, sunday and monday are non-biz days
302
+ # so it is delivered on Tuesday
303
+ delivery_date = Date.today + 3 + 2
304
+ assert_equal delivery_date, rate_estimates.rates.first.delivery_date
305
+ end
306
+ end
307
+
308
+ def test_delivery_date_from_ground_home_transit_time_on_saturday
309
+ mock_response = xml_fixture('fedex/raterequest_response_with_ground_home_delivery')
310
+
311
+ @carrier.expects(:commit).returns(mock_response)
312
+
313
+ today = Date.parse("Wed 03 Jun 2015") #Wednesday
314
+
315
+ Timecop.freeze(today) do
316
+ rate_estimates = @carrier.find_rates( location_fixtures[:ottawa],
317
+ location_fixtures[:beverly_hills],
318
+ package_fixtures.values_at(:book, :wii), :test => true)
319
+
320
+ # the above fixture will specify a transit time of 3 days
321
+ # since ground home delivers on Saturday, there is no delay
322
+ delivery_date = Date.today + 3
323
+ assert_equal delivery_date, rate_estimates.rates.first.delivery_date
324
+ end
325
+ end
326
+
327
+ def test_failure_to_parse_invalid_xml_results_in_a_useful_error
328
+ mock_response = xml_fixture('fedex/invalid_fedex_reply')
329
+
330
+ @carrier.expects(:commit).returns(mock_response)
331
+
332
+ assert_raises ReactiveShipping::ResponseContentError do
333
+ @carrier.find_rates(
334
+ location_fixtures[:ottawa],
335
+ location_fixtures[:beverly_hills],
336
+ package_fixtures.values_at(:book, :wii),
337
+ test: true
338
+ )
339
+ end
340
+ end
341
+
342
+ def test_parsing_response_without_notifications
343
+ mock_response = xml_fixture('fedex/reply_without_notifications')
344
+
345
+ @carrier.expects(:commit).returns(mock_response)
346
+
347
+ response = @carrier.find_rates(
348
+ location_fixtures[:ottawa],
349
+ location_fixtures[:beverly_hills],
350
+ package_fixtures.values_at(:book, :wii),
351
+ test: true
352
+ )
353
+
354
+ assert_predicate response, :success?
355
+ end
356
+
357
+ ### find_tracking_info
358
+
359
+ def test_response_transient_failure
360
+ mock_response = xml_fixture('fedex/tracking_response_failure_code_9045')
361
+ @carrier.expects(:commit).returns(mock_response)
362
+
363
+ error = assert_raises(ReactiveShipping::ShipmentNotFound) do
364
+ @carrier.find_tracking_info('123456789013')
365
+ end
366
+
367
+ msg = 'Sorry, we are unable to process your tracking request. Please retry later, or contact Customer Service at 1.800.Go.FedEx(R) 800.463.3339.'
368
+ assert_equal msg, error.message
369
+ end
370
+
371
+ def test_response_with_failure_nested_in_track_details
372
+ mock_response = xml_fixture('fedex/tracking_response_failure_code_9080')
373
+ @carrier.expects(:commit).returns(mock_response)
374
+
375
+ error = assert_raises(ReactiveShipping::ResponseContentError) do
376
+ @carrier.find_tracking_info('123456789013')
377
+ end
378
+
379
+ msg = 'Sorry, we are unable to process your tracking request. Please contact Customer Service at 1.800.Go.FedEx(R) 800.463.3339.'
380
+ assert_equal msg, error.message
381
+ end
382
+
383
+ def test_response_with_failure_nested_in_completed_track_details
384
+ mock_response = xml_fixture('fedex/tracking_response_unable_to_process')
385
+ @carrier.expects(:commit).returns(mock_response)
386
+
387
+ error = assert_raises(ReactiveShipping::ResponseError) do
388
+ @carrier.find_tracking_info('123456789013')
389
+ end
390
+
391
+ msg = 'FAILURE - 6330: Sorry, we are unable to process your tracking request. Please retry later, or contact Customer Service at 1.800.Go.FedEx(R) 800.463.3339.'
392
+ assert_equal msg, error.message
393
+ end
394
+
395
+ def test_tracking_info_for_delivered_with_signature
396
+ mock_response = xml_fixture('fedex/tracking_response_delivered_with_signature')
397
+ @carrier.expects(:commit).returns(mock_response)
398
+
399
+ response = @carrier.find_tracking_info('449044304137821')
400
+ assert_equal '449044304137821', response.tracking_number
401
+ assert_equal 'AVILLALON', response.delivery_signature
402
+ assert response.delivered?
403
+ refute response.exception?
404
+
405
+ assert_equal Date.parse('2013-12-30'), response.ship_time
406
+ assert_nil response.scheduled_delivery_date
407
+ assert_equal Time.parse('2014-01-02T18:23:29Z'), response.actual_delivery_date
408
+
409
+ origin_address = ReactiveShipping::Location.new(
410
+ city: 'JEFFERSONVILLE',
411
+ country: 'US',
412
+ state: 'IN'
413
+ )
414
+ assert_equal origin_address.to_hash, response.origin.to_hash
415
+
416
+ destination_address = ReactiveShipping::Location.new(
417
+ city: 'Miami',
418
+ country: 'US',
419
+ state: 'FL'
420
+ )
421
+ assert_equal destination_address.to_hash, response.destination.to_hash
422
+
423
+ assert_equal 11, response.shipment_events.length
424
+ assert_equal 'Delivered', response.latest_event.name
425
+ assert_equal 'PU', response.shipment_events.first.type_code
426
+ assert_equal 'OC', response.shipment_events.second.type_code
427
+ assert_equal 'AR', response.shipment_events.third.type_code
428
+ end
429
+
430
+ def test_tracking_info_for_delivered_at_door
431
+ mock_response = xml_fixture('fedex/tracking_response_delivered_at_door')
432
+ @carrier.expects(:commit).returns(mock_response)
433
+
434
+ response = @carrier.find_tracking_info('403934084723025')
435
+ assert_equal '403934084723025', response.tracking_number
436
+ assert response.delivered?
437
+ refute response.exception?
438
+ assert_equal 10, response.shipment_events.length
439
+ assert_equal 'PU', response.shipment_events.first.type_code
440
+ assert_equal 'OC', response.shipment_events.second.type_code
441
+ assert_equal 'AR', response.shipment_events.third.type_code
442
+ assert_equal 'Delivered', response.latest_event.name
443
+ assert_equal 'DL', response.latest_event.type_code
444
+ assert_nil response.delivery_signature
445
+ end
446
+
447
+ def test_state_degrades_to_unknown
448
+ mock_response = xml_fixture('fedex/tracking_response_with_blank_state')
449
+ @carrier.expects(:commit).returns(mock_response)
450
+
451
+ response = @carrier.find_tracking_info('798701052354')
452
+
453
+ destination_address = ReactiveShipping::Location.new(
454
+ city: 'SAITAMA',
455
+ country: 'Japan',
456
+ state: 'unknown'
457
+ )
458
+
459
+ assert_equal destination_address.to_hash, response.destination.to_hash
460
+ end
461
+
462
+ def test_tracking_info_for_in_transit
463
+ mock_response = xml_fixture('fedex/tracking_response_in_transit')
464
+ @carrier.expects(:commit).returns(mock_response)
465
+
466
+ response = @carrier.find_tracking_info('123456789012')
467
+ refute response.delivered?
468
+ refute response.exception?
469
+
470
+ assert_equal '123456789012', response.tracking_number
471
+ assert_equal :fedex, response.carrier
472
+ assert_equal 'FedEx', response.carrier_name
473
+ assert_equal :in_transit, response.status
474
+ assert_equal 'IT', response.status_code
475
+ assert_equal "Package available for clearance", response.status_description
476
+ assert_nil response.delivery_signature
477
+
478
+ assert_equal Time.parse('2014-11-17T22:39:00+11:00'), response.ship_time
479
+ assert_nil response.scheduled_delivery_date
480
+ assert_nil response.actual_delivery_date
481
+
482
+ assert_nil response.origin
483
+
484
+ destination_address = ReactiveShipping::Location.new(
485
+ city: 'GRAFTON',
486
+ country: 'AU',
487
+ state: 'ON'
488
+ )
489
+ assert_equal destination_address.to_hash, response.destination.to_hash
490
+
491
+ assert_equal 1, response.shipment_events.length
492
+ assert_equal 'In transit', response.latest_event.name
493
+ assert_equal 'IT', response.latest_event.type_code
494
+ end
495
+
496
+ def test_tracking_info_for_shipment_exception
497
+ mock_response = xml_fixture('fedex/tracking_response_shipment_exception')
498
+ @carrier.expects(:commit).returns(mock_response)
499
+
500
+ response = @carrier.find_tracking_info('957794015041323')
501
+ assert_equal '957794015041323', response.tracking_number
502
+ refute response.delivered?
503
+ assert response.exception?
504
+ assert_equal :exception, response.status
505
+ assert_equal "Unable to deliver", response.status_description
506
+
507
+ assert_equal Date.parse('2014-01-27'), response.ship_time
508
+ assert_nil response.scheduled_delivery_date
509
+ assert_nil response.actual_delivery_date
510
+
511
+ origin_address = ReactiveShipping::Location.new(
512
+ city: 'AUSTIN',
513
+ country: 'US',
514
+ state: 'TX'
515
+ )
516
+ assert_equal origin_address.to_hash, response.origin.to_hash
517
+
518
+ destination_address = ReactiveShipping::Location.new(
519
+ city: 'GOOSE CREEK',
520
+ country: 'US',
521
+ state: 'SC'
522
+ )
523
+ assert_equal destination_address.to_hash, response.destination.to_hash
524
+
525
+ assert_equal 8, response.shipment_events.length
526
+ assert_equal "Shipment exception", response.latest_event.name
527
+ assert_equal "SE", response.latest_event.type_code
528
+ end
529
+
530
+ def test_tracking_info_without_status
531
+ mock_response = xml_fixture('fedex/tracking_response_multiple_results')
532
+ @carrier.expects(:commit).returns(mock_response)
533
+
534
+ error = assert_raises(ReactiveShipping::ResponseError) do
535
+ @carrier.find_tracking_info('123456789012')
536
+ end
537
+
538
+ msg = 'Multiple matches were found. Specify a unqiue identifier: 2456987000~123456789012~FX, 2456979001~123456789012~FX, 2456979000~123456789012~FX'
539
+ assert_equal msg, error.message
540
+ end
541
+
542
+ def test_tracking_info_with_unknown_tracking_number
543
+ mock_response = xml_fixture('fedex/tracking_response_not_found')
544
+ @carrier.expects(:commit).returns(mock_response)
545
+
546
+ error = assert_raises(ReactiveShipping::ShipmentNotFound) do
547
+ @carrier.find_tracking_info('123456789013')
548
+ end
549
+
550
+ msg = 'This tracking number cannot be found. Please check the number or contact the sender.'
551
+ assert_equal msg, error.message
552
+ end
553
+
554
+ def test_tracking_info_with_bad_tracking_number
555
+ mock_response = xml_fixture('fedex/tracking_response_bad_tracking_number')
556
+ @carrier.expects(:commit).returns(mock_response)
557
+
558
+ assert_raises(ReactiveShipping::ResponseError) do
559
+ @carrier.find_tracking_info('abc')
560
+ end
561
+ end
562
+
563
+ def test_tracking_info_with_uncovered_error
564
+ mock_response = xml_fixture('fedex/tracking_response_invalid_tracking_number')
565
+ @carrier.expects(:commit).returns(mock_response)
566
+
567
+ error = assert_raises(ReactiveShipping::ResponseContentError) do
568
+ @carrier.find_tracking_info('123456789013')
569
+ end
570
+
571
+ msg = 'Invalid tracking numbers. Please check the following numbers and resubmit.'
572
+ assert_equal msg, error.message
573
+ end
574
+
575
+ def test_tracking_info_with_empty_status_detail
576
+ mock_response = xml_fixture('fedex/tracking_response_empty_status_detail')
577
+ @carrier.expects(:commit).returns(mock_response)
578
+ response = @carrier.find_tracking_info('123456789012')
579
+
580
+ assert_equal '123456789012', response.tracking_number
581
+ assert_nil response.status_code
582
+ assert_nil response.status
583
+ assert_nil response.status_description
584
+ assert_nil response.delivery_signature
585
+ assert_empty response.shipment_events
586
+ end
587
+
588
+ def test_tracking_info_with_missing_status_code
589
+ mock_response = xml_fixture('fedex/tracking_response_missing_status_code')
590
+ @carrier.expects(:commit).returns(mock_response)
591
+
592
+ response = @carrier.find_tracking_info('123456789012')
593
+ assert_equal '123456789012', response.tracking_number
594
+ assert_nil response.status_code
595
+ assert_nil response.status
596
+ assert_nil response.status_description
597
+ assert_nil response.delivery_signature
598
+ assert_empty response.shipment_events
599
+ end
600
+
601
+ ### create_shipment
602
+
603
+ def test_create_shipment
604
+ confirm_response = xml_fixture('fedex/create_shipment_response')
605
+ @carrier.stubs(:commit).returns(confirm_response)
606
+
607
+ response = @carrier.create_shipment(
608
+ location_fixtures[:beverly_hills],
609
+ location_fixtures[:new_york],
610
+ package_fixtures.values_at(:chocolate_stuff),
611
+ :test => true
612
+ )
613
+
614
+ # These assertions are to check that the xml fixture is extracted properly.
615
+ assert_equal 1, response.labels.count
616
+ assert_equal response.labels.first.tracking_number, "794637052920"
617
+ assert_equal response.labels.first.img_data.size, 8286
618
+ end
619
+
620
+ def test_create_shipment_signature_option
621
+ packages = package_fixtures.values_at(:chocolate_stuff)
622
+ packages.each {|p| p.options[:signature_option] = :indirect }
623
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
624
+ location_fixtures[:beverly_hills],
625
+ location_fixtures[:annapolis],
626
+ packages,
627
+ :test => true))
628
+ assert_equal result.search('SpecialServicesRequested/SpecialServiceTypes').text, "SIGNATURE_OPTION"
629
+ assert_equal result.search('SpecialServicesRequested/SignatureOptionDetail').text.strip, "INDIRECT"
630
+ end
631
+
632
+ def test_create_shipment_reference
633
+ packages = package_fixtures.values_at(:wii)
634
+ packages.each do |p|
635
+ p.options[:reference_numbers] = [
636
+ { :value => "FOO-123"},
637
+ { :type => "INVOICE_NUMBER", :value => "BAR-456" }
638
+ ]
639
+ end
640
+
641
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
642
+ location_fixtures[:beverly_hills],
643
+ location_fixtures[:annapolis],
644
+ packages,
645
+ :test => true))
646
+
647
+ assert_equal result.search('RequestedPackageLineItems/CustomerReferences[first()]/Value').text, "FOO-123"
648
+ assert_equal result.search('RequestedPackageLineItems/CustomerReferences[first()]/CustomerReferenceType').text, "CUSTOMER_REFERENCE"
649
+ assert_equal result.search('RequestedPackageLineItems/CustomerReferences[last()]/Value').text, "BAR-456"
650
+ assert_equal result.search('RequestedPackageLineItems/CustomerReferences[last()]/CustomerReferenceType').text, "INVOICE_NUMBER"
651
+ end
652
+
653
+ def test_create_shipment_label_format_option
654
+ packages = package_fixtures.values_at(:chocolate_stuff)
655
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
656
+ location_fixtures[:beverly_hills],
657
+ location_fixtures[:annapolis],
658
+ packages,
659
+ :label_format => 'ZPLII',
660
+ :test => true))
661
+ assert_equal result.search('RequestedShipment/LabelSpecification/ImageType').text, "ZPLII"
662
+ end
663
+
664
+ def test_create_shipment_default_label_stock_type
665
+ packages = package_fixtures.values_at(:wii)
666
+
667
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
668
+ location_fixtures[:beverly_hills],
669
+ location_fixtures[:annapolis],
670
+ packages,
671
+ :test => true))
672
+
673
+ assert_equal result.search('RequestedShipment/LabelSpecification/LabelStockType').text, FedEx::DEFAULT_LABEL_STOCK_TYPE
674
+ end
675
+
676
+ def test_create_shipment_label_stock_type
677
+ label_stock_type = 'PAPER_4X6'
678
+ packages = package_fixtures.values_at(:wii)
679
+
680
+ result = Nokogiri::XML(@carrier.send(:build_shipment_request,
681
+ location_fixtures[:beverly_hills],
682
+ location_fixtures[:annapolis],
683
+ packages,
684
+ :test => true,
685
+ :label_stock_type => label_stock_type))
686
+
687
+ assert_equal result.search('RequestedShipment/LabelSpecification/LabelStockType').text, label_stock_type
688
+ end
689
+
690
+ def test_maximum_address_field_length
691
+ assert_equal 35, @carrier.maximum_address_field_length
692
+ end
693
+ end