simple_shipping 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.metrics +6 -0
  4. data/.rspec +4 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.simplecov +43 -0
  8. data/Gemfile +26 -0
  9. data/Gemfile.lock +201 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.markdown +207 -0
  12. data/Rakefile +68 -0
  13. data/VERSION +1 -0
  14. data/coverage/.resultset.json +1579 -0
  15. data/lib/colorized_text.rb +34 -0
  16. data/lib/simple_shipping.rb +27 -0
  17. data/lib/simple_shipping/abstract.rb +11 -0
  18. data/lib/simple_shipping/abstract/builder.rb +47 -0
  19. data/lib/simple_shipping/abstract/client.rb +111 -0
  20. data/lib/simple_shipping/abstract/model.rb +40 -0
  21. data/lib/simple_shipping/abstract/request.rb +27 -0
  22. data/lib/simple_shipping/abstract/response.rb +26 -0
  23. data/lib/simple_shipping/address.rb +22 -0
  24. data/lib/simple_shipping/contact.rb +24 -0
  25. data/lib/simple_shipping/demo.rb +9 -0
  26. data/lib/simple_shipping/demo/base.rb +71 -0
  27. data/lib/simple_shipping/demo/fedex.rb +46 -0
  28. data/lib/simple_shipping/demo/ups.rb +68 -0
  29. data/lib/simple_shipping/exceptions.rb +40 -0
  30. data/lib/simple_shipping/fedex.rb +14 -0
  31. data/lib/simple_shipping/fedex/client.rb +41 -0
  32. data/lib/simple_shipping/fedex/package_builder.rb +24 -0
  33. data/lib/simple_shipping/fedex/party_builder.rb +50 -0
  34. data/lib/simple_shipping/fedex/request.rb +54 -0
  35. data/lib/simple_shipping/fedex/response.rb +5 -0
  36. data/lib/simple_shipping/fedex/shipment_builder.rb +123 -0
  37. data/lib/simple_shipping/fedex/shipment_request.rb +14 -0
  38. data/lib/simple_shipping/fedex/shipment_response.rb +12 -0
  39. data/lib/simple_shipping/package.rb +43 -0
  40. data/lib/simple_shipping/party.rb +21 -0
  41. data/lib/simple_shipping/shipment.rb +42 -0
  42. data/lib/simple_shipping/ups.rb +24 -0
  43. data/lib/simple_shipping/ups/client.rb +46 -0
  44. data/lib/simple_shipping/ups/package_builder.rb +101 -0
  45. data/lib/simple_shipping/ups/party_builder.rb +38 -0
  46. data/lib/simple_shipping/ups/request.rb +27 -0
  47. data/lib/simple_shipping/ups/response.rb +63 -0
  48. data/lib/simple_shipping/ups/ship_accept_request.rb +21 -0
  49. data/lib/simple_shipping/ups/ship_accept_response.rb +6 -0
  50. data/lib/simple_shipping/ups/ship_client.rb +70 -0
  51. data/lib/simple_shipping/ups/ship_confirm_request.rb +22 -0
  52. data/lib/simple_shipping/ups/ship_confirm_response.rb +6 -0
  53. data/lib/simple_shipping/ups/shipment_builder.rb +66 -0
  54. data/lib/simple_shipping/ups/shipment_request.rb +22 -0
  55. data/lib/simple_shipping/ups/shipment_response.rb +6 -0
  56. data/lib/simple_shipping/ups/void_client.rb +50 -0
  57. data/lib/simple_shipping/ups/void_request.rb +42 -0
  58. data/lib/simple_shipping/ups/void_response.rb +6 -0
  59. data/lib/tasks/demo.rake +58 -0
  60. data/script/ups_certification.rb +140 -0
  61. data/simple_shipping.gemspec +168 -0
  62. data/spec/fixtures/fedex_shipment_request.soap.xml.erb +85 -0
  63. data/spec/fixtures/fedex_shipment_response.soap.xml.erb +182 -0
  64. data/spec/fixtures/ups_shipment_request.soap.xml.erb +88 -0
  65. data/spec/fixtures/ups_shipment_response.soap.xml.erb +58 -0
  66. data/spec/fixtures/ups_shipment_response_with_faked_label_data.soap.xml.erb +54 -0
  67. data/spec/fixtures/ups_void_request.soap.xml.erb +29 -0
  68. data/spec/fixtures/ups_void_response.soap.xml.erb +21 -0
  69. data/spec/lib/simple_shipping/address_spec.rb +19 -0
  70. data/spec/lib/simple_shipping/contact_spec.rb +28 -0
  71. data/spec/lib/simple_shipping/exceptions_spec.rb +58 -0
  72. data/spec/lib/simple_shipping/fedex/package_builder_spec.rb +5 -0
  73. data/spec/lib/simple_shipping/fedex/party_builder_spec.rb +5 -0
  74. data/spec/lib/simple_shipping/fedex/response/shipment_reponse_spec.rb +5 -0
  75. data/spec/lib/simple_shipping/fedex/response_spec.rb +5 -0
  76. data/spec/lib/simple_shipping/fedex/shipment_builder_spec.rb +23 -0
  77. data/spec/lib/simple_shipping/package_spec.rb +32 -0
  78. data/spec/lib/simple_shipping/party_spec.rb +18 -0
  79. data/spec/lib/simple_shipping/shipment_spec.rb +35 -0
  80. data/spec/lib/simple_shipping/ups/package_builder_spec.rb +26 -0
  81. data/spec/lib/simple_shipping/ups/party_builder_spec.rb +47 -0
  82. data/spec/lib/simple_shipping/ups/response/shipment_response_spec.rb +5 -0
  83. data/spec/lib/simple_shipping/ups/response_spec.rb +33 -0
  84. data/spec/lib/simple_shipping/ups/shipment_builder_spec.rb +19 -0
  85. data/spec/requests/fedex_spec.rb +47 -0
  86. data/spec/requests/ups_spec.rb +75 -0
  87. data/spec/spec_helper.rb +47 -0
  88. data/spec/support/custom_matchers/basic_matcher.rb +13 -0
  89. data/spec/support/custom_matchers/have_attribute_matcher.rb +22 -0
  90. data/spec/support/custom_matchers/have_default_value_matcher.rb +26 -0
  91. data/spec/support/custom_matchers/have_errors_on_matcher.rb +23 -0
  92. data/spec/support/custom_matchers/validate_inclusion_of_matcher.rb +37 -0
  93. data/spec/support/custom_matchers/validate_presence_of_matcher.rb +24 -0
  94. data/spec/support/custom_matchers/validate_submodel_matcher.rb +44 -0
  95. data/spec/support/shared_behaviours/builders_behaviour.rb +9 -0
  96. data/spec/support/shared_behaviours/responses_behaviour.rb +10 -0
  97. data/tmp/metric_fu/_data/20131210.yml +9964 -0
  98. data/wsdl/fedex/ship_service_v10.wsdl +5566 -0
  99. data/wsdl/ups/Ship.wsdl +120 -0
  100. data/wsdl/ups/Void.wsdl +58 -0
  101. metadata +308 -0
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleShipping::Ups
4
+ describe PartyBuilder do
5
+ it_should_behave_like "builders"
6
+
7
+ it 'validate account number if party is shipper' do
8
+ shipper = SimpleShipping::Party.new
9
+
10
+ shipper.stub!(:valid? => true)
11
+ lambda {
12
+ PartyBuilder.build(shipper, :shipper => true)
13
+ }.should raise_error SimpleShipping::ValidationError
14
+
15
+ shipper.stub!(:account_number => '123')
16
+ lambda {
17
+ PartyBuilder.build(shipper, :shipper => true)
18
+ }.should_not raise_error SimpleShipping::ValidationError
19
+ end
20
+ end
21
+
22
+ describe "build" do
23
+ it "should include all addresses" do
24
+ contact = SimpleShipping::Contact.new(:phone_number => "5555555555", :person_name => "Frank")
25
+ address = SimpleShipping::Address.new(:country_code => "US", :state_code => "IL", :city => "Chicago", :postal_code => "60622")
26
+ address.street_line = "foo"
27
+ address.street_line_2 = "bar"
28
+ address.street_line_3 = "baz"
29
+
30
+ shipper = SimpleShipping::Party.new(:contact => contact, :address => address)
31
+ party = PartyBuilder.build(shipper)
32
+ party['Address']['AddressLine'].should have(3).addresses
33
+ end
34
+
35
+ it "should exclude nil addresses" do
36
+ contact = SimpleShipping::Contact.new(:phone_number => "5555555555", :person_name => "Frank")
37
+ address = SimpleShipping::Address.new(:country_code => "US", :state_code => "IL", :city => "Chicago", :postal_code => "60622")
38
+ address.street_line = "foo"
39
+ address.street_line_2 = nil
40
+ address.street_line_3 = "baz"
41
+
42
+ shipper = SimpleShipping::Party.new(:contact => contact, :address => address)
43
+ party = PartyBuilder.build(shipper)
44
+ party['Address']['AddressLine'].should have(2).addresses
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleShipping::Ups::ShipmentResponse do
4
+ it_should_behave_like "responses_with_labels"
5
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleShipping::Ups::Response do
4
+ it_should_behave_like "responses"
5
+
6
+ context "when success response is received" do
7
+ subject { response }
8
+
9
+ let(:response_fixture) { fixture("ups_shipment_response") }
10
+ let(:http_response) { ::HTTPI::Response.new(200, {}, response_fixture) }
11
+ let(:savon_response) do
12
+ ::Savon::Response.new(
13
+ http_response,
14
+ {
15
+ strip_namespaces: true,
16
+ convert_response_tags_to: ->(tag) { tag.snakecase.to_sym }
17
+ },
18
+ {}
19
+ )
20
+ end
21
+ let(:response) { ::SimpleShipping::Ups::ShipmentResponse.new(savon_response) }
22
+
23
+ its(:shipment_identification_number) { should eq("1Z35679R0294268838") }
24
+ its(:tracking_number) { should eq("1Z35679R0294268838") }
25
+
26
+ describe "label image" do
27
+ let(:response_fixture) { fixture("ups_shipment_response_with_faked_label_data") }
28
+
29
+ # HTML_IMAGE_DATA is the Base64 decoded version of SFRNTF9JTUFHRV9EQVRB
30
+ its(:label_html) { should eq("HTML_IMAGE_DATA") }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleShipping::Ups::ShipmentBuilder do
4
+ it_should_behave_like "builders"
5
+
6
+ describe 'validation' do
7
+ it 'validates inclusion of service_types' do
8
+ shipment = SimpleShipping::Shipment.new
9
+ shipment.stub!(:valid? => true)
10
+
11
+ lambda {
12
+ SimpleShipping::Ups::ShipmentBuilder.build(shipment, :service_type => :ground)
13
+ }.should_not raise_error SimpleShipping::ValidationError
14
+ lambda {
15
+ SimpleShipping::Ups::ShipmentBuilder.build(shipment, :service_type => :bad)
16
+ }.should raise_error SimpleShipping::ValidationError
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe "FedEx integration test" do
4
+ before { Timecop.freeze }
5
+ after { Timecop.return }
6
+
7
+ #let(:credentials) do
8
+ # YAML.load_file(SimpleShipping::DEMO_CREDENTIALS_FILE)['fedex'].symbolize_keys!
9
+ #end
10
+
11
+ let(:credentials) {{
12
+ :key => 'fedex key',
13
+ :password => 'secret word',
14
+ :account_number => '101010101',
15
+ :meter_number => '202020202'
16
+ }}
17
+
18
+ let(:demo) do
19
+ options = credentials.merge(:live => false)
20
+ SimpleShipping::Demo::Fedex.new(options)
21
+ end
22
+
23
+ context "#shipment_request" do
24
+ it "builds correct SOAP request envelope" do
25
+ req_matcher = lambda do |req|
26
+ expected = Nokogiri::XML(fixture(:fedex_shipment_request, credentials))
27
+ actual = Nokogiri::XML(req.body)
28
+
29
+ actual.root.should be_equivalent_to(expected.root).respecting_element_order
30
+
31
+ req.headers['Soapaction'].should == %{"processShipment"}
32
+
33
+ true
34
+ end
35
+
36
+ req = stub_http_request(:post, demo.fedex_client.class.testing_address).
37
+ with(&req_matcher).
38
+ to_return(:body => fixture(:fedex_shipment_response), :status => 200, :headers => {})
39
+
40
+ resp = demo.shipment_request
41
+
42
+ resp.label_image_base64.should be_present
43
+
44
+ req.should have_been_made
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe "UPS test" do
4
+ let(:credentials) {{
5
+ :username => "Joe13",
6
+ :password => "JoesSecretWord",
7
+ :account_number => "1010101",
8
+ :access_license_number => "Joe's license"
9
+ }}
10
+
11
+ let(:demo) do
12
+ options = credentials.merge!(:live => false)
13
+ SimpleShipping::Demo::Ups.new(options)
14
+ end
15
+
16
+ let(:erb_request_vars) do
17
+ credentials.merge(:shipment_identification_number => demo.shipment_identification_number)
18
+ end
19
+
20
+ describe "ship_client" do
21
+ describe "#shipment_request" do
22
+ it "builds correct SOAP request envelope" do
23
+ req_matcher = lambda do |req|
24
+ expected = Nokogiri::XML(fixture(:ups_shipment_request, erb_request_vars))
25
+ actual = Nokogiri::XML(req.body)
26
+
27
+ actual.root.should be_equivalent_to(expected.root)
28
+ #actual.root.to_s.should == expected.root.to_s
29
+
30
+ true
31
+ end
32
+
33
+ req = stub_http_request(:post, demo.ship_client.class.testing_address).
34
+ with(&req_matcher).
35
+ to_return(:body => fixture(:ups_shipment_response), :status => 200, :headers => {})
36
+
37
+ resp = demo.shipment_request(
38
+
39
+ )
40
+
41
+
42
+ resp.label_image_base64.should be_present
43
+
44
+ req.should have_been_made
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "void client" do
50
+ describe "#void_request" do
51
+ it "builds correct SOAP request envelope" do
52
+ req_matcher = lambda do |req|
53
+ expected = Nokogiri::XML(fixture(:ups_void_request, erb_request_vars))
54
+ actual = Nokogiri::XML(req.body)
55
+
56
+ actual.root.should be_equivalent_to(expected.root)
57
+ #actual.root.to_s.should == expected.root.to_s
58
+
59
+ true
60
+ end
61
+
62
+ req = stub_http_request(:post, demo.void_client.class.testing_address).
63
+ with(&req_matcher).
64
+ to_return(:body => fixture(:ups_void_response), :status => 200, :headers => {})
65
+
66
+ expect{ demo.void_request }.to(
67
+ # NOTE: Exception message is important! This is how we can find out whether SOAP request was made correct -- aignatyev 20130204
68
+ raise_exception(SimpleShipping::RequestError, /No shipment found within the allowed void period/)
69
+ )
70
+
71
+ req.should have_been_made
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'simple_shipping'
5
+
6
+ require "simplecov"
7
+ require "simplecov-rcov-text"
8
+
9
+ SimpleCov.formatter = SimpleCov::Formatter::RcovTextFormatter
10
+ SimpleCov.start do
11
+ add_filter "/spec/"
12
+ end
13
+
14
+
15
+ require "savon/mock/spec_helper"
16
+ require 'webmock/rspec'
17
+ require "timecop"
18
+ require "erubis"
19
+ require 'equivalent-xml'
20
+
21
+ # Requires supporting files with custom matchers and macros, etc,
22
+ # in ./support/ and its subdirectories.
23
+ require File.expand_path("../support/custom_matchers/basic_matcher", __FILE__)
24
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
25
+
26
+ RSpec.configure do |config|
27
+ config.include SimpleShipping::CustomMatchers
28
+ config.include Savon::SpecHelper
29
+ config.after(:each) { savon.unmock! }
30
+
31
+ config.before(:each) do
32
+ # We are going to enable webmock in some spec on-demand.
33
+ # This is the reason why this config directive is placed in before(:each) hook -- aignatyev 20130204
34
+ WebMock.disable_net_connect!(:allow_localhost => true)
35
+ end
36
+
37
+ def fixtures_dir
38
+ Pathname.new(File.expand_path('../fixtures', __FILE__))
39
+ end
40
+
41
+ def fixture(name, vars = {})
42
+ path = fixtures_dir.join(name.to_s + ".soap.xml.erb")
43
+ template = File.open(path).read
44
+
45
+ Erubis::Eruby.new(template).result(vars)
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class BasicMatcher
4
+ def failure_message
5
+ "expected to #{description}"
6
+ end
7
+
8
+ def negative_failure_message
9
+ "expected to not #{description}"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class HaveAttributeMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ model.respond_to?(@attribute) &&
10
+ model.respond_to?("#{@attribute}=")
11
+ end
12
+
13
+ def description
14
+ "have attribute #{@attribute.inspect}"
15
+ end
16
+ end
17
+
18
+ def have_attribute(attr_name)
19
+ HaveAttributeMatcher.new(attr_name)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class HaveDefaultValueMatcher < BasicMatcher
4
+ def initialize(value)
5
+ @value = value
6
+ end
7
+
8
+ def matches?(model)
9
+ model.send(@attribute) == @value
10
+ end
11
+
12
+ def for_attribute(attribute)
13
+ @attribute = attribute.to_sym
14
+ self
15
+ end
16
+
17
+ def description
18
+ "have default value #{@value.inspect} for attribute #{@attribute.inspect}"
19
+ end
20
+ end
21
+
22
+ def have_default_value(value)
23
+ HaveDefaultValueMatcher.new(value)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class HaveErrorsOnMatcher < BasicMatcher
4
+
5
+ def initialize(attribute)
6
+ @attribute = attribute.to_sym
7
+ end
8
+
9
+ def matches?(model)
10
+ model.valid?
11
+ !!model.errors.messages[@attribute]
12
+ end
13
+
14
+ def description
15
+ "have errors on #{@attribute.inspect}"
16
+ end
17
+ end
18
+
19
+ def have_errors_on(attr_name)
20
+ HaveErrorsOnMatcher.new(attr_name)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class ValidateInclusionOfMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ @enumeration.each do |value|
10
+ model.send("#{@attribute}=", value)
11
+ return false if has_error?(model)
12
+ end
13
+ model.send("#{@attribute}=", "ANOTHER_VALUE_#{rand}")
14
+ has_error?(model)
15
+ end
16
+
17
+ def in(*enumeration)
18
+ @enumeration = enumeration
19
+ self
20
+ end
21
+
22
+ def description
23
+ "validate inclusion of #{@attribute.inspect} in #{@enumeration.inspect}"
24
+ end
25
+
26
+ def has_error?(model)
27
+ model.valid?
28
+ model.errors.messages[@attribute] and
29
+ model.errors.messages[@attribute].include?("is not included in the list")
30
+ end
31
+ end
32
+
33
+ def validate_inclusion_of(attr_name)
34
+ ValidateInclusionOfMatcher.new(attr_name)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class ValidatePresenceOfMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ model.send("#{@attribute}=", nil)
10
+ model.valid?
11
+ model.errors.messages[@attribute] and
12
+ model.errors.messages[@attribute].include? "can't be blank"
13
+ end
14
+
15
+ def description
16
+ "validate presence of #{@attribute.inspect}"
17
+ end
18
+ end
19
+
20
+ def validate_presence_of(attr_name)
21
+ ValidatePresenceOfMatcher.new(attr_name)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleShipping
2
+ module CustomMatchers
3
+ class ValidateSubmodelMatcher < BasicMatcher
4
+ def initialize(attribute)
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def matches?(model)
9
+ @model = model
10
+
11
+ @model.send("#{@attribute}=", Class.new.new)
12
+ return false unless has_error?
13
+
14
+ submodel = @class.new
15
+ submodel.stub!(:valid? => false)
16
+ @model.send("#{@attribute}=", submodel)
17
+ return false unless has_error?
18
+
19
+ submodel.stub!(:valid? => true)
20
+ @model.send("#{@attribute}=", submodel)
21
+ !has_error?
22
+ end
23
+
24
+ def as(klass)
25
+ @class = klass
26
+ self
27
+ end
28
+
29
+ def description
30
+ "validate submodel #{@attribute.inspect} as instance of #{@class}"
31
+ end
32
+
33
+ def has_error?
34
+ @model.valid?
35
+ !!@model.errors.messages[@attribute]
36
+ end
37
+ private :has_error?
38
+ end
39
+
40
+ def validate_submodel(attr_name)
41
+ ValidateSubmodelMatcher.new(attr_name)
42
+ end
43
+ end
44
+ end