solidus-adyen 0.1.2

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.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/.rubocop.yml +9 -0
  4. data/.rubocop_todo.yml +325 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +238 -0
  8. data/Rakefile +16 -0
  9. data/app/assets/javascripts/spree/backend/solidus-adyen.js +1 -0
  10. data/app/assets/javascripts/spree/checkout/payment/adyen.js +10 -0
  11. data/app/assets/javascripts/spree/frontend/solidus-adyen.js +1 -0
  12. data/app/assets/stylesheets/spree/backend/solidus-adyen/buttons.scss +15 -0
  13. data/app/assets/stylesheets/spree/backend/solidus-adyen/communication.scss +121 -0
  14. data/app/assets/stylesheets/spree/backend/solidus-adyen/variables.scss +4 -0
  15. data/app/assets/stylesheets/spree/backend/solidus-adyen.css +5 -0
  16. data/app/assets/stylesheets/spree/frontend/solidus-adyen.css +3 -0
  17. data/app/controllers/concerns/spree/adyen/admin/refunds_controller.rb +59 -0
  18. data/app/controllers/spree/adyen/hpps_controller.rb +22 -0
  19. data/app/controllers/spree/adyen_notifications_controller.rb +34 -0
  20. data/app/controllers/spree/adyen_redirect_controller.rb +90 -0
  21. data/app/models/adyen_notification.rb +159 -0
  22. data/app/models/concerns/spree/adyen/order.rb +11 -0
  23. data/app/models/concerns/spree/adyen/payment.rb +95 -0
  24. data/app/models/spree/adyen/hpp_source.rb +101 -0
  25. data/app/models/spree/adyen/notification_processor.rb +102 -0
  26. data/app/models/spree/adyen/presenters/communication.rb +31 -0
  27. data/app/models/spree/adyen/presenters/communications/adyen_notification.rb +26 -0
  28. data/app/models/spree/adyen/presenters/communications/base.rb +50 -0
  29. data/app/models/spree/adyen/presenters/communications/hpp_source.rb +31 -0
  30. data/app/models/spree/adyen/presenters/communications/log_entry.rb +28 -0
  31. data/app/models/spree/adyen/presenters/communications.rb +9 -0
  32. data/app/models/spree/gateway/adyen_hpp.rb +95 -0
  33. data/app/overrides/spree/admin/shared/_order_summary.rb +6 -0
  34. data/app/views/spree/admin/payments/source_forms/_adyen.html.erb +1 -0
  35. data/app/views/spree/admin/payments/source_views/_adyen.html.erb +14 -0
  36. data/app/views/spree/adyen/_manual_refund.html.erb +9 -0
  37. data/app/views/spree/adyen/communication/_communication.html.erb +42 -0
  38. data/app/views/spree/adyen/hpps/directory.html.erb +10 -0
  39. data/app/views/spree/checkout/payment/_adyen.html.erb +33 -0
  40. data/bin/checkout.rb +111 -0
  41. data/bin/regen.sh +1 -0
  42. data/config/initializers/solidus_adyen.rb +1 -0
  43. data/config/locales/en.yml +23 -0
  44. data/config/routes.rb +13 -0
  45. data/db/migrate/20131017040945_create_adyen_notifications.rb +29 -0
  46. data/db/migrate/20150911201942_add_index_adyen_notifications_psp_reference.rb +5 -0
  47. data/db/migrate/20150914162539_create_spree_adyen_hpp_sources.rb +21 -0
  48. data/db/migrate/20151007090519_add_days_to_ship_to_config.rb +5 -0
  49. data/db/migrate/20151020230830_remove_indices_on_adyen_notifications.rb +6 -0
  50. data/db/migrate/20151106093023_allow_merchant_reference_to_be_null_for_adyen_notification.rb +5 -0
  51. data/lib/solidus-adyen.rb +1 -0
  52. data/lib/spree/adyen/engine.rb +32 -0
  53. data/lib/spree/adyen/form.rb +135 -0
  54. data/lib/spree/adyen/hpp_check.rb +11 -0
  55. data/lib/spree/adyen/url.rb +30 -0
  56. data/lib/spree/adyen/version.rb +5 -0
  57. data/lib/spree/adyen.rb +6 -0
  58. data/solidus-adyen.gemspec +51 -0
  59. data/spec/controllers/concerns/spree/adyen/admin/refunds_controller_spec.rb +76 -0
  60. data/spec/controllers/spree/adyen/hpps_controller_spec.rb +43 -0
  61. data/spec/controllers/spree/adyen_notifications_controller_spec.rb +118 -0
  62. data/spec/controllers/spree/adyen_redirect_controller_spec.rb +154 -0
  63. data/spec/factories/active_merchant_billing_response.rb +23 -0
  64. data/spec/factories/adyen_notification.rb +91 -0
  65. data/spec/factories/spree_adyen_hpp_source.rb +15 -0
  66. data/spec/factories/spree_gateway_adyen_hpp.rb +23 -0
  67. data/spec/factories/spree_payment.rb +13 -0
  68. data/spec/lib/spree/adyen/form_spec.rb +214 -0
  69. data/spec/models/adyen_notification_spec.rb +86 -0
  70. data/spec/models/concerns/spree/adyen/order_spec.rb +22 -0
  71. data/spec/models/concerns/spree/adyen/payment_spec.rb +93 -0
  72. data/spec/models/spree/adyen/hpp_source_spec.rb +101 -0
  73. data/spec/models/spree/adyen/notification_processor_spec.rb +205 -0
  74. data/spec/models/spree/adyen/presenters/communication_spec.rb +62 -0
  75. data/spec/models/spree/gateway/adyen_hpp_spec.rb +76 -0
  76. data/spec/spec_helper.rb +65 -0
  77. data/spec/support/shared_contexts/mock_adyen_api.rb +47 -0
  78. metadata +463 -0
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe AdyenNotification do
4
+ it { is_expected.to have_many(:next).inverse_of(:prev) }
5
+ it { is_expected.to belong_to(:prev).inverse_of(:next) }
6
+ it { is_expected.to belong_to :order }
7
+
8
+ describe ".payment" do
9
+ subject { notification.payment }
10
+
11
+ let(:ref) { "999999999" }
12
+ let!(:payment) { create :payment, response_code: ref }
13
+ let!(:notification) { described_class.new attr => ref }
14
+
15
+ shared_examples "finds the payment" do
16
+ it { is_expected.to eq payment }
17
+ end
18
+
19
+ context "normal notification" do
20
+ let(:attr) { :original_reference }
21
+ include_examples "finds the payment"
22
+ end
23
+
24
+ context "modification notification" do
25
+ let(:attr) { :psp_reference }
26
+ include_examples "finds the payment"
27
+ end
28
+ end
29
+
30
+ describe "#build" do
31
+ subject { described_class.build params }
32
+
33
+ let(:params) do
34
+ { "currency" => "USD",
35
+ "eventCode" => "AUTHORISATION",
36
+ "eventDate" => "2013-10-21T00:00:00.00Z",
37
+ "live" => "false",
38
+ "merchantAccountCode" => "Test",
39
+ "merchantReference" => "R999999999",
40
+ "operations" => "CANCEL,CAPTURE,REFUND",
41
+ "originalReference" => "",
42
+ "paymentMethod" => "visa",
43
+ "pspReference" => "999999999",
44
+ "reason" => "41061:1111:6/2016",
45
+ "success" => "true",
46
+ "value" => "6999",
47
+ }
48
+ end
49
+
50
+ it "makes a new notification with the expected fields" do
51
+ is_expected.
52
+ to be_a_new_record.
53
+ and have_attributes(
54
+ currency: "USD",
55
+ event_code: "AUTHORISATION",
56
+ event_date: Time.utc(2013, 10, 21),
57
+ live: false,
58
+ merchant_account_code: "Test",
59
+ merchant_reference: "R999999999",
60
+ operations: "CANCEL,CAPTURE,REFUND",
61
+ original_reference: nil,
62
+ payment_method: "visa",
63
+ psp_reference: "999999999",
64
+ reason: "41061:1111:6/2016",
65
+ success: true,
66
+ value: 6999,
67
+ )
68
+ end
69
+ end
70
+
71
+ describe ".actions" do
72
+ subject { notification.actions }
73
+ let(:notification) { create :notification, :auth, operations: operations }
74
+
75
+ context "when the notification has operations" do
76
+ let(:operations) { "CAPTURE,REFUND,CANCEL_OR_REFUND" }
77
+
78
+ it { is_expected.to eq ["capture", "refund", "cancel_or_refund"] }
79
+ end
80
+
81
+ context "when the notification's operations are nil" do
82
+ let(:operations) { nil }
83
+ it { is_expected.to eq [] }
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Spree::Order do
4
+ include_context "mock adyen api", success: true
5
+
6
+ describe "requires_manual_refund?" do
7
+ subject { order.requires_manual_refund? }
8
+
9
+ let!(:order) { create :order_ready_to_ship }
10
+
11
+ it { is_expected.to be false }
12
+
13
+ context "when it is cancelled and has a payment that much be manually refunded" do
14
+ let!(:payment) { create :hpp_payment, order: order, source: source }
15
+ let(:source) { create :hpp_source, :sofort, order: order }
16
+
17
+ before { order.cancel! }
18
+
19
+ it { is_expected.to be true }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,93 @@
1
+ require "spec_helper"
2
+
3
+ describe Spree::Adyen::Payment do
4
+ let(:payment) { create :hpp_payment }
5
+
6
+ shared_examples "gateway action" do
7
+ context "when the action succeeds" do
8
+ include_context "mock adyen api", success: true
9
+
10
+ it "logs the response" do
11
+ expect{ subject }.to change{ payment.reload.log_entries.count }.by(1)
12
+ end
13
+
14
+ it "changes payment state to processing" do
15
+ expect{ subject }.to change{ payment.state }.to("processing")
16
+ end
17
+ end
18
+
19
+ context "when the action fails" do
20
+ include_context(
21
+ "mock adyen api",
22
+ success: false,
23
+ fault_message: "Expected message")
24
+
25
+ it "logs the response" do
26
+ expect{ subject }.
27
+ to raise_error(Spree::Core::GatewayError).
28
+ and change{ payment.reload.log_entries.count }.by(1)
29
+ end
30
+
31
+ it "does not change the status of the payment" do
32
+ expect{ subject }.
33
+ to raise_error(Spree::Core::GatewayError, "Expected message").
34
+ and keep { payment.reload.state }
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "cancel!" do
40
+ subject { payment.cancel! }
41
+ include_examples "gateway action"
42
+
43
+ context "when the payment doesn't have an hpp source" do
44
+ let(:payment) { create :payment }
45
+
46
+ it "keeps the orginal behaviour" do
47
+ expect{ subject }.
48
+ to change { payment.reload.state }.
49
+ from("checkout").
50
+ to("void")
51
+ end
52
+ end
53
+
54
+ context "when payment is only manually refundable" do
55
+ let(:payment) { create :hpp_payment, source: source }
56
+ let(:source) { create :hpp_source, :sofort }
57
+
58
+ it "creates a log entry" do
59
+ expect { subject }.
60
+ to change { payment.reload.log_entries.count }
61
+ end
62
+
63
+ it "doesn't change the state" do
64
+ expect { subject }.
65
+ to_not change { payment.reload.state }
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "capture!" do
71
+ subject { payment.capture! }
72
+ include_examples "gateway action"
73
+
74
+ context "when the payment doesn't have an hpp source" do
75
+ let(:payment) { create :payment }
76
+
77
+ it "keeps the orginal behaviour" do
78
+ expect{ subject }.
79
+ to change { payment.reload.state }.
80
+ from("checkout").
81
+ to("completed").
82
+
83
+ and change { payment.capture_events.count }.
84
+ by(1)
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "credit!" do
90
+ subject { payment.credit! "1000", currency: "EUR" }
91
+ include_examples "gateway action"
92
+ end
93
+ end
@@ -0,0 +1,101 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Spree::Adyen::HppSource do
4
+ include_context "mock adyen api", success: true
5
+
6
+ it { is_expected.to belong_to(:order) }
7
+ it { is_expected.to have_one(:payment) }
8
+ it { is_expected.to have_many(:notifications) }
9
+
10
+ let(:hpp_source) do
11
+ create :hpp_source,
12
+ psp_reference: "999999999",
13
+ merchant_reference: "R11111111",
14
+ payment: create(:hpp_payment)
15
+ end
16
+
17
+ describe ".actions" do
18
+ subject { hpp_source.actions }
19
+ let!(:notification) do
20
+ create(
21
+ :notification,
22
+ :auth,
23
+ operations: "CAPTURE,REFUND",
24
+ psp_reference: "999999999",
25
+ merchant_reference: "R11111111",
26
+ processed: true
27
+ )
28
+ end
29
+
30
+ it { expect(hpp_source.notifications.count).to eq 1 }
31
+ it { expect(hpp_source.actions).
32
+ to eq %w{capture credit} }
33
+
34
+ shared_examples "has no actions" do
35
+ it { is_expected.to eq [] }
36
+ end
37
+
38
+ context "when it has no auth notification" do
39
+ let!(:notification) { nil }
40
+ include_examples "has no actions"
41
+ end
42
+
43
+ context "when the payment is void" do
44
+ before { hpp_source.payment.void }
45
+ include_examples "has no actions"
46
+ end
47
+
48
+ context "when the payment is still proccesing" do
49
+ before { hpp_source.payment.started_processing! }
50
+ include_examples "has no actions"
51
+ end
52
+ end
53
+
54
+ describe ".can_capture?" do
55
+ subject { hpp_source.can_capture? payment }
56
+ let!(:payment) { create :payment, amount: 10.0 }
57
+
58
+ it { is_expected.to be true }
59
+
60
+ context "when there is no outstanding balance" do
61
+ before do
62
+ payment.
63
+ capture_events.
64
+ create! amount: ::Money.new(1000, "EUR").to_f
65
+ end
66
+ it { is_expected.to be false }
67
+ end
68
+ end
69
+
70
+ describe ".authorised?" do
71
+ subject { described_class.new(auth_result: event).authorised? }
72
+
73
+ context "when pending" do
74
+ let(:event) { "PENDING" }
75
+ it { is_expected.to be true }
76
+ end
77
+
78
+ context "when authorised" do
79
+ let(:event) { "AUTHORISED" }
80
+ it { is_expected.to be true }
81
+ end
82
+
83
+ context "when something else" do
84
+ let(:event) { "REFUSED" }
85
+ it { is_expected.to be false }
86
+ end
87
+ end
88
+
89
+ describe ".can_cancel?" do
90
+ subject { hpp_source.can_cancel? hpp_source.payment }
91
+
92
+ context "when the payment has refunds" do
93
+ before { create :refund, amount: 1, payment: hpp_source.payment }
94
+ it { is_expected.to be false }
95
+ end
96
+
97
+ context "when the payment doesn't have refunds" do
98
+ it { is_expected.to be true }
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,205 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Spree::Adyen::NotificationProcessor do
4
+ include_context "mock adyen api", success: true
5
+
6
+ describe "#process" do
7
+ subject { described_class.new(notification).process! }
8
+
9
+ let!(:payment) do
10
+ create(
11
+ :payment,
12
+ amount: 23.99,
13
+ state: payment_state,
14
+ payment_method: hpp_gateway,
15
+ order: create(:order, currency: "EUR")
16
+ )
17
+ end
18
+
19
+ let!(:hpp_gateway) do
20
+ create(:hpp_gateway)
21
+ end
22
+
23
+ let!(:notification) do
24
+ create(
25
+ :notification,
26
+ event_type, # these are registered traits, refer to the factory
27
+ success: success,
28
+ value: 2399,
29
+ currency: "EUR",
30
+ payment: payment
31
+ )
32
+ end
33
+
34
+ let(:payment_state) { "pending" }
35
+ let(:success) { true }
36
+
37
+ shared_examples "returns the notification" do
38
+ it "always returns the notification" do
39
+ is_expected.to be_a AdyenNotification
40
+ end
41
+ end
42
+
43
+ shared_examples "processed event" do
44
+ include_examples "returns the notification"
45
+
46
+ it "marks the notification as processed" do
47
+ expect{ subject }.
48
+ to change{ notification.processed }.
49
+ from(false).
50
+ to(true)
51
+ end
52
+ end
53
+
54
+ shared_examples "completes payment" do
55
+ include_examples "processed event"
56
+
57
+ it "updates the captured amount" do
58
+ expect{ subject }.
59
+ to change{ payment.captured_amount }.
60
+ from(0).
61
+ to(23.99).
62
+
63
+ and change{ payment.capture_events.count }.
64
+ from(0).
65
+ to(1)
66
+ end
67
+
68
+ it "completes the payment" do
69
+ expect{ subject }.
70
+ to change{ payment.reload.state }.
71
+ from("pending").
72
+ to("completed")
73
+ end
74
+ end
75
+
76
+ shared_examples "fails payment" do
77
+ include_examples "processed event"
78
+
79
+ it "marks the payment as a failure" do
80
+ expect{ subject }.
81
+ to change{ payment.reload.state }.
82
+ from("pending").
83
+ to("failed")
84
+ end
85
+ end
86
+
87
+ shared_examples "does nothing" do
88
+ include_examples "processed event"
89
+
90
+ it "does not change the payment state" do
91
+ expect{ subject }.to_not change{ payment.state }
92
+ end
93
+
94
+ it "does not change the captured amount" do
95
+ expect{ subject }.to_not change{ payment.captured_amount }
96
+ end
97
+ end
98
+
99
+ context "when event is AUTHORISATION" do
100
+ let(:event_type) { :auth }
101
+
102
+ it "changes the available actions"
103
+
104
+ context "and payment method was c_cash", pending: true do
105
+ pending "completes payment"
106
+ end
107
+
108
+ context "and payment method was bank transfer", pending: true do
109
+ pending "completes payment"
110
+ end
111
+
112
+ context "and payment method was ideal" do
113
+ let(:event_type) { :ideal_auth }
114
+ include_examples "completes payment"
115
+ end
116
+
117
+ context "and it was not successful" do
118
+ let(:success) { false }
119
+ include_examples "fails payment"
120
+
121
+ context "and the payment was already complete" do
122
+ let(:payment_state) { "completed" }
123
+ include_examples "does nothing"
124
+ end
125
+ end
126
+ end
127
+
128
+ context "when event is CAPTURE" do
129
+ let(:event_type) { :capture }
130
+ include_examples "completes payment"
131
+ end
132
+
133
+ context "when event is CANCEL_OR_REFUND" do
134
+ let(:event_type) { :cancel_or_refund }
135
+
136
+ before do
137
+ payment.complete
138
+ end
139
+
140
+ it "voids the payment" do
141
+ expect { subject }.
142
+ to change { payment.reload.state }.
143
+ from("completed").
144
+ to("void")
145
+ end
146
+ end
147
+
148
+ context "when event is REFUND" do
149
+ include_examples "processed event"
150
+
151
+ let(:event_type) { :refund }
152
+ let(:payment_state) { "processing" }
153
+
154
+ it "creates a refund" do
155
+ expect { subject }.
156
+ to change { payment.reload.refunds.count }.
157
+ from(0).
158
+ to(1)
159
+ end
160
+
161
+ it "changes the payment state to completed" do
162
+ expect { subject }.
163
+ to change { payment.reload.state }.
164
+ from("processing").
165
+ to("completed")
166
+ end
167
+ end
168
+
169
+ context "when the event is an event we don't process" do
170
+ let(:event_type) { :pending }
171
+ include_examples "returns the notification"
172
+
173
+ it "sets processed to false" do
174
+ expect(subject.processed).to be false
175
+ end
176
+ end
177
+ end
178
+
179
+ describe "#process_outstanding!" do
180
+ subject { described_class.process_outstanding! payment }
181
+
182
+ let!(:payment) { create :hpp_payment, amount: 19.99, state: "pending" }
183
+
184
+ let!(:notifications) do
185
+ opts = {payment: payment, value: 1999}
186
+ [
187
+ create(:notification, :auth, **opts),
188
+ create(:notification, :capture, **opts)
189
+ ]
190
+ end
191
+
192
+ it "processes all notifications" do
193
+ subject
194
+ notifications.map(&:reload)
195
+ expect(notifications).to all be_processed
196
+ end
197
+
198
+ it "modifies the payment" do
199
+ expect { subject }.
200
+ to change { payment.state }.from("pending").to("completed").
201
+
202
+ and change { payment.captured_amount }.from(0).to(19.99)
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,62 @@
1
+ require "spec_helper"
2
+
3
+ module Spree
4
+ module Adyen
5
+ module Presenters
6
+ RSpec.describe Communication do
7
+ let!(:source) { payment.source }
8
+ let!(:payment) { create :hpp_payment }
9
+ let!(:notification) { create :notification, :auth, payment: payment }
10
+
11
+ let!(:log_entry) do
12
+ payment.send(
13
+ :record_response,
14
+ OpenStruct.new(success?: true, message: "sup")
15
+ )
16
+ end
17
+
18
+ describe "#from_source" do
19
+ subject { described_class.from_source source }
20
+
21
+ it "builds a collection of presenters that all implement the interface" do
22
+ expect(subject).
23
+ to be_an(Array).
24
+ and all(
25
+ be_a(Communications::Base).
26
+ and respond_to(:success?).
27
+ and respond_to(:processed?).
28
+ and respond_to(:inbound?).
29
+ and respond_to(:fields))
30
+ end
31
+ end
32
+
33
+ describe "#build" do
34
+ subject { described_class.build(record) }
35
+
36
+ [[:log_entry, Communications::LogEntry],
37
+ [:source, Communications::HppSource],
38
+ [:notification, Communications::AdyenNotification]
39
+ ].each do |assigned_name, presenter|
40
+ context "when presented object is a #{assigned_name.to_s.humanize}" do
41
+ let(:record) { send assigned_name }
42
+
43
+ it "creates a presenter using #{presenter}" do
44
+ is_expected.to be_a presenter
45
+ end
46
+ end
47
+ end
48
+
49
+ context "when a presenter cannot be found" do
50
+ let(:record) { Object.new }
51
+
52
+ it "fails" do
53
+ expect { subject }.to raise_error(
54
+ RuntimeError,
55
+ /Couldn't map to a communication type/)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,76 @@
1
+ require "spec_helper"
2
+ require "support/shared_contexts/mock_adyen_api"
3
+
4
+ module Spree
5
+ describe Gateway::AdyenHPP do
6
+ let(:hpp_source) { create :hpp_source, psp_reference: "9999" }
7
+ let(:gateway) { described_class.new }
8
+
9
+ include_context "mock adyen api", success: true
10
+
11
+ shared_examples "delayed gateway action" do
12
+ context "when the action succeeds" do
13
+ include_context "mock adyen api", success: true
14
+
15
+ it { is_expected.to be_a ::ActiveMerchant::Billing::Response }
16
+
17
+ it "returns the orginal psp ref as an authorization" do
18
+ expect(subject.authorization).to eq "9999"
19
+ end
20
+ end
21
+
22
+ context "when the action fails" do
23
+ include_context(
24
+ "mock adyen api",
25
+ success: false,
26
+ fault_message: "Should fail")
27
+
28
+ it "has a response that contains the failure message" do
29
+ expect(subject.success?).to be false
30
+ expect(subject.message).to eq "Should fail"
31
+ end
32
+ end
33
+ end
34
+
35
+ describe ".capture" do
36
+ subject { gateway.capture(2000, "9999", currency: "EUR") }
37
+ include_examples "delayed gateway action"
38
+ end
39
+
40
+ describe ".credit" do
41
+ subject { gateway.credit(2000, "9999", currency: "EUR") }
42
+ include_examples "delayed gateway action"
43
+ end
44
+
45
+ describe ".cancel" do
46
+ subject { gateway.cancel("9999") }
47
+ include_examples "delayed gateway action"
48
+ end
49
+
50
+ describe ".authorize" do
51
+ subject { gateway.authorize 2000, hpp_source, currency: "EUR" }
52
+ it { is_expected.to be_a ActiveMerchant::Billing::Response }
53
+ end
54
+
55
+ context "calculate ship_before_date" do
56
+ let(:test_time) { Time.local(2015, 9, 1, 12, 0, 0) }
57
+
58
+ context "days_to_ship has been set" do
59
+ it "returns tomorrow" do
60
+ Timecop.freeze(test_time) do
61
+ expect(subject.ship_before_date).to eq Time.local(2015, 9, 2, 12, 0, 0)
62
+ end
63
+ end
64
+ end
65
+
66
+ context "days_to_ship has not been set" do
67
+ it "returns date days_to_ship in the future" do
68
+ subject.preferred_days_to_ship = 3
69
+ Timecop.freeze(test_time) do
70
+ expect(subject.ship_before_date).to eq Time.local(2015, 9, 4, 12, 0, 0)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end