defra_ruby_mocks 1.1.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +52 -2
  3. data/Rakefile +0 -2
  4. data/app/controllers/defra_ruby_mocks/company_controller.rb +1 -1
  5. data/app/controllers/defra_ruby_mocks/worldpay_controller.rb +33 -12
  6. data/app/services/defra_ruby_mocks/{worldpay_request_service.rb → worldpay_payment_service.rb} +3 -9
  7. data/app/services/defra_ruby_mocks/worldpay_refund_service.rb +37 -0
  8. data/app/services/defra_ruby_mocks/worldpay_request_handler_service.rb +40 -0
  9. data/app/services/defra_ruby_mocks/worldpay_resource_service.rb +55 -0
  10. data/app/services/defra_ruby_mocks/worldpay_response_service.rb +71 -52
  11. data/app/views/defra_ruby_mocks/worldpay/payment_request.xml.erb +4 -0
  12. data/app/views/defra_ruby_mocks/worldpay/refund_request.xml.erb +4 -0
  13. data/app/views/defra_ruby_mocks/worldpay/stuck.html.erb +37 -0
  14. data/lib/defra_ruby_mocks/engine.rb +2 -1
  15. data/lib/defra_ruby_mocks/missing_resource_error.rb +9 -0
  16. data/lib/defra_ruby_mocks/unrecognised_worldpay_request_error.rb +5 -0
  17. data/lib/defra_ruby_mocks/version.rb +1 -1
  18. data/spec/dummy/log/development.log +180 -0
  19. data/spec/dummy/log/test.log +1287 -323
  20. data/spec/examples.txt +99 -56
  21. data/spec/fixtures/{worldpay_request_invalid.xml → payment_request_invalid.xml} +0 -0
  22. data/spec/fixtures/{worldpay_request_valid.xml → payment_request_valid.xml} +0 -0
  23. data/spec/fixtures/refund_request_invalid.xml +6 -0
  24. data/spec/fixtures/refund_request_valid.xml +11 -0
  25. data/spec/fixtures/unrecognised_request.xml +6 -0
  26. data/spec/requests/worldpay_spec.rb +87 -26
  27. data/spec/services/{worldpay_request_service_spec.rb → worldpay_payment_service_spec.rb} +25 -22
  28. data/spec/services/worldpay_refund_service_spec.rb +68 -0
  29. data/spec/services/worldpay_request_handler_service_spec.rb +79 -0
  30. data/spec/services/worldpay_resource_service_spec.rb +112 -0
  31. data/spec/services/worldpay_response_service_spec.rb +185 -58
  32. data/spec/support/helpers/xml_matchers.rb +19 -0
  33. metadata +50 -17
  34. data/app/views/defra_ruby_mocks/worldpay/payments_service.xml.erb +0 -4
  35. data/lib/defra_ruby_mocks/missing_registration_error.rb +0 -9
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ module DefraRubyMocks
6
+ RSpec.describe WorldpayRequestHandlerService do
7
+ describe ".run" do
8
+ context "when a request is made" do
9
+
10
+ let(:merchant_code) { "MERCHME" }
11
+ let(:args) { { merchant_code: merchant_code, xml: xml } }
12
+
13
+ context "and it's for a payment" do
14
+ before do
15
+ allow_any_instance_of(WorldpayPaymentService).to receive(:generate_id) { order_id }
16
+ end
17
+
18
+ let(:xml) { Nokogiri::XML(File.read("spec/fixtures/payment_request_valid.xml")) }
19
+ let(:order_id) { "1234567890" }
20
+ let(:request_type) { { request_type: :payment } }
21
+ let(:response_values) do
22
+ {
23
+ merchant_code: merchant_code,
24
+ order_code: "1577726052",
25
+ id: order_id,
26
+ url: "http://example.com"
27
+ }
28
+ end
29
+
30
+ it "correctly determines the request service to use" do
31
+ expect(WorldpayPaymentService).to receive(:run).with(args) { response_values }
32
+
33
+ described_class.run(xml)
34
+ end
35
+
36
+ it "returns the values the controller needs to handle the request" do
37
+ expect(WorldpayPaymentService).to receive(:run).with(args) { response_values }
38
+
39
+ expect(described_class.run(xml)).to eq(request_type.merge(response_values))
40
+ end
41
+ end
42
+
43
+ context "and it's for a refund" do
44
+ let(:xml) { Nokogiri::XML(File.read("spec/fixtures/refund_request_valid.xml")) }
45
+ let(:request_type) { { request_type: :refund } }
46
+ let(:response_values) do
47
+ {
48
+ merchant_code: merchant_code,
49
+ order_code: "1579644835",
50
+ refund_value: "2500",
51
+ currency_code: "GBP",
52
+ exponent: "2"
53
+ }
54
+ end
55
+
56
+ it "correctly determines the request service to use" do
57
+ expect(WorldpayRefundService).to receive(:run).with(args) { response_values }
58
+
59
+ described_class.run(xml)
60
+ end
61
+
62
+ it "returns the values the controller needs to handle the request" do
63
+ expect(WorldpayRefundService).to receive(:run).with(args) { response_values }
64
+
65
+ expect(described_class.run(xml)).to eq(request_type.merge(response_values))
66
+ end
67
+ end
68
+
69
+ context "but it's not recognised" do
70
+ let(:xml) { Nokogiri::XML(File.read("spec/fixtures/unrecognised_request.xml")) }
71
+
72
+ it "raises a 'UnrecognisedWorldpayRequestError'" do
73
+ expect { described_class.run(xml) }.to raise_error UnrecognisedWorldpayRequestError
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ module DefraRubyMocks
6
+ RSpec.describe WorldpayResourceService do
7
+ before(:each) do
8
+ allow(::WasteCarriersEngine::TransientRegistration).to receive(:where) { transient_relation }
9
+ allow(::WasteCarriersEngine::Registration).to receive(:where) { registration_relation }
10
+ end
11
+
12
+ let(:reference) { "12345" }
13
+ let(:company_name) { "Pay for the thing" }
14
+
15
+ let(:resource) { double(:resource, finance_details: finance_details, company_name: company_name) }
16
+ let(:finance_details) { double(:finance_details, orders: orders) }
17
+ let(:orders) { double(:orders, order_by: sorted_orders) }
18
+ let(:sorted_orders) { double(:sorted_orders, first: order) }
19
+ let(:order) { double(:order) }
20
+
21
+ let(:args) { { reference: reference } }
22
+
23
+ describe ".run" do
24
+
25
+ context "when the resource is a TransientRegistration" do
26
+ let(:transient_relation) { double(:relation, first: resource) }
27
+
28
+ it "will only search transient registrations" do
29
+ described_class.run(args)
30
+
31
+ expect(::WasteCarriersEngine::TransientRegistration).to have_received(:where).with(token: reference)
32
+
33
+ expect(::WasteCarriersEngine::Registration).not_to have_received(:where).with(reg_uuid: reference)
34
+ end
35
+
36
+ it "returns an object with the matching resource" do
37
+ expect(described_class.run(args).resource).to eq(resource)
38
+ end
39
+
40
+ it "returns an object with the expected order" do
41
+ expect(described_class.run(args).order).to eq(order)
42
+ end
43
+
44
+ it "returns an object with the expected company name" do
45
+ expect(described_class.run(args).company_name).to eq(company_name.downcase)
46
+ end
47
+ end
48
+
49
+ context "when the resource is a Registration" do
50
+ let(:transient_relation) { double(:relation, first: nil) }
51
+ let(:registration_relation) { double(:relation, first: resource) }
52
+
53
+ it "will search transient registrations first, then registrations" do
54
+ described_class.run(args)
55
+
56
+ expect(::WasteCarriersEngine::TransientRegistration).to have_received(:where).with(token: reference)
57
+
58
+ expect(::WasteCarriersEngine::Registration).to have_received(:where).with(reg_uuid: reference)
59
+ end
60
+
61
+ it "returns an object with the matching resource" do
62
+ expect(described_class.run(args).resource).to eq(resource)
63
+ end
64
+
65
+ it "returns an object with the expected order" do
66
+ expect(described_class.run(args).order).to eq(order)
67
+ end
68
+
69
+ it "returns an object with the expected company name" do
70
+ expect(described_class.run(args).company_name).to eq(company_name.downcase)
71
+ end
72
+ end
73
+
74
+ context "when the resource is a OrderCopyCardsRegistration" do
75
+ before do
76
+ # Because we do not copy the company name to
77
+ # `OrderCopyCardsRegistration` instances when we create them in WCR
78
+ # we need to locate the orignal registration they are based on. We
79
+ # determine in the class if the 'resource' is an instance of one by
80
+ # comparing the result of resource.class.to_s to
81
+ # "WasteCarriersEngine::OrderCopyCardsRegistration". The problem is
82
+ # when testing 'resource' is actually an instance of
83
+ # `RSpec::Mocks::Double`! So we subvert the call to class on
84
+ # RSpec::Mocks::Double to return "WasteCarriersEngine::OrderCopyCardsRegistration"
85
+ # just in this spec. We can then test that the service does indeed
86
+ # locate the original registration for a company name
87
+ allow_any_instance_of(RSpec::Mocks::Double).to receive(:class)
88
+ .and_return("WasteCarriersEngine::OrderCopyCardsRegistration")
89
+ end
90
+
91
+ let(:copy_card_resource) { double(:resource, finance_details: finance_details, reg_identifier: "CBDU123") }
92
+ let(:transient_relation) { double(:relation, first: copy_card_resource) }
93
+ let(:registration_relation) { double(:relation, first: resource) }
94
+
95
+ it "locates the original registration to grab the company name" do
96
+ expect(described_class.run(args).company_name).to eq(company_name.downcase)
97
+
98
+ expect(::WasteCarriersEngine::Registration).to have_received(:where).with(reg_identifier: "CBDU123")
99
+ end
100
+ end
101
+
102
+ context "when the resource does not exist" do
103
+ let(:transient_relation) { double(:relation, first: nil) }
104
+ let(:registration_relation) { double(:relation, first: nil) }
105
+
106
+ it "causes an error" do
107
+ expect { described_class.run(args) }.to raise_error MissingResourceError
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -3,6 +3,7 @@
3
3
  require "rails_helper"
4
4
 
5
5
  module DefraRubyMocks
6
+
6
7
  RSpec.describe WorldpayResponseService do
7
8
  before(:each) do
8
9
  Helpers::Configuration.prep_for_tests
@@ -12,10 +13,11 @@ module DefraRubyMocks
12
13
  config.worldpay_mac_secret = mac_secret
13
14
  end
14
15
 
15
- allow(::WasteCarriersEngine::TransientRegistration).to receive(:where) { relation }
16
- allow(::WasteCarriersEngine::Registration).to receive(:where) { relation }
16
+ allow(WorldpayResourceService).to receive(:run) { resource }
17
17
  end
18
18
 
19
+ let(:resource) { double(:resource, order: order, company_name: company_name.downcase) }
20
+
19
21
  let(:admin_code) { "admincode1" }
20
22
  let(:merchant_code) { "merchantcode1" }
21
23
  let(:mac_secret) { "mac1" }
@@ -23,29 +25,17 @@ module DefraRubyMocks
23
25
  let(:order_code) { "54321" }
24
26
  let(:order_key) { "#{admin_code}^#{merchant_code}^#{order_code}" }
25
27
  let(:order_value) { 105_00 }
28
+ let(:payment_status) { :AUTHORISED }
29
+ let(:company_name) { "Pay for the thing" }
26
30
 
27
- let(:registration) { double(:registration, finance_details: finance_details) }
28
- let(:finance_details) { double(:finance_details, orders: orders) }
29
- let(:orders) { double(:orders, order_by: sorted_orders) }
30
- let(:sorted_orders) { double(:sorted_orders, first: order) }
31
31
  let(:order) { double(:order, order_code: order_code, total_amount: order_value) }
32
32
 
33
- let(:mac) do
34
- data = [
35
- order_key,
36
- order_value,
37
- "GBP",
38
- "AUTHORISED",
39
- mac_secret
40
- ]
41
-
42
- Digest::MD5.hexdigest(data.join).to_s
43
- end
33
+ let(:mac) { Digest::MD5.hexdigest(mac_data.join).to_s }
44
34
 
45
35
  let(:query_string) do
46
36
  [
47
37
  "orderKey=#{order_key}",
48
- "paymentStatus=AUTHORISED",
38
+ "paymentStatus=#{payment_status}",
49
39
  "paymentAmount=#{order_value}",
50
40
  "paymentCurrency=GBP",
51
41
  "mac=#{mac}",
@@ -53,91 +43,228 @@ module DefraRubyMocks
53
43
  ].join("&")
54
44
  end
55
45
 
46
+ let(:args) do
47
+ {
48
+ success_url: success_url,
49
+ failure_url: failure_url,
50
+ pending_url: pending_url,
51
+ cancel_url: cancel_url,
52
+ error_url: error_url
53
+ }
54
+ end
55
+
56
56
  describe ".run" do
57
57
  context "when the request comes from the waste-carriers-front-office" do
58
58
  let(:success_url) { "http://example.com/fo/#{reference}/worldpay/success" }
59
+ let(:failure_url) { "http://example.com/fo/#{reference}/worldpay/failure" }
60
+ let(:pending_url) { "http://example.com/fo/#{reference}/worldpay/pending" }
61
+ let(:cancel_url) { "http://example.com/fo/#{reference}/worldpay/cancel" }
62
+ let(:error_url) { "http://example.com/fo/#{reference}/worldpay/error" }
59
63
 
60
64
  context "and is valid" do
61
65
  let(:relation) { double(:relation, first: registration) }
66
+ let(:mac_data) { [order_key, order_value, "GBP", payment_status, mac_secret] }
62
67
 
63
68
  it "can extract the reference from the `success_url`" do
64
- described_class.run(success_url)
65
-
66
- expect(::WasteCarriersEngine::TransientRegistration).to have_received(:where).with(token: reference)
69
+ expect(described_class.run(args).reference).to eq(reference)
67
70
  end
68
71
 
69
72
  it "can generate a valid order key" do
70
- params = parse_for_params(described_class.run(success_url))
73
+ expect(described_class.run(args).order_key).to eq(order_key)
74
+ end
75
+
76
+ context "and is for a successful payment" do
77
+ it "can generate a valid mac" do
78
+ puts "Test data #{mac_data.join}"
79
+ expect(described_class.run(args).mac).to eq(mac)
80
+ end
81
+
82
+ it "returns a url in the expected format" do
83
+ expected_response_url = "#{success_url}?#{query_string}"
84
+
85
+ expect(described_class.run(args).url).to eq(expected_response_url)
86
+ end
87
+ end
71
88
 
72
- expect(params["orderKey"]).to eq(order_key)
89
+ context "and is for a rejected payment" do
90
+ let(:payment_status) { :REFUSED }
91
+ let(:company_name) { "Reject for the thing" }
92
+
93
+ it "can generate a valid mac" do
94
+ expect(described_class.run(args).mac).to eq(mac)
95
+ end
96
+
97
+ it "returns a url in the expected format" do
98
+ expected_response_url = "#{failure_url}?#{query_string}"
99
+
100
+ expect(described_class.run(args).url).to eq(expected_response_url)
101
+ end
102
+ end
103
+
104
+ context "and is for a stuck payment" do
105
+ let(:payment_status) { :STUCK }
106
+ let(:company_name) { "Give me a stuck thing" }
107
+
108
+ it "can generate a valid mac" do
109
+ expect(described_class.run(args).mac).to eq(mac)
110
+ end
111
+
112
+ it "returns a status of :STUCK" do
113
+ expect(described_class.run(args).status).to eq(:STUCK)
114
+ end
73
115
  end
74
116
 
75
- it "can generate a valid mac" do
76
- params = parse_for_params(described_class.run(success_url))
117
+ context "and is for a pending payment" do
118
+ let(:payment_status) { :SENT_FOR_AUTHORISATION }
119
+ let(:company_name) { "Pending for the thing" }
120
+
121
+ it "can generate a valid mac" do
122
+ expect(described_class.run(args).mac).to eq(mac)
123
+ end
124
+
125
+ it "returns a url in the expected format" do
126
+ expected_response_url = "#{pending_url}?#{query_string}"
77
127
 
78
- expect(params["mac"]).to eq(mac)
128
+ expect(described_class.run(args).url).to eq(expected_response_url)
129
+ end
79
130
  end
80
131
 
81
- it "returns a url in the expected format" do
82
- expected_response = "#{success_url}?#{query_string}"
132
+ context "and is for a cancelled payment" do
133
+ let(:payment_status) { :CANCELLED }
134
+ let(:company_name) { "Cancel the thing" }
135
+ let(:mac_data) { [order_key, order_value, "GBP", mac_secret] }
83
136
 
84
- expect(described_class.run(success_url)).to eq(expected_response)
137
+ it "can generate a valid mac" do
138
+ expect(described_class.run(args).mac).to eq(mac)
139
+ end
140
+
141
+ it "returns a url in the expected format" do
142
+ expected_response_url = "#{cancel_url}?#{query_string}"
143
+
144
+ expect(described_class.run(args).url).to eq(expected_response_url)
145
+ end
85
146
  end
86
- end
87
147
 
88
- context "but the registration does not exist" do
89
- let(:relation) { double(:relation, first: nil) }
148
+ context "and is for an errored payment" do
149
+ let(:payment_status) { :ERROR }
150
+ let(:company_name) { "Error the thing" }
90
151
 
91
- it "causes an error" do
92
- expect { described_class.run(success_url) }.to raise_error MissingRegistrationError
152
+ it "can generate a valid mac" do
153
+ expect(described_class.run(args).mac).to eq(mac)
154
+ end
155
+
156
+ it "returns a url in the expected format" do
157
+ expected_response_url = "#{error_url}?#{query_string}"
158
+
159
+ expect(described_class.run(args).url).to eq(expected_response_url)
160
+ end
93
161
  end
94
162
  end
95
163
  end
96
164
 
97
165
  context "when the request comes from the waste-carriers-frontend" do
98
- before do
99
- # The service will search transient registrations for a match first
100
- # before then searching for the registration. Hence we need to stub
101
- # `locate_transient_registration()` to allow the service to then
102
- # call `locate_registration()`
103
- allow_any_instance_of(described_class).to receive(:locate_transient_registration).and_return(nil)
104
- end
105
-
106
166
  let(:success_url) { "http://example.com/your-registration/#{reference}/worldpay/success/54321/NEWREG?locale=en" }
167
+ let(:failure_url) { "http://example.com/your-registration/#{reference}/worldpay/failure/54321/NEWREG?locale=en" }
168
+ let(:pending_url) { "http://example.com/your-registration/#{reference}/worldpay/pending/54321/NEWREG?locale=en" }
169
+ let(:cancel_url) { "http://example.com/your-registration/#{reference}/worldpay/cancel/54321/NEWREG?locale=en" }
170
+ let(:error_url) { "http://example.com/your-registration/#{reference}/worldpay/error/54321/NEWREG?locale=en" }
107
171
 
108
172
  context "and is valid" do
109
173
  let(:relation) { double(:relation, first: registration) }
174
+ let(:mac_data) { [order_key, order_value, "GBP", payment_status, mac_secret] }
110
175
 
111
176
  it "can extract the reference from the `success_url`" do
112
- described_class.run(success_url)
113
-
114
- expect(::WasteCarriersEngine::Registration).to have_received(:where).with(reg_uuid: reference)
177
+ expect(described_class.run(args).reference).to eq(reference)
115
178
  end
116
179
 
117
180
  it "can generate a valid order key" do
118
- params = parse_for_params(described_class.run(success_url))
181
+ expect(described_class.run(args).order_key).to eq(order_key)
182
+ end
119
183
 
120
- expect(params["orderKey"]).to eq(order_key)
184
+ context "and is for a successful payment" do
185
+ it "can generate a valid mac" do
186
+ expect(described_class.run(args).mac).to eq(mac)
187
+ end
188
+
189
+ it "returns a url in the expected format" do
190
+ expected_response_url = "#{success_url}&#{query_string}"
191
+
192
+ expect(described_class.run(args).url).to eq(expected_response_url)
193
+ end
121
194
  end
122
195
 
123
- it "can generate a valid mac" do
124
- params = parse_for_params(described_class.run(success_url))
196
+ context "and is for a rejected payment" do
197
+ let(:payment_status) { :REFUSED }
198
+ let(:company_name) { "Reject for the thing" }
199
+
200
+ it "can generate a valid mac" do
201
+ expect(described_class.run(args).mac).to eq(mac)
202
+ end
125
203
 
126
- expect(params["mac"]).to eq(mac)
204
+ it "returns a url in the expected format" do
205
+ expected_response_url = "#{failure_url}&#{query_string}"
206
+
207
+ expect(described_class.run(args).url).to eq(expected_response_url)
208
+ end
127
209
  end
128
210
 
129
- it "returns a url in the expected format" do
130
- expected_response = "#{success_url}&#{query_string}"
211
+ context "and is for a stuck payment" do
212
+ let(:payment_status) { :STUCK }
213
+ let(:company_name) { "Give me a stuck thing" }
214
+
215
+ it "can generate a valid mac" do
216
+ expect(described_class.run(args).mac).to eq(mac)
217
+ end
131
218
 
132
- expect(described_class.run(success_url)).to eq(expected_response)
219
+ it "returns a status of :STUCK" do
220
+ expect(described_class.run(args).status).to eq(:STUCK)
221
+ end
133
222
  end
134
- end
135
223
 
136
- context "but the registration does not exist" do
137
- let(:relation) { double(:relation, first: nil) }
224
+ context "and is for a pending payment" do
225
+ let(:payment_status) { :SENT_FOR_AUTHORISATION }
226
+ let(:company_name) { "Pending for the thing" }
227
+
228
+ it "can generate a valid mac" do
229
+ expect(described_class.run(args).mac).to eq(mac)
230
+ end
231
+
232
+ it "returns a url in the expected format" do
233
+ expected_response_url = "#{pending_url}&#{query_string}"
234
+
235
+ expect(described_class.run(args).url).to eq(expected_response_url)
236
+ end
237
+ end
238
+
239
+ context "and is for a cancelled payment" do
240
+ let(:payment_status) { :CANCELLED }
241
+ let(:company_name) { "Cancel the thing" }
242
+ let(:mac_data) { [order_key, order_value, "GBP", mac_secret] }
243
+
244
+ it "can generate a valid mac" do
245
+ expect(described_class.run(args).mac).to eq(mac)
246
+ end
247
+
248
+ it "returns a url in the expected format" do
249
+ expected_response_url = "#{cancel_url}&#{query_string}"
250
+
251
+ expect(described_class.run(args).url).to eq(expected_response_url)
252
+ end
253
+ end
254
+
255
+ context "and is for an errored payment" do
256
+ let(:payment_status) { :ERROR }
257
+ let(:company_name) { "Error the thing" }
258
+
259
+ it "can generate a valid mac" do
260
+ expect(described_class.run(args).mac).to eq(mac)
261
+ end
262
+
263
+ it "returns a url in the expected format" do
264
+ expected_response_url = "#{error_url}&#{query_string}"
138
265
 
139
- it "causes an error" do
140
- expect { described_class.run(success_url) }.to raise_error MissingRegistrationError
266
+ expect(described_class.run(args).url).to eq(expected_response_url)
267
+ end
141
268
  end
142
269
  end
143
270
  end