solidus_shipstation 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.bundle/config +2 -0
- data/.circleci/config.yml +41 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +17 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/.rubocop_todo.yml +40 -0
- data/CHANGELOG.md +36 -0
- data/Gemfile +33 -0
- data/LICENSE +26 -0
- data/README.md +208 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/spree/backend/solidus_shipstation.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_shipstation.js +2 -0
- data/app/assets/stylesheets/spree/backend/solidus_shipstation.css +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_shipstation.css +4 -0
- data/app/controllers/spree/shipstation_controller.rb +45 -0
- data/app/decorators/models/solidus_shipstation/spree/shipment_decorator.rb +33 -0
- data/app/helpers/solidus_shipstation/export_helper.rb +32 -0
- data/app/jobs/solidus_shipstation/api/schedule_shipment_syncs_job.rb +19 -0
- data/app/jobs/solidus_shipstation/api/sync_shipments_job.rb +41 -0
- data/app/queries/solidus_shipstation/shipment/between_query.rb +14 -0
- data/app/queries/solidus_shipstation/shipment/exportable_query.rb +24 -0
- data/app/queries/solidus_shipstation/shipment/pending_api_sync_query.rb +63 -0
- data/app/views/spree/shipstation/export.xml.builder +58 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +86 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20210220093010_add_shipstation_api_sync_fields.rb +9 -0
- data/lib/generators/solidus_shipstation/install/install_generator.rb +27 -0
- data/lib/generators/solidus_shipstation/install/templates/initializer.rb +62 -0
- data/lib/solidus_shipstation.rb +16 -0
- data/lib/solidus_shipstation/api/batch_syncer.rb +70 -0
- data/lib/solidus_shipstation/api/client.rb +38 -0
- data/lib/solidus_shipstation/api/rate_limited_error.rb +23 -0
- data/lib/solidus_shipstation/api/request_error.rb +33 -0
- data/lib/solidus_shipstation/api/request_runner.rb +50 -0
- data/lib/solidus_shipstation/api/shipment_serializer.rb +84 -0
- data/lib/solidus_shipstation/api/threshold_verifier.rb +28 -0
- data/lib/solidus_shipstation/configuration.rb +44 -0
- data/lib/solidus_shipstation/engine.rb +19 -0
- data/lib/solidus_shipstation/errors.rb +23 -0
- data/lib/solidus_shipstation/shipment_notice.rb +58 -0
- data/lib/solidus_shipstation/testing_support/factories.rb +4 -0
- data/lib/solidus_shipstation/version.rb +5 -0
- data/solidus_shipstation.gemspec +40 -0
- data/spec/controllers/spree/shipstation_controller_spec.rb +103 -0
- data/spec/fixtures/shipstation_xml_schema.xsd +171 -0
- data/spec/jobs/solidus_shipstation/api/schedule_shipment_syncs_job_spec.rb +32 -0
- data/spec/jobs/solidus_shipstation/api/sync_shipments_job_spec.rb +102 -0
- data/spec/lib/solidus_shipstation/api/batch_syncer_spec.rb +229 -0
- data/spec/lib/solidus_shipstation/api/client_spec.rb +120 -0
- data/spec/lib/solidus_shipstation/api/rate_limited_error_spec.rb +21 -0
- data/spec/lib/solidus_shipstation/api/request_error_spec.rb +20 -0
- data/spec/lib/solidus_shipstation/api/request_runner_spec.rb +64 -0
- data/spec/lib/solidus_shipstation/api/shipment_serializer_spec.rb +12 -0
- data/spec/lib/solidus_shipstation/api/threshold_verifier_spec.rb +61 -0
- data/spec/lib/solidus_shipstation/shipment_notice_spec.rb +111 -0
- data/spec/lib/solidus_shipstation_spec.rb +9 -0
- data/spec/models/spree/shipment_spec.rb +49 -0
- data/spec/queries/solidus_shipstation/shipment/between_query_spec.rb +53 -0
- data/spec/queries/solidus_shipstation/shipment/exportable_query_spec.rb +53 -0
- data/spec/queries/solidus_shipstation/shipment/pending_api_sync_query_spec.rb +37 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/configuration_helper.rb +13 -0
- data/spec/support/controllers.rb +1 -0
- data/spec/support/webmock.rb +3 -0
- data/spec/support/xsd.rb +5 -0
- metadata +248 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe SolidusShipstation::Api::ScheduleShipmentSyncsJob do
|
4
|
+
it 'schedules the shipment sync in batches' do
|
5
|
+
stub_configuration(api_batch_size: 2)
|
6
|
+
shipments = create_list(:shipment, 3)
|
7
|
+
relation = instance_double('ActiveRecord::Relation').tap do |r|
|
8
|
+
allow(r).to receive(:find_in_batches)
|
9
|
+
.with(batch_size: 2)
|
10
|
+
.and_yield(shipments[0..1])
|
11
|
+
.and_yield([shipments.last])
|
12
|
+
end
|
13
|
+
allow(SolidusShipstation::Shipment::PendingApiSyncQuery).to receive(:apply)
|
14
|
+
.and_return(relation)
|
15
|
+
|
16
|
+
described_class.perform_now
|
17
|
+
|
18
|
+
expect(SolidusShipstation::Api::SyncShipmentsJob).to have_been_enqueued.with(shipments[0..1])
|
19
|
+
expect(SolidusShipstation::Api::SyncShipmentsJob).to have_been_enqueued.with([shipments.last])
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'reports any errors to the handler' do
|
23
|
+
error_handler = instance_spy('Proc')
|
24
|
+
stub_configuration(error_handler: error_handler)
|
25
|
+
error = RuntimeError.new('Something went wrong')
|
26
|
+
allow(SolidusShipstation::Shipment::PendingApiSyncQuery).to receive(:apply).and_raise(error)
|
27
|
+
|
28
|
+
described_class.perform_now
|
29
|
+
|
30
|
+
expect(error_handler).to have_received(:call).with(error, {})
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe SolidusShipstation::Api::SyncShipmentsJob do
|
4
|
+
include ActiveSupport::Testing::TimeHelpers
|
5
|
+
|
6
|
+
context 'when a shipment is syncable' do
|
7
|
+
context 'when the sync can be completed successfully' do
|
8
|
+
it 'syncs the provided shipments in batch' do
|
9
|
+
shipment = build_stubbed(:shipment) { |s| stub_syncability(s, true) }
|
10
|
+
batch_syncer = stub_successful_batch_syncer
|
11
|
+
|
12
|
+
described_class.perform_now([shipment])
|
13
|
+
|
14
|
+
expect(batch_syncer).to have_received(:call).with([shipment])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when the sync cannot be completed' do
|
19
|
+
context 'when the error is a rate limit' do
|
20
|
+
it 'retries intelligently when hitting a rate limit' do
|
21
|
+
freeze_time do
|
22
|
+
shipment = build_stubbed(:shipment) { |s| stub_syncability(s, true) }
|
23
|
+
stub_failing_batch_syncer(SolidusShipstation::Api::RateLimitedError.new(
|
24
|
+
response_code: 429,
|
25
|
+
response_headers: { 'X-Rate-Limit-Reset' => 30 },
|
26
|
+
response_body: '{"message":"Too Many Requests"}',
|
27
|
+
retry_in: 30.seconds,
|
28
|
+
))
|
29
|
+
|
30
|
+
described_class.perform_now([shipment])
|
31
|
+
|
32
|
+
expect(described_class).to have_been_enqueued.at(30.seconds.from_now)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when the error is a server error' do
|
38
|
+
it 'calls the error handler' do
|
39
|
+
error_handler = instance_spy('Proc')
|
40
|
+
stub_configuration(error_handler: error_handler)
|
41
|
+
shipment = build_stubbed(:shipment) { |s| stub_syncability(s, true) }
|
42
|
+
error = SolidusShipstation::Api::RequestError.new(
|
43
|
+
response_code: 500,
|
44
|
+
response_headers: {},
|
45
|
+
response_body: '{"message":"Internal Server Error"}',
|
46
|
+
)
|
47
|
+
stub_failing_batch_syncer(error)
|
48
|
+
|
49
|
+
described_class.perform_now([shipment])
|
50
|
+
|
51
|
+
expect(error_handler).to have_received(:call).with(error, {})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when a shipment is not syncable' do
|
58
|
+
it 'skips shipments that are not pending sync' do
|
59
|
+
shipment = build_stubbed(:shipment) { |s| stub_syncability(s, false) }
|
60
|
+
batch_syncer = stub_successful_batch_syncer
|
61
|
+
|
62
|
+
described_class.perform_now([shipment])
|
63
|
+
|
64
|
+
expect(batch_syncer).not_to have_received(:call)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'fires a solidus_shipstation.api.sync_skipped event' do
|
68
|
+
stub_const('Spree::Event', class_spy(Spree::Event))
|
69
|
+
shipment = build_stubbed(:shipment) { |s| stub_syncability(s, false) }
|
70
|
+
stub_successful_batch_syncer
|
71
|
+
|
72
|
+
described_class.perform_now([shipment])
|
73
|
+
|
74
|
+
expect(Spree::Event).to have_received(:fire).with(
|
75
|
+
'solidus_shipstation.api.sync_skipped',
|
76
|
+
shipment: shipment,
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def stub_successful_batch_syncer
|
84
|
+
instance_spy(SolidusShipstation::Api::BatchSyncer).tap do |batch_syncer|
|
85
|
+
allow(SolidusShipstation::Api::BatchSyncer).to receive(:from_config).and_return(batch_syncer)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def stub_failing_batch_syncer(error)
|
90
|
+
instance_double(SolidusShipstation::Api::BatchSyncer).tap do |batch_syncer|
|
91
|
+
allow(SolidusShipstation::Api::BatchSyncer).to receive(:from_config).and_return(batch_syncer)
|
92
|
+
|
93
|
+
allow(batch_syncer).to receive(:call).and_raise(error)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def stub_syncability(shipment, result)
|
98
|
+
allow(SolidusShipstation::Api::ThresholdVerifier).to receive(:call)
|
99
|
+
.with(shipment)
|
100
|
+
.and_return(result)
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
RSpec.describe SolidusShipstation::Api::BatchSyncer do
|
2
|
+
include ActiveSupport::Testing::TimeHelpers
|
3
|
+
|
4
|
+
describe '.from_config' do
|
5
|
+
it 'creates a syncer with the configured API client' do
|
6
|
+
client = instance_double(SolidusShipstation::Api::Client)
|
7
|
+
allow(SolidusShipstation::Api::Client).to receive(:from_config).and_return(client)
|
8
|
+
shipment_matcher = -> {}
|
9
|
+
stub_configuration(api_shipment_matcher: shipment_matcher)
|
10
|
+
|
11
|
+
batch_syncer = described_class.from_config
|
12
|
+
|
13
|
+
expect(batch_syncer).to have_attributes(
|
14
|
+
client: client,
|
15
|
+
shipment_matcher: shipment_matcher,
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#call' do
|
21
|
+
context 'when the API call is successful' do
|
22
|
+
context 'when the sync operation succeeded' do
|
23
|
+
it 'updates the ShipStation data on the shipment' do
|
24
|
+
freeze_time do
|
25
|
+
shipment = instance_spy('Spree::Shipment', number: 'H123456')
|
26
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
27
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_return(
|
28
|
+
{
|
29
|
+
'results' => [
|
30
|
+
{
|
31
|
+
'orderNumber' => shipment.number,
|
32
|
+
'success' => true,
|
33
|
+
'orderId' => '123456',
|
34
|
+
}
|
35
|
+
]
|
36
|
+
}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
build_batch_syncer(client: api_client).call([shipment])
|
41
|
+
|
42
|
+
expect(shipment).to have_received(:update_columns).with(
|
43
|
+
shipstation_order_id: '123456',
|
44
|
+
shipstation_synced_at: Time.zone.now,
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'emits a solidus_shipstation.api.sync_completed event' do
|
50
|
+
stub_const('Spree::Event', class_spy(Spree::Event))
|
51
|
+
shipment = instance_spy('Spree::Shipment', number: 'H123456')
|
52
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
53
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_return(
|
54
|
+
{
|
55
|
+
'results' => [
|
56
|
+
{
|
57
|
+
'orderNumber' => shipment.number,
|
58
|
+
'success' => true,
|
59
|
+
'orderId' => '123456',
|
60
|
+
}
|
61
|
+
]
|
62
|
+
}
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
build_batch_syncer(client: api_client).call([shipment])
|
67
|
+
|
68
|
+
expect(Spree::Event).to have_received(:fire).with(
|
69
|
+
'solidus_shipstation.api.sync_completed',
|
70
|
+
shipment: shipment,
|
71
|
+
payload: {
|
72
|
+
'orderNumber' => shipment.number,
|
73
|
+
'success' => true,
|
74
|
+
'orderId' => '123456',
|
75
|
+
},
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when the sync operation failed' do
|
81
|
+
it 'does not update the ShipStation data on the shipment' do
|
82
|
+
shipment = instance_spy('Spree::Shipment', number: 'H123456')
|
83
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
84
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_return(
|
85
|
+
{
|
86
|
+
'results' => [
|
87
|
+
{
|
88
|
+
'orderNumber' => shipment.number,
|
89
|
+
'success' => false,
|
90
|
+
'orderId' => '123456',
|
91
|
+
}
|
92
|
+
]
|
93
|
+
}
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
build_batch_syncer(client: api_client).call([shipment])
|
98
|
+
|
99
|
+
expect(shipment).not_to have_received(:update_columns)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'emits a solidus_shipstation.api.sync_failed event' do
|
103
|
+
stub_const('Spree::Event', class_spy(Spree::Event))
|
104
|
+
shipment = instance_spy('Spree::Shipment', number: 'H123456')
|
105
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
106
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_return(
|
107
|
+
{
|
108
|
+
'results' => [
|
109
|
+
{
|
110
|
+
'orderNumber' => shipment.number,
|
111
|
+
'success' => false,
|
112
|
+
'orderId' => '123456',
|
113
|
+
}
|
114
|
+
]
|
115
|
+
}
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
build_batch_syncer(client: api_client).call([shipment])
|
120
|
+
|
121
|
+
expect(Spree::Event).to have_received(:fire).with(
|
122
|
+
'solidus_shipstation.api.sync_failed',
|
123
|
+
shipment: shipment,
|
124
|
+
payload: {
|
125
|
+
'orderNumber' => shipment.number,
|
126
|
+
'success' => false,
|
127
|
+
'orderId' => '123456',
|
128
|
+
},
|
129
|
+
)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when the API call hits a rate limit' do
|
135
|
+
it 'emits a solidus_shipstation.api.rate_limited event' do
|
136
|
+
stub_const('Spree::Event', class_spy(Spree::Event))
|
137
|
+
shipment = instance_double('Spree::Shipment')
|
138
|
+
error = SolidusShipstation::Api::RateLimitedError.new(
|
139
|
+
response_headers: { 'X-Rate-Limit-Reset' => 20 },
|
140
|
+
response_body: '{"message":"Too Many Requests"}',
|
141
|
+
response_code: 429,
|
142
|
+
retry_in: 20.seconds,
|
143
|
+
)
|
144
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
145
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_raise(error)
|
146
|
+
end
|
147
|
+
|
148
|
+
begin
|
149
|
+
build_batch_syncer(client: api_client).call([shipment])
|
150
|
+
rescue SolidusShipstation::Api::RateLimitedError
|
151
|
+
# We want to ignore the error here, since we're testing for the event.
|
152
|
+
end
|
153
|
+
|
154
|
+
expect(Spree::Event).to have_received(:fire).with(
|
155
|
+
'solidus_shipstation.api.rate_limited',
|
156
|
+
shipments: [shipment],
|
157
|
+
error: error,
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 're-raises the error' do
|
162
|
+
shipment = instance_double('Spree::Shipment')
|
163
|
+
error = SolidusShipstation::Api::RateLimitedError.new(
|
164
|
+
response_headers: { 'X-Rate-Limit-Reset' => 20 },
|
165
|
+
response_body: '{"message":"Too Many Requests"}',
|
166
|
+
response_code: 429,
|
167
|
+
retry_in: 20.seconds,
|
168
|
+
)
|
169
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
170
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_raise(error)
|
171
|
+
end
|
172
|
+
|
173
|
+
expect {
|
174
|
+
build_batch_syncer(client: api_client).call([shipment])
|
175
|
+
}.to raise_error(error)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when the API call results in a server error' do
|
180
|
+
it 'emits a solidus_shipstation.api.sync_errored event' do
|
181
|
+
stub_const('Spree::Event', class_spy(Spree::Event))
|
182
|
+
shipment = instance_double('Spree::Shipment')
|
183
|
+
error = SolidusShipstation::Api::RequestError.new(
|
184
|
+
response_headers: {},
|
185
|
+
response_body: '{"message":"Internal Server Error"}',
|
186
|
+
response_code: 500,
|
187
|
+
)
|
188
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
189
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_raise(error)
|
190
|
+
end
|
191
|
+
|
192
|
+
begin
|
193
|
+
build_batch_syncer(client: api_client).call([shipment])
|
194
|
+
rescue SolidusShipstation::Api::RequestError
|
195
|
+
# We want to ignore the error here, since we're testing for the event.
|
196
|
+
end
|
197
|
+
|
198
|
+
expect(Spree::Event).to have_received(:fire).with(
|
199
|
+
'solidus_shipstation.api.sync_errored',
|
200
|
+
shipments: [shipment],
|
201
|
+
error: error,
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 're-raises the error' do
|
206
|
+
stub_const('Spree::Event', class_spy(Spree::Event))
|
207
|
+
shipment = instance_double('Spree::Shipment')
|
208
|
+
error = SolidusShipstation::Api::RequestError.new(
|
209
|
+
response_headers: {},
|
210
|
+
response_body: '{"message":"Internal Server Error"}',
|
211
|
+
response_code: 500,
|
212
|
+
)
|
213
|
+
api_client = instance_double(SolidusShipstation::Api::Client).tap do |client|
|
214
|
+
allow(client).to receive(:bulk_create_orders).with([shipment]).and_raise(error)
|
215
|
+
end
|
216
|
+
|
217
|
+
expect {
|
218
|
+
build_batch_syncer(client: api_client).call([shipment])
|
219
|
+
}.to raise_error(error)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
def build_batch_syncer(client:, shipment_matcher: ->(_, shipments) { shipments.first })
|
227
|
+
described_class.new(client: client, shipment_matcher: shipment_matcher)
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
RSpec.describe SolidusShipstation::Api::Client do
|
2
|
+
describe '.from_config' do
|
3
|
+
it 'generates a client from the configuration' do
|
4
|
+
request_runner = instance_double('SolidusShipstation::Api::RequestRunner')
|
5
|
+
error_handler = instance_spy('Proc')
|
6
|
+
shipment_serializer = instance_spy('SolidusShipstation::Api::Serializer')
|
7
|
+
allow(SolidusShipstation::Api::RequestRunner).to receive(:from_config).and_return(request_runner)
|
8
|
+
allow(SolidusShipstation.config).to receive_messages(
|
9
|
+
error_handler: error_handler,
|
10
|
+
api_shipment_serializer: shipment_serializer,
|
11
|
+
)
|
12
|
+
|
13
|
+
client = described_class.from_config
|
14
|
+
|
15
|
+
expect(client).to have_attributes(
|
16
|
+
request_runner: request_runner,
|
17
|
+
error_handler: error_handler,
|
18
|
+
shipment_serializer: shipment_serializer,
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#bulk_create_orders' do
|
24
|
+
it 'calls the bulk order creation endpoint' do
|
25
|
+
request_runner = instance_spy('SolidusShipstation::Api::RequestRunner')
|
26
|
+
shipment = build_stubbed(:shipment)
|
27
|
+
serialized_shipment = { 'key' => 'value' }
|
28
|
+
|
29
|
+
client = build_client(
|
30
|
+
request_runner: request_runner,
|
31
|
+
shipment_serializer: stub_shipment_serializer(shipment => serialized_shipment),
|
32
|
+
)
|
33
|
+
client.bulk_create_orders([shipment])
|
34
|
+
|
35
|
+
expect(request_runner).to have_received(:call).with(
|
36
|
+
:post,
|
37
|
+
'/orders/createorders',
|
38
|
+
[serialized_shipment],
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not fail for serialization errors' do
|
43
|
+
request_runner = instance_spy('SolidusShipstation::Api::RequestRunner')
|
44
|
+
successful_shipment = build_stubbed(:shipment)
|
45
|
+
serialized_shipment = { 'key' => 'value' }
|
46
|
+
failing_shipment = build_stubbed(:shipment)
|
47
|
+
error = RuntimeError.new('Failed to serialize shipment!')
|
48
|
+
|
49
|
+
client = build_client(
|
50
|
+
request_runner: request_runner,
|
51
|
+
shipment_serializer: stub_shipment_serializer(
|
52
|
+
successful_shipment => serialized_shipment,
|
53
|
+
failing_shipment => error,
|
54
|
+
)
|
55
|
+
)
|
56
|
+
client.bulk_create_orders([failing_shipment, successful_shipment])
|
57
|
+
|
58
|
+
expect(request_runner).to have_received(:call).with(
|
59
|
+
:post,
|
60
|
+
'/orders/createorders',
|
61
|
+
[serialized_shipment],
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'reports any serialization errors to the error handler' do
|
66
|
+
error_handler = instance_spy('Proc')
|
67
|
+
shipment = build_stubbed(:shipment)
|
68
|
+
error = RuntimeError.new('Failed to serialize shipment!')
|
69
|
+
|
70
|
+
client = build_client(
|
71
|
+
shipment_serializer: stub_shipment_serializer(shipment => error),
|
72
|
+
error_handler: error_handler,
|
73
|
+
)
|
74
|
+
client.bulk_create_orders([shipment])
|
75
|
+
|
76
|
+
expect(error_handler).to have_received(:call).with(error, shipment: shipment)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'skips the API call if all shipments failed serialization' do
|
80
|
+
request_runner = instance_spy('SolidusShipstation::Api::RequestRunner')
|
81
|
+
failing_shipment = build_stubbed(:shipment)
|
82
|
+
|
83
|
+
client = build_client(
|
84
|
+
shipment_serializer: stub_shipment_serializer(
|
85
|
+
failing_shipment => RuntimeError.new('Failed to serialize shipment!'),
|
86
|
+
),
|
87
|
+
request_runner: request_runner,
|
88
|
+
)
|
89
|
+
client.bulk_create_orders([failing_shipment])
|
90
|
+
|
91
|
+
expect(request_runner).not_to have_received(:call)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def build_client(options = {})
|
98
|
+
described_class.new({
|
99
|
+
request_runner: instance_spy('SolidusShipstation::Api::RequestRunner'),
|
100
|
+
error_handler: instance_spy('Proc'),
|
101
|
+
shipment_serializer: stub_shipment_serializer,
|
102
|
+
}.merge(options))
|
103
|
+
end
|
104
|
+
|
105
|
+
def stub_shipment_serializer(results_map = {})
|
106
|
+
serializer = class_spy('SolidusShipstation::Api::Serializer')
|
107
|
+
|
108
|
+
results_map.each_pair do |shipment, result_or_error|
|
109
|
+
stub = allow(serializer).to receive(:call).with(shipment)
|
110
|
+
|
111
|
+
if result_or_error.is_a?(Hash)
|
112
|
+
stub.and_return(result_or_error)
|
113
|
+
else
|
114
|
+
stub.and_raise(result_or_error)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
serializer
|
119
|
+
end
|
120
|
+
end
|