solidus_easypost 1.0.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +41 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.github_changelog_generator +2 -0
  6. data/.gitignore +21 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +7 -0
  9. data/.rubocop_todo.yml +36 -0
  10. data/CHANGELOG.md +113 -0
  11. data/Gemfile +33 -0
  12. data/LICENSE +26 -0
  13. data/README.md +194 -0
  14. data/Rakefile +6 -0
  15. data/app/controllers/spree/admin/postage_labels_controller.rb +20 -0
  16. data/app/decorators/models/solidus_easypost/spree/carton_decorator.rb +38 -0
  17. data/app/decorators/models/solidus_easypost/spree/shipment_decorator.rb +47 -0
  18. data/app/decorators/models/solidus_easypost/spree/shipping_rate_decorator.rb +13 -0
  19. data/app/models/solidus_easypost/parcel_dimension.rb +34 -0
  20. data/app/models/solidus_easypost/return_authorization.rb +27 -0
  21. data/app/overrides/spree/admin/orders/_shipment/add_postage_label.html.erb.deface +10 -0
  22. data/bin/console +17 -0
  23. data/bin/rails +7 -0
  24. data/bin/rails-engine +13 -0
  25. data/bin/rails-sandbox +16 -0
  26. data/bin/rake +7 -0
  27. data/bin/sandbox +86 -0
  28. data/bin/setup +8 -0
  29. data/config/initializers/webhooks.rb +7 -0
  30. data/config/locales/en.yml +8 -0
  31. data/config/routes.rb +9 -0
  32. data/db/migrate/20140515024440_add_easy_post_fields_to_shipping_rate.rb +9 -0
  33. data/db/migrate/20201025110912_add_tracker_id_to_cartons.rb +6 -0
  34. data/lib/generators/solidus_easypost/install/install_generator.rb +28 -0
  35. data/lib/generators/solidus_easypost/install/templates/initializer.rb +26 -0
  36. data/lib/solidus_easypost/address_builder.rb +45 -0
  37. data/lib/solidus_easypost/calculator/base_dimension_calculator.rb +28 -0
  38. data/lib/solidus_easypost/calculator/weight_dimension_calculator.rb +22 -0
  39. data/lib/solidus_easypost/configuration.rb +34 -0
  40. data/lib/solidus_easypost/engine.rb +19 -0
  41. data/lib/solidus_easypost/errors/unknown_partial_resource_error.rb +11 -0
  42. data/lib/solidus_easypost/estimator.rb +37 -0
  43. data/lib/solidus_easypost/parcel_builder.rb +27 -0
  44. data/lib/solidus_easypost/shipment_builder.rb +32 -0
  45. data/lib/solidus_easypost/shipping_method_selector.rb +17 -0
  46. data/lib/solidus_easypost/shipping_rate_calculator.rb +9 -0
  47. data/lib/solidus_easypost/testing_support/factories/address_factory.rb +10 -0
  48. data/lib/solidus_easypost/testing_support/factories/product_factory.rb +7 -0
  49. data/lib/solidus_easypost/testing_support/factories/shipment_factory.rb +30 -0
  50. data/lib/solidus_easypost/testing_support/factories/shipping_method_factory.rb +8 -0
  51. data/lib/solidus_easypost/testing_support/factories/stock_location_factory.rb +10 -0
  52. data/lib/solidus_easypost/testing_support/factories/variant_factory.rb +7 -0
  53. data/lib/solidus_easypost/testing_support/factories.rb +3 -0
  54. data/lib/solidus_easypost/tracker_webhook_handler.rb +14 -0
  55. data/lib/solidus_easypost/version.rb +5 -0
  56. data/lib/solidus_easypost.rb +32 -0
  57. data/solidus_easypost.gemspec +41 -0
  58. data/spec/cassettes/address_builder/from_address.yml +65 -0
  59. data/spec/cassettes/address_builder/from_stock_location.yml +65 -0
  60. data/spec/cassettes/estimator.yml +269 -0
  61. data/spec/cassettes/integration/checkout.yml +377 -0
  62. data/spec/cassettes/integration/order_shipping/with_purchase_labels.yml +373 -0
  63. data/spec/cassettes/integration/order_shipping/without_purchase_labels.yml +251 -0
  64. data/spec/cassettes/parcel_builder/from_package.yml +63 -0
  65. data/spec/cassettes/parcel_builder/from_return_authorization.yml +63 -0
  66. data/spec/cassettes/postage_labels/show.yml +72 -0
  67. data/spec/cassettes/return_authorization.yml +310 -0
  68. data/spec/cassettes/shipment_builder/from_package.yml +251 -0
  69. data/spec/cassettes/shipment_builder/from_return_authorization.yml +251 -0
  70. data/spec/cassettes/shipment_builder/from_shipment.yml +250 -0
  71. data/spec/controllers/spree/admin/postage_labels_controller_spec.rb +18 -0
  72. data/spec/features/admin/postage_labels_spec.rb +18 -0
  73. data/spec/integration/checkout_spec.rb +45 -0
  74. data/spec/integration/order_shipping_spec.rb +33 -0
  75. data/spec/models/solidus_easypost/parcel_dimension_spec.rb +80 -0
  76. data/spec/models/solidus_easypost/return_authorization_spec.rb +17 -0
  77. data/spec/models/spree/carton_spec.rb +57 -0
  78. data/spec/models/spree/shipment_spec.rb +69 -0
  79. data/spec/models/spree/shipping_rate_spec.rb +23 -0
  80. data/spec/solidus_easypost/address_builder_spec.rb +17 -0
  81. data/spec/solidus_easypost/calculator/weight_dimension_calculator_spec.rb +39 -0
  82. data/spec/solidus_easypost/estimator_spec.rb +52 -0
  83. data/spec/solidus_easypost/parcel_builder_spec.rb +113 -0
  84. data/spec/solidus_easypost/shipment_builder_spec.rb +28 -0
  85. data/spec/solidus_easypost/shipping_method_selector_spec.rb +33 -0
  86. data/spec/solidus_easypost/shipping_rate_calculator_spec.rb +12 -0
  87. data/spec/solidus_easypost/tracker_webhook_handler_spec.rb +58 -0
  88. data/spec/solidus_easypost_spec.rb +19 -0
  89. data/spec/spec_helper.rb +31 -0
  90. data/spec/support/easypost.rb +5 -0
  91. data/spec/support/factory_bot.rb +3 -0
  92. data/spec/support/helpers/api_stubs.rb +40 -0
  93. data/spec/support/helpers/configuration.rb +28 -0
  94. data/spec/support/helpers/shipping_methods.rb +26 -0
  95. data/spec/support/solidus.rb +1 -0
  96. data/spec/support/vcr.rb +12 -0
  97. metadata +170 -142
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SolidusEasypost::ParcelDimension do
6
+ describe '.new' do
7
+ subject(:instance) { described_class.new(params) }
8
+
9
+ let(:params) do
10
+ { height: 3 }
11
+ end
12
+
13
+ context 'when the weight is not passed' do
14
+ let(:params) do
15
+ { height: 3 }
16
+ end
17
+
18
+ it 'raises an argument error exception' do
19
+ expect {
20
+ instance
21
+ }.to raise_error(ArgumentError)
22
+ end
23
+ end
24
+
25
+ context 'when the passed weight is zero' do
26
+ let(:params) do
27
+ { height: 3, weight: 0 }
28
+ end
29
+
30
+ it 'raises an argument error exception' do
31
+ expect {
32
+ instance
33
+ }.to raise_error(ArgumentError)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#to_h' do
39
+ subject(:instance_hash) { described_class.new(params).to_h }
40
+
41
+ context 'when the params contains zero values' do
42
+ let(:params) do
43
+ { weight: 3, width: 0, height: 0, unknown: -1 }
44
+ end
45
+
46
+ it 'returns the correct parcel dimension hash' do
47
+ expect(instance_hash).to eq({
48
+ weight: 3
49
+ })
50
+ end
51
+ end
52
+
53
+ context 'when the params contains the wrong params' do
54
+ let(:params) do
55
+ { weight: 3, unknown: -1, another: 11 }
56
+ end
57
+
58
+ it 'returns the correct parcel dimension hash' do
59
+ expect(instance_hash).to eq({
60
+ weight: 3
61
+ })
62
+ end
63
+ end
64
+
65
+ context 'when the params contains the correct params' do
66
+ let(:params) do
67
+ { weight: 3, width: 1, depth: 6, height: 11 }
68
+ end
69
+
70
+ it 'returns the correct parcel dimension hash' do
71
+ expect(instance_hash).to eq({
72
+ height: 11,
73
+ weight: 3,
74
+ width: 1,
75
+ length: 6,
76
+ })
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SolidusEasypost::ReturnAuthorization, vcr: { cassette_name: 'return_authorization' } do
6
+ describe '#return_label' do
7
+ it 'returns a valid return label' do
8
+ return_authorization = create(:return_item).return_authorization
9
+
10
+ easypost_return_authorization = described_class.new(return_authorization)
11
+ selected_easypost_rate = easypost_return_authorization.easypost_shipment.rates.first
12
+ return_label = easypost_return_authorization.return_label(selected_easypost_rate)
13
+
14
+ expect(return_label).to have_attributes(object: 'PostageLabel')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ RSpec.describe Spree::Carton do
2
+ describe '.create' do
3
+ context 'when track_all_cartons is true' do
4
+ it 'tracks the package automatically' do
5
+ stub_easypost_config(track_all_cartons: true)
6
+ tracker = instance_double(EasyPost::Tracker, id: 'trk_test')
7
+ allow(EasyPost::Tracker).to receive(:create).and_return(tracker)
8
+
9
+ carton = create(:carton)
10
+
11
+ expect(carton.easy_post_tracker_id).to eq('trk_test')
12
+ end
13
+ end
14
+
15
+ context 'when track_all_cartons is false' do
16
+ it 'does not track all packages automatically' do
17
+ stub_easypost_config(track_all_cartons: false)
18
+
19
+ carton = create(:carton)
20
+
21
+ expect(carton.easy_post_tracker_id).to eq(nil)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#easypost_tracker' do
27
+ context 'when a tracker was already created' do
28
+ it 'returns the existing tracker' do
29
+ carton = create(:carton, easy_post_tracker_id: 'trk_test')
30
+ tracker = instance_double(EasyPost::Tracker)
31
+ allow(EasyPost::Tracker).to receive(:retrieve).with('trk_test').and_return(tracker)
32
+
33
+ expect(carton.easypost_tracker).to eq(tracker)
34
+ end
35
+ end
36
+
37
+ context 'when a tracker was not created' do
38
+ it 'creates a new tracker' do
39
+ carton = create(:carton, tracking: 'TESTTRACKING', shipping_method: create(:shipping_method, carrier: 'FedEx'))
40
+ tracker = instance_double(EasyPost::Tracker, id: 'trk_test')
41
+ allow(EasyPost::Tracker).to receive(:create).with(tracking_code: 'TESTTRACKING', carrier: 'FedEx').and_return(tracker)
42
+
43
+ expect(carton.easypost_tracker).to eq(tracker)
44
+ end
45
+
46
+ it "sets the tracker's ID on the carton" do
47
+ carton = create(:carton)
48
+ tracker = instance_double(EasyPost::Tracker, id: 'trk_test')
49
+ allow(EasyPost::Tracker).to receive(:create).and_return(tracker)
50
+
51
+ carton.easypost_tracker
52
+
53
+ expect(carton.easy_post_tracker_id).to eq('trk_test')
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Spree::Shipment do
6
+ describe '#easypost_shipment' do
7
+ context 'when no shipping rate was selected' do
8
+ it 'returns nil' do
9
+ shipment = create(:shipment)
10
+
11
+ expect(shipment.easypost_shipment).to be_nil
12
+ end
13
+ end
14
+
15
+ context 'when a shipping rate was selected' do
16
+ it 'returns the shipment associated with the shipping rate' do
17
+ easypost_shipment = stub_easypost_shipment
18
+ shipment = create(
19
+ :shipment,
20
+ :with_easypost,
21
+ easypost_shipment_id: easypost_shipment.id,
22
+ )
23
+
24
+ expect(shipment.easypost_shipment).to eq(easypost_shipment)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '#ship!' do
30
+ context 'when purchase_labels is true' do
31
+ it 'buys the selected rate' do
32
+ stub_easypost_config(purchase_labels: true)
33
+ easypost_shipment = stub_easypost_shipment
34
+ selected_easypost_rate = easypost_shipment.rates.first
35
+
36
+ shipment = create(
37
+ :shipment,
38
+ :with_easypost,
39
+ state: 'ready',
40
+ easypost_shipment_id: easypost_shipment.id,
41
+ easypost_rate_id: selected_easypost_rate.id,
42
+ )
43
+ shipment.ship!
44
+
45
+ expect(easypost_shipment).to have_received(:buy).with(selected_easypost_rate)
46
+ end
47
+ end
48
+
49
+ context 'when purchase_labels is false' do
50
+ it 'does not buy rates automatically' do
51
+ stub_easypost_config(purchase_labels: false)
52
+ easypost_shipment = stub_easypost_shipment
53
+ selected_easypost_rate = easypost_shipment.rates.first
54
+
55
+ shipment = create(
56
+ :shipment,
57
+ :with_easypost,
58
+ state: 'ready',
59
+ easypost_shipment_id: easypost_shipment.id,
60
+ easypost_rate_id: selected_easypost_rate.id,
61
+ )
62
+
63
+ shipment.ship!
64
+
65
+ expect(easypost_shipment).not_to have_received(:buy)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Spree::ShippingRate do
6
+ describe '#name' do
7
+ context 'when the shipping rate has its own name' do
8
+ it 'returns the name on the shipping rate' do
9
+ shipping_rate = build_stubbed(:shipping_rate, name: 'USPS Express')
10
+
11
+ expect(shipping_rate.name).to eq('USPS Express')
12
+ end
13
+ end
14
+
15
+ context 'when the shipping rate does not have its own name' do
16
+ it 'returns the name on the shipping method' do
17
+ shipping_rate = build_stubbed(:shipping_rate, name: nil)
18
+
19
+ expect(shipping_rate.name).to eq(shipping_rate.shipping_method.name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.describe SolidusEasypost::AddressBuilder do
2
+ describe '.from_address', vcr: { cassette_name: 'address_builder/from_address' } do
3
+ it 'builds an address with the correct attributes' do
4
+ address = described_class.from_address(build_stubbed(:address))
5
+
6
+ expect(address).to have_attributes(object: 'Address')
7
+ end
8
+ end
9
+
10
+ describe '.from_stock_location', vcr: { cassette_name: 'address_builder/from_stock_location' } do
11
+ it 'builds an address with the correct attributes' do
12
+ address = described_class.from_stock_location(create(:stock_location))
13
+
14
+ expect(address).to have_attributes(object: 'Address')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe SolidusEasypost::Calculator::WeightDimensionCalculator do
4
+ describe '#compute' do
5
+ subject(:compute) { described_class.new.compute(resource) }
6
+
7
+ context 'when a wrong resource is passed' do
8
+ let(:resource) { create(:shipment) }
9
+
10
+ it 'raises an unknown partial resource error' do
11
+ expect { compute }.to raise_error(SolidusEasypost::Errors::UnknownPartialResourceError)
12
+ end
13
+ end
14
+
15
+ context 'when a Spree::Stock::Package is passed' do
16
+ let(:resource) { create(:shipment).to_package }
17
+
18
+ before { allow(SolidusEasypost::ParcelDimension).to receive(:new) }
19
+
20
+ it 'build a parcel dimension' do
21
+ compute
22
+
23
+ expect(SolidusEasypost::ParcelDimension).to have_received(:new).with({ weight: 10.to_f })
24
+ end
25
+ end
26
+
27
+ context 'when a SolidusEasypost::ReturnAuthorization is passed' do
28
+ let(:resource) { SolidusEasypost::ReturnAuthorization.new(create(:return_authorization)) }
29
+
30
+ before { allow(SolidusEasypost::ParcelDimension).to receive(:new) }
31
+
32
+ it 'build a parcel dimension' do
33
+ compute
34
+
35
+ expect(SolidusEasypost::ParcelDimension).to have_received(:new).with({ weight: 0.to_f })
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SolidusEasypost::Estimator, vcr: { cassette_name: 'estimator' } do
6
+ describe '#shipping_rates' do
7
+ context 'when the shipping methods are available to users' do
8
+ it 'returns the shipping rates' do
9
+ stub_shipping_method_selector(available_to_users: true)
10
+
11
+ rates = described_class.new.shipping_rates(create(:shipment).to_package)
12
+
13
+ hash = {
14
+ "USPS Express" => 22.74,
15
+ "USPS First" => 3.82,
16
+ "USPS ParcelSelect" => 6.85,
17
+ "USPS Priority" => 6.95,
18
+ }
19
+
20
+ expect(rates.sort_by(&:name).map { |r| [r.name, r.cost] }.to_h).to eq(hash)
21
+ end
22
+ end
23
+
24
+ context 'when the shipping methods are not available to users' do
25
+ it 'returns no rates' do
26
+ stub_shipping_method_selector(available_to_users: false)
27
+
28
+ rates = described_class.new.shipping_rates(create(:shipment).to_package)
29
+
30
+ expect(rates).to be_empty
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def stub_shipping_method_selector(available_to_users:)
38
+ shipping_method_selector = Class.new.tap do |klass|
39
+ klass.define_method(:shipping_method_for) do |rate|
40
+ Spree::ShippingMethod.new(
41
+ name: "#{rate.carrier} #{rate.service}",
42
+ available_to_users: available_to_users,
43
+ )
44
+ end
45
+ end
46
+
47
+ stub_const('FakeShippingMethodSelector', shipping_method_selector)
48
+
49
+ allow(SolidusEasypost.configuration).to receive(:shipping_method_selector_class)
50
+ .and_return(FakeShippingMethodSelector)
51
+ end
52
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe SolidusEasypost::ParcelBuilder do
4
+ describe '.from_package', vcr: { cassette_name: 'parcel_builder/from_package' } do
5
+ let(:shipment) { create(:shipment) }
6
+ let(:package) { shipment.to_package }
7
+ let(:parcel_dimension_calculator) { instance_spy('SolidusEasypost.configuration.parcel_dimension_calculator_class') }
8
+ let(:parcel_dimension) { instance_spy('SolidusEasypost::ParcelDimension') }
9
+
10
+ before do
11
+ allow(SolidusEasypost.configuration.parcel_dimension_calculator_class)
12
+ .to receive(:new)
13
+ .and_return(parcel_dimension_calculator)
14
+
15
+ allow(parcel_dimension_calculator).to receive(:compute).and_return(parcel_dimension)
16
+ allow(parcel_dimension).to receive(:to_h).and_return(dimension_hash)
17
+ allow(EasyPost::Parcel).to receive(:create).and_call_original
18
+ end
19
+
20
+ context 'when there is only the weight set' do
21
+ let(:dimension_hash) { { weight: 10.to_f } }
22
+
23
+ it 'builds a parcel with the correct attributes' do
24
+ parcel = described_class.from_package(package)
25
+
26
+ expect(parcel_dimension_calculator)
27
+ .to have_received(:compute)
28
+ .with(package)
29
+
30
+ expect(EasyPost::Parcel)
31
+ .to have_received(:create)
32
+ .with({ weight: 10.to_f })
33
+
34
+ expect(parcel).to have_attributes(object: 'Parcel')
35
+ end
36
+ end
37
+
38
+ context 'when all the properties are set' do
39
+ let(:dimension_hash) do
40
+ { weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f }
41
+ end
42
+
43
+ it 'builds a parcel with the correct attributes' do
44
+ parcel = described_class.from_package(package)
45
+
46
+ expect(parcel_dimension_calculator)
47
+ .to have_received(:compute)
48
+ .with(package)
49
+
50
+ expect(EasyPost::Parcel)
51
+ .to have_received(:create)
52
+ .with({ weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f })
53
+
54
+ expect(parcel).to have_attributes(object: 'Parcel')
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '.from_return_authorization', vcr: { cassette_name: 'parcel_builder/from_return_authorization' } do
60
+ let(:return_item) { create(:return_item) }
61
+ let(:return_authorization) { return_item.return_authorization }
62
+ let(:parcel_dimension_calculator) { instance_spy('SolidusEasypost.configuration.parcel_dimension_calculator_class') }
63
+ let(:parcel_dimension) { instance_spy('SolidusEasypost::ParcelDimension') }
64
+
65
+ before do
66
+ allow(SolidusEasypost.configuration.parcel_dimension_calculator_class)
67
+ .to receive(:new)
68
+ .and_return(parcel_dimension_calculator)
69
+
70
+ allow(parcel_dimension_calculator).to receive(:compute).and_return(parcel_dimension)
71
+ allow(parcel_dimension).to receive(:to_h).and_return(dimension_hash)
72
+ allow(EasyPost::Parcel).to receive(:create).and_call_original
73
+ end
74
+
75
+ context 'when there is only the weight set' do
76
+ let(:dimension_hash) { { weight: 10.to_f } }
77
+
78
+ it 'builds a parcel with the correct attributes' do
79
+ parcel = described_class.from_return_authorization(return_authorization)
80
+
81
+ expect(parcel_dimension_calculator)
82
+ .to have_received(:compute)
83
+ .with(return_authorization)
84
+
85
+ expect(EasyPost::Parcel)
86
+ .to have_received(:create)
87
+ .with({ weight: 10.to_f })
88
+
89
+ expect(parcel).to have_attributes(object: 'Parcel')
90
+ end
91
+ end
92
+
93
+ context 'when all the properties are set' do
94
+ let(:dimension_hash) do
95
+ { weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f }
96
+ end
97
+
98
+ it 'builds a parcel with the correct attributes' do
99
+ parcel = described_class.from_return_authorization(return_authorization)
100
+
101
+ expect(parcel_dimension_calculator)
102
+ .to have_received(:compute)
103
+ .with(return_authorization)
104
+
105
+ expect(EasyPost::Parcel)
106
+ .to have_received(:create)
107
+ .with({ weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f })
108
+
109
+ expect(parcel).to have_attributes(object: 'Parcel')
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe SolidusEasypost::ShipmentBuilder do
4
+ describe '.from_package', vcr: { cassette_name: 'shipment_builder/from_package' } do
5
+ it 'builds a shipment with the correct attributes' do
6
+ shipment = described_class.from_package(create(:shipment).to_package)
7
+
8
+ expect(shipment).to have_attributes(object: 'Shipment')
9
+ end
10
+ end
11
+
12
+ describe '.from_shipment', vcr: { cassette_name: 'shipment_builder/from_shipment' } do
13
+ it 'builds a shipment with the correct attributes' do
14
+ shipment = described_class.from_shipment(create(:shipment))
15
+
16
+ expect(shipment).to have_attributes(object: 'Shipment')
17
+ end
18
+ end
19
+
20
+ describe '.from_return_authorization', vcr: { cassette_name: 'shipment_builder/from_return_authorization' } do
21
+ it 'builds a shipment with the correct attributes' do
22
+ solidus_return_authorization = create(:return_item).return_authorization
23
+ shipment = described_class.from_return_authorization(SolidusEasypost::ReturnAuthorization.new(solidus_return_authorization))
24
+
25
+ expect(shipment).to have_attributes(object: 'Shipment')
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ RSpec.describe SolidusEasypost::ShippingMethodSelector do
2
+ describe '#shipping_method_for' do
3
+ context 'when a shipping method for the given carrier and service exists' do
4
+ it 'returns the existing shipping method' do
5
+ shipping_method = create(:shipping_method, carrier: 'USPS', service_level: 'Express')
6
+ easypost_rate = EasyPost::Rate.construct_from('carrier' => 'USPS', 'service' => 'Express')
7
+
8
+ selector = described_class.new
9
+ selected_shipping_method = selector.shipping_method_for(easypost_rate)
10
+
11
+ expect(selected_shipping_method).to eq(shipping_method)
12
+ end
13
+ end
14
+
15
+ context 'when a shipping method for the given carrier and service does not exist' do
16
+ it 'creates a new shipping method' do
17
+ shipping_category = create(:shipping_category)
18
+ easypost_rate = EasyPost::Rate.construct_from('carrier' => 'USPS', 'service' => 'Express')
19
+
20
+ selector = described_class.new
21
+ selected_shipping_method = selector.shipping_method_for(easypost_rate)
22
+
23
+ expect(selected_shipping_method).to have_attributes(
24
+ name: 'USPS Express',
25
+ carrier: 'USPS',
26
+ service_level: 'Express',
27
+ shipping_categories: [shipping_category],
28
+ available_to_users: false,
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.describe SolidusEasypost::ShippingRateCalculator do
2
+ describe '#compute' do
3
+ it 'returns the amount on the EasyPost rate' do
4
+ easypost_rate = EasyPost::Rate.construct_from('rate' => 25.0)
5
+
6
+ calculator = described_class.new
7
+ computed_rate = calculator.compute(easypost_rate)
8
+
9
+ expect(computed_rate).to eq(25.0)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,58 @@
1
+ RSpec.describe SolidusEasypost::TrackerWebhookHandler do
2
+ describe '.call' do
3
+ context 'when the event is not tracker.updated' do
4
+ it 'does not process the event' do
5
+ stub_const('Spree::Event', class_spy(Spree::Event))
6
+ create(:carton, easy_post_tracker_id: 'trk_test')
7
+
8
+ payload = {
9
+ 'description' => 'tracker.created',
10
+ 'result' => {
11
+ 'id' => 'trk_test',
12
+ },
13
+ }
14
+ described_class.call(payload)
15
+
16
+ expect(Spree::Event).not_to have_received(:fire)
17
+ end
18
+ end
19
+
20
+ context 'when the tracker was not registered in Solidus' do
21
+ it 'does not process the event' do
22
+ stub_const('Spree::Event', class_spy(Spree::Event))
23
+ create(:carton, easy_post_tracker_id: 'trk_test')
24
+
25
+ payload = {
26
+ 'description' => 'tracker.updated',
27
+ 'result' => {
28
+ 'id' => 'trk_test1',
29
+ },
30
+ }
31
+ described_class.call(payload)
32
+
33
+ expect(Spree::Event).not_to have_received(:fire)
34
+ end
35
+ end
36
+
37
+ context 'when the event is a tracker update for a registered tracker' do
38
+ it 'forwards the event via the event bus' do
39
+ stub_const('Spree::Event', class_spy(Spree::Event))
40
+ carton = create(:carton, easy_post_tracker_id: 'trk_test')
41
+
42
+ payload = {
43
+ 'description' => 'tracker.updated',
44
+ 'result' => {
45
+ 'id' => 'trk_test',
46
+ },
47
+ }
48
+ described_class.call(payload)
49
+
50
+ expect(Spree::Event).to have_received(:fire).with(
51
+ 'solidus_easypost.tracker.updated',
52
+ carton: carton,
53
+ payload: payload,
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SolidusEasypost do
6
+ describe '.configuration' do
7
+ it 'returns the configuration' do
8
+ expect(described_class.configuration).to be_instance_of(SolidusEasypost::Configuration)
9
+ end
10
+ end
11
+
12
+ describe '.configure' do
13
+ it 'yields the configuration' do
14
+ expect { |b| described_class.configure(&b) }.to yield_with_args(
15
+ an_instance_of(SolidusEasypost::Configuration),
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Configure Rails Environment
4
+ ENV['RAILS_ENV'] = 'test'
5
+
6
+ # Run Coverage report
7
+ require 'solidus_dev_support/rspec/coverage'
8
+
9
+ # Create the dummy app if it's still missing.
10
+ dummy_env = "#{__dir__}/dummy/config/environment.rb"
11
+ system 'bin/rake extension:test_app' unless File.exist? dummy_env
12
+ require dummy_env
13
+
14
+ # Requires factories and other useful helpers defined in spree_core.
15
+ require 'solidus_dev_support/rspec/feature_helper'
16
+
17
+ # Requires supporting ruby files with custom matchers and macros, etc,
18
+ # in spec/support/ and its subdirectories.
19
+ Dir["#{__dir__}/support/**/*.rb"].sort.each { |f| require f }
20
+
21
+ # Requires factories defined in lib/solidus_easypost/testing_support/factories.rb
22
+ require 'solidus_easypost/testing_support/factories'
23
+
24
+ RSpec.configure do |config|
25
+ config.infer_spec_type_from_file_location!
26
+ config.use_transactional_fixtures = false
27
+
28
+ if Spree.solidus_gem_version < Gem::Version.new('2.11')
29
+ config.extend Spree::TestingSupport::AuthorizationHelpers::Request, type: :system
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.configure do |config|
4
+ config.before { EasyPost.api_key = 'CvzYtuda6KRI9JjG7SAHbA' }
5
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.use_parent_strategy = false