defra_ruby_mocks 1.0.0 → 1.1.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.
data/spec/examples.txt CHANGED
@@ -1,30 +1,56 @@
1
- example_id | status | run_time |
2
- ---------------------------------------------------------- | ------ | --------------- |
3
- ./spec/defra_ruby_mocks_spec.rb[1:1:1] | passed | 0.00386 seconds |
4
- ./spec/defra_ruby_mocks_spec.rb[1:2:1:1] | passed | 0.0002 seconds |
5
- ./spec/defra_ruby_mocks_spec.rb[1:2:2:1] | passed | 0.00229 seconds |
6
- ./spec/lib/configuration_spec.rb[1:1:1:1] | passed | 0.00235 seconds |
7
- ./spec/lib/configuration_spec.rb[1:1:2:1] | passed | 0.00021 seconds |
8
- ./spec/lib/configuration_spec.rb[1:1:3:1] | passed | 0.00021 seconds |
9
- ./spec/lib/configuration_spec.rb[1:1:4:1] | passed | 0.00018 seconds |
10
- ./spec/lib/configuration_spec.rb[1:2:1:1] | passed | 0.00017 seconds |
11
- ./spec/lib/configuration_spec.rb[1:2:2:1] | passed | 0.00016 seconds |
12
- ./spec/lib/configuration_spec.rb[1:2:3:1] | passed | 0.00015 seconds |
13
- ./spec/lib/configuration_spec.rb[1:2:4:1] | passed | 0.00012 seconds |
14
- ./spec/requests/company_spec.rb[1:1:1:1] | passed | 0.06356 seconds |
15
- ./spec/requests/company_spec.rb[1:1:2:1] | passed | 0.00938 seconds |
16
- ./spec/requests/company_spec.rb[1:1:3:1:1] | passed | 0.00503 seconds |
17
- ./spec/requests/company_spec.rb[1:1:3:2:1] | passed | 0.00478 seconds |
18
- ./spec/requests/company_spec.rb[1:2:1] | passed | 0.01834 seconds |
19
- ./spec/services/companies_house_service_spec.rb[1:1:1:1] | passed | 0.0002 seconds |
20
- ./spec/services/companies_house_service_spec.rb[1:1:2:1:1] | passed | 0.0001 seconds |
21
- ./spec/services/companies_house_service_spec.rb[1:1:2:2:1] | passed | 0.00009 seconds |
22
- ./spec/services/companies_house_service_spec.rb[1:1:2:3:1] | passed | 0.00012 seconds |
23
- ./spec/services/companies_house_service_spec.rb[1:1:2:4:1] | passed | 0.00009 seconds |
24
- ./spec/services/companies_house_service_spec.rb[1:1:2:5:1] | passed | 0.0001 seconds |
25
- ./spec/services/companies_house_service_spec.rb[1:1:2:6:1] | passed | 0.0001 seconds |
26
- ./spec/services/companies_house_service_spec.rb[1:1:2:7:1] | passed | 0.00013 seconds |
27
- ./spec/services/companies_house_service_spec.rb[1:1:2:8:1] | passed | 0.00012 seconds |
28
- ./spec/services/companies_house_service_spec.rb[1:1:2:9:1] | passed | 0.00012 seconds |
29
- ./spec/services/companies_house_service_spec.rb[1:1:3:1:1] | passed | 0.00012 seconds |
30
- ./spec/services/companies_house_service_spec.rb[1:1:3:2:1] | passed | 0.00015 seconds |
1
+ example_id | status | run_time |
2
+ ------------------------------------------------------------- | ------ | --------------- |
3
+ ./spec/defra_ruby_mocks_spec.rb[1:1:1] | passed | 0.00552 seconds |
4
+ ./spec/defra_ruby_mocks_spec.rb[1:2:1:1] | passed | 0.00013 seconds |
5
+ ./spec/defra_ruby_mocks_spec.rb[1:2:2:1] | passed | 0.00016 seconds |
6
+ ./spec/lib/configuration_spec.rb[1:1:1:1] | passed | 0.00009 seconds |
7
+ ./spec/lib/configuration_spec.rb[1:1:2:1] | passed | 0.00009 seconds |
8
+ ./spec/lib/configuration_spec.rb[1:1:3:1] | passed | 0.0001 seconds |
9
+ ./spec/lib/configuration_spec.rb[1:1:4:1] | passed | 0.00008 seconds |
10
+ ./spec/lib/configuration_spec.rb[1:2:1:1] | passed | 0.00238 seconds |
11
+ ./spec/lib/configuration_spec.rb[1:2:2:1] | passed | 0.00009 seconds |
12
+ ./spec/lib/configuration_spec.rb[1:2:3:1] | passed | 0.0001 seconds |
13
+ ./spec/lib/configuration_spec.rb[1:2:4:1] | passed | 0.00011 seconds |
14
+ ./spec/requests/company_spec.rb[1:1:1:1] | passed | 0.00478 seconds |
15
+ ./spec/requests/company_spec.rb[1:1:2:1] | passed | 0.065 seconds |
16
+ ./spec/requests/company_spec.rb[1:1:3:1:1] | passed | 0.00475 seconds |
17
+ ./spec/requests/company_spec.rb[1:1:3:2:1] | passed | 0.01227 seconds |
18
+ ./spec/requests/company_spec.rb[1:2:1] | passed | 0.0137 seconds |
19
+ ./spec/requests/worldpay_spec.rb[1:1:1:1:1] | passed | 0.02505 seconds |
20
+ ./spec/requests/worldpay_spec.rb[1:1:1:2:1] | passed | 0.00472 seconds |
21
+ ./spec/requests/worldpay_spec.rb[1:1:2:1:1] | passed | 0.00564 seconds |
22
+ ./spec/requests/worldpay_spec.rb[1:1:2:2:1:1] | passed | 0.00433 seconds |
23
+ ./spec/requests/worldpay_spec.rb[1:2:1:1] | passed | 0.00355 seconds |
24
+ ./spec/requests/worldpay_spec.rb[1:2:2:1] | passed | 0.00297 seconds |
25
+ ./spec/services/companies_house_service_spec.rb[1:1:1:1] | passed | 0.00013 seconds |
26
+ ./spec/services/companies_house_service_spec.rb[1:1:2:1:1] | passed | 0.00009 seconds |
27
+ ./spec/services/companies_house_service_spec.rb[1:1:2:2:1] | passed | 0.00009 seconds |
28
+ ./spec/services/companies_house_service_spec.rb[1:1:2:3:1] | passed | 0.00011 seconds |
29
+ ./spec/services/companies_house_service_spec.rb[1:1:2:4:1] | passed | 0.00009 seconds |
30
+ ./spec/services/companies_house_service_spec.rb[1:1:2:5:1] | passed | 0.00009 seconds |
31
+ ./spec/services/companies_house_service_spec.rb[1:1:2:6:1] | passed | 0.00009 seconds |
32
+ ./spec/services/companies_house_service_spec.rb[1:1:2:7:1] | passed | 0.0001 seconds |
33
+ ./spec/services/companies_house_service_spec.rb[1:1:2:8:1] | passed | 0.00009 seconds |
34
+ ./spec/services/companies_house_service_spec.rb[1:1:2:9:1] | passed | 0.00009 seconds |
35
+ ./spec/services/companies_house_service_spec.rb[1:1:3:1:1] | passed | 0.0001 seconds |
36
+ ./spec/services/companies_house_service_spec.rb[1:1:3:2:1] | passed | 0.00012 seconds |
37
+ ./spec/services/worldpay_request_service_spec.rb[1:1:1:1] | passed | 0.00014 seconds |
38
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:1] | passed | 0.00233 seconds |
39
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:2] | passed | 0.00076 seconds |
40
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:3:1] | passed | 0.0005 seconds |
41
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:3:2] | passed | 0.00047 seconds |
42
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:4:1] | passed | 0.0005 seconds |
43
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:4:2] | passed | 0.0005 seconds |
44
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:4:3] | passed | 0.00084 seconds |
45
+ ./spec/services/worldpay_request_service_spec.rb[1:1:2:1:5:1] | passed | 0.00056 seconds |
46
+ ./spec/services/worldpay_request_service_spec.rb[1:1:3:1] | passed | 0.00032 seconds |
47
+ ./spec/services/worldpay_response_service_spec.rb[1:1:1:1:1] | passed | 0.00076 seconds |
48
+ ./spec/services/worldpay_response_service_spec.rb[1:1:1:1:2] | passed | 0.00083 seconds |
49
+ ./spec/services/worldpay_response_service_spec.rb[1:1:1:1:3] | passed | 0.0009 seconds |
50
+ ./spec/services/worldpay_response_service_spec.rb[1:1:1:1:4] | passed | 0.00081 seconds |
51
+ ./spec/services/worldpay_response_service_spec.rb[1:1:1:2:1] | passed | 0.00063 seconds |
52
+ ./spec/services/worldpay_response_service_spec.rb[1:1:2:1:1] | passed | 0.02869 seconds |
53
+ ./spec/services/worldpay_response_service_spec.rb[1:1:2:1:2] | passed | 0.0011 seconds |
54
+ ./spec/services/worldpay_response_service_spec.rb[1:1:2:1:3] | passed | 0.00111 seconds |
55
+ ./spec/services/worldpay_response_service_spec.rb[1:1:2:1:4] | passed | 0.00138 seconds |
56
+ ./spec/services/worldpay_response_service_spec.rb[1:1:2:2:1] | passed | 0.00091 seconds |
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0"?>
2
+ <!DOCTYPE paymentService PUBLIC "-//WorldPay/DTD WorldPay PaymentService v1/EN" "http://dtd.worldpay.com/paymentService_v1.dtd">
3
+ <paymentService version="1.4">
4
+ <submit>
5
+ </submit>
6
+ </paymentService>
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0"?>
2
+ <!DOCTYPE paymentService PUBLIC "-//WorldPay/DTD WorldPay PaymentService v1/EN" "http://dtd.worldpay.com/paymentService_v1.dtd">
3
+ <paymentService version="1.4" merchantCode="MERCHME">
4
+ <submit>
5
+ <order orderCode="1577726052">
6
+ <description>Your Waste Carrier Registration CBDU2</description>
7
+ <amount currencyCode="GBP" value="10500" exponent="2"/>
8
+ <orderContent>Waste Carrier Registration renewal: CBDU2 for Booth Mcdaniel Associates</orderContent>
9
+ <paymentMethodMask>
10
+ <include code="VISA-SSL"/>
11
+ <include code="MAESTRO-SSL"/>
12
+ <include code="ECMC-SSL"/>
13
+ </paymentMethodMask>
14
+ <shopper>
15
+ <shopperEmailAddress>vucij@example.com</shopperEmailAddress>
16
+ </shopper>
17
+ <billingAddress>
18
+ <address>
19
+ <firstName>Bell</firstName>
20
+ <lastName>Cruz</lastName>
21
+ <address1>HARMSEN GROUP DEANERY ROAD</address1>
22
+ <address2/>
23
+ <postalCode>BS1 5AH</postalCode>
24
+ <city>BRISTOL</city>
25
+ <countryCode>GB</countryCode>
26
+ </address>
27
+ </billingAddress>
28
+ </order>
29
+ </submit>
30
+ </paymentService>
@@ -2,14 +2,14 @@
2
2
 
3
3
  require "rails_helper"
4
4
 
5
- module DefraRuby
5
+ module DefraRubyMocks
6
6
  RSpec.describe "Company", type: :request do
7
+ after(:all) { Helpers::Configuration.reset_for_tests }
7
8
 
8
9
  let(:path) { "/defra_ruby_mocks/company" }
9
10
 
10
11
  context "when mocks are enabled" do
11
- before(:all) { Helpers::Configuration.prep_for_tests }
12
- after(:all) { Helpers::Configuration.reset_for_tests }
12
+ before(:each) { Helpers::Configuration.prep_for_tests }
13
13
 
14
14
  context "when the company number is 99999999 for not found" do
15
15
  let(:company_number) { "99999999" }
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ module DefraRubyMocks
6
+ RSpec.describe "Worldpay", type: :request do
7
+ after(:all) { Helpers::Configuration.reset_for_tests }
8
+
9
+ context "when mocks are enabled" do
10
+ before(:each) do
11
+ Helpers::Configuration.prep_for_tests
12
+ DefraRubyMocks.configure do |config|
13
+ config.worldpay_admin_code = "admincode1"
14
+ config.worldpay_mac_secret = "macsecret1"
15
+ config.worldpay_domain = "http://localhost:3000/defra_ruby_mocks"
16
+ end
17
+ end
18
+
19
+ context "#payments_service" do
20
+ let(:path) { "/defra_ruby_mocks/worldpay/payments-service" }
21
+
22
+ context "and the request is valid" do
23
+ let(:data) { File.read("spec/fixtures/worldpay_request_valid.xml") }
24
+
25
+ it "returns an XML response with a 200 code" do
26
+ get path, {}, "RAW_POST_DATA" => data
27
+
28
+ expect(response.content_type).to eq("application/xml")
29
+ expect(response.code).to eq("200")
30
+ end
31
+ end
32
+
33
+ context "and the request is invalid" do
34
+ let(:data) { File.read("spec/fixtures/worldpay_request_invalid.xml") }
35
+
36
+ it "returns a response with a 500 code" do
37
+ get path, {}, "RAW_POST_DATA" => data
38
+
39
+ expect(response.code).to eq("500")
40
+ end
41
+ end
42
+ end
43
+
44
+ context "#dispatcher" do
45
+ let(:relation) { double(:relation, first: registration) }
46
+ let(:registration) { double(:registration, finance_details: finance_details) }
47
+ let(:finance_details) { double(:finance_details, orders: orders) }
48
+ let(:orders) { double(:orders, order_by: sorted_orders) }
49
+ let(:sorted_orders) { double(:sorted_orders, first: order) }
50
+ let(:order) { double(:order, order_code: "987654", total_amount: 105_00) }
51
+
52
+ let(:path) { "/defra_ruby_mocks/worldpay/dispatcher?successURL=#{CGI.escape(success_url)}" }
53
+
54
+ context "and the request is valid" do
55
+ let(:response_params) { "orderKey=admincode1^^987654&paymentStatus=AUTHORISED&paymentAmount=10500&paymentCurrency=GBP&mac=0ba5271e1ed1b26f9bb428ef7fb536a4&source=WP" }
56
+ let(:success_url) { "http://example.com/fo/12345/worldpay/success" }
57
+
58
+ it "redirects the user with a 300 code" do
59
+ expect(::WasteCarriersEngine::TransientRegistration).to receive(:where) { relation }
60
+
61
+ get path
62
+
63
+ expect(response).to redirect_to("#{success_url}?#{response_params}")
64
+ expect(response.code).to eq("302")
65
+ end
66
+ end
67
+
68
+ context "and the request is invalid" do
69
+ context "because the success url is not in a recognised format" do
70
+ let(:success_url) { "http://example.com/forthewin" }
71
+
72
+ it "returns a response with a 500 code" do
73
+ get path
74
+
75
+ expect(response.code).to eq("500")
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ context "when mocks are disabled" do
83
+ before(:each) { DefraRubyMocks.configuration.enable = false }
84
+
85
+ context "#payments_service" do
86
+ let(:path) { "/defra_ruby_mocks/worldpay/payments-service" }
87
+
88
+ it "cannot load the page" do
89
+ expect { get path }.to raise_error(ActionController::RoutingError)
90
+ end
91
+ end
92
+
93
+ context "#dispatcher" do
94
+ let(:path) { "/defra_ruby_mocks/worldpay/dispatcher" }
95
+
96
+ it "cannot load the page" do
97
+ expect { get path }.to raise_error(ActionController::RoutingError)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ module DefraRubyMocks
6
+ RSpec.describe WorldpayRequestService do
7
+ describe ".run" do
8
+ after(:each) { Helpers::Configuration.reset_for_tests }
9
+
10
+ context "when the mocks config is missing a worldpay domain" do
11
+ it "raises a 'InvalidConfigError'" do
12
+ expect { described_class.run(nil) }.to raise_error InvalidConfigError
13
+ end
14
+ end
15
+
16
+ context "when the XML data is valid" do
17
+ before(:each) do
18
+ DefraRubyMocks.configure do |config|
19
+ config.worldpay_domain = "http://localhost:3000/defra_ruby_mocks"
20
+ end
21
+ end
22
+
23
+ let(:data) { File.read("spec/fixtures/worldpay_request_valid.xml") }
24
+
25
+ context "the result it returns" do
26
+ it "is a hash" do
27
+ expect(described_class.run(data)).to be_an_instance_of(Hash)
28
+ end
29
+
30
+ it "contains 4 values" do
31
+ result = described_class.run(data).length
32
+ expect(result).to eq(4)
33
+ end
34
+
35
+ context "has values extracted from the XML data" do
36
+ it "a merchant code" do
37
+ result = described_class.run(data)[:merchant_code]
38
+
39
+ expect(result).to eq("MERCHME")
40
+ end
41
+
42
+ it "an order code" do
43
+ result = described_class.run(data)[:order_code]
44
+
45
+ expect(result).to eq("1577726052")
46
+ end
47
+ end
48
+
49
+ context "has a generated ID which is" do
50
+ it "10 characters long" do
51
+ result = described_class.run(data)[:id]
52
+
53
+ expect(result.length).to eq(10)
54
+ end
55
+
56
+ it "only made up of the digits 0 to 9" do
57
+ result = described_class.run(data)[:id]
58
+
59
+ expect(result.scan(/\D/).empty?).to be_truthy
60
+ end
61
+
62
+ it "different each time" do
63
+ results = []
64
+ 3.times do
65
+ results << described_class.run(data)[:id]
66
+ end
67
+
68
+ expect(results.uniq.length).to eq(results.length)
69
+ end
70
+ end
71
+
72
+ context "has a url" do
73
+ it "based on the configured domain, and extracted merchant and order codes" do
74
+ result = described_class.run(data)[:url]
75
+
76
+ expect(result).to eq("http://localhost:3000/defra_ruby_mocks/worldpay/dispatcher?OrderKey=MERCHME%5E1577726052")
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ context "when the data is invalid" do
84
+ let(:data) { File.read("spec/fixtures/worldpay_request_invalid.xml") }
85
+
86
+ it "raises an error" do
87
+ expect { described_class.run(data) }.to raise_error StandardError
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ module DefraRubyMocks
6
+ RSpec.describe WorldpayResponseService do
7
+ before(:each) do
8
+ Helpers::Configuration.prep_for_tests
9
+ DefraRubyMocks.configure do |config|
10
+ config.worldpay_admin_code = admin_code
11
+ config.worldpay_merchant_code = merchant_code
12
+ config.worldpay_mac_secret = mac_secret
13
+ end
14
+
15
+ allow(::WasteCarriersEngine::TransientRegistration).to receive(:where) { relation }
16
+ allow(::WasteCarriersEngine::Registration).to receive(:where) { relation }
17
+ end
18
+
19
+ let(:admin_code) { "admincode1" }
20
+ let(:merchant_code) { "merchantcode1" }
21
+ let(:mac_secret) { "mac1" }
22
+ let(:reference) { "12345" }
23
+ let(:order_code) { "54321" }
24
+ let(:order_key) { "#{admin_code}^#{merchant_code}^#{order_code}" }
25
+ let(:order_value) { 105_00 }
26
+
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
+ let(:order) { double(:order, order_code: order_code, total_amount: order_value) }
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
44
+
45
+ let(:query_string) do
46
+ [
47
+ "orderKey=#{order_key}",
48
+ "paymentStatus=AUTHORISED",
49
+ "paymentAmount=#{order_value}",
50
+ "paymentCurrency=GBP",
51
+ "mac=#{mac}",
52
+ "source=WP"
53
+ ].join("&")
54
+ end
55
+
56
+ describe ".run" do
57
+ context "when the request comes from the waste-carriers-front-office" do
58
+ let(:success_url) { "http://example.com/fo/#{reference}/worldpay/success" }
59
+
60
+ context "and is valid" do
61
+ let(:relation) { double(:relation, first: registration) }
62
+
63
+ 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)
67
+ end
68
+
69
+ it "can generate a valid order key" do
70
+ params = parse_for_params(described_class.run(success_url))
71
+
72
+ expect(params["orderKey"]).to eq(order_key)
73
+ end
74
+
75
+ it "can generate a valid mac" do
76
+ params = parse_for_params(described_class.run(success_url))
77
+
78
+ expect(params["mac"]).to eq(mac)
79
+ end
80
+
81
+ it "returns a url in the expected format" do
82
+ expected_response = "#{success_url}?#{query_string}"
83
+
84
+ expect(described_class.run(success_url)).to eq(expected_response)
85
+ end
86
+ end
87
+
88
+ context "but the registration does not exist" do
89
+ let(:relation) { double(:relation, first: nil) }
90
+
91
+ it "causes an error" do
92
+ expect { described_class.run(success_url) }.to raise_error MissingRegistrationError
93
+ end
94
+ end
95
+ end
96
+
97
+ 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
+ let(:success_url) { "http://example.com/your-registration/#{reference}/worldpay/success/54321/NEWREG?locale=en" }
107
+
108
+ context "and is valid" do
109
+ let(:relation) { double(:relation, first: registration) }
110
+
111
+ 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)
115
+ end
116
+
117
+ it "can generate a valid order key" do
118
+ params = parse_for_params(described_class.run(success_url))
119
+
120
+ expect(params["orderKey"]).to eq(order_key)
121
+ end
122
+
123
+ it "can generate a valid mac" do
124
+ params = parse_for_params(described_class.run(success_url))
125
+
126
+ expect(params["mac"]).to eq(mac)
127
+ end
128
+
129
+ it "returns a url in the expected format" do
130
+ expected_response = "#{success_url}&#{query_string}"
131
+
132
+ expect(described_class.run(success_url)).to eq(expected_response)
133
+ end
134
+ end
135
+
136
+ context "but the registration does not exist" do
137
+ let(:relation) { double(:relation, first: nil) }
138
+
139
+ it "causes an error" do
140
+ expect { described_class.run(success_url) }.to raise_error MissingRegistrationError
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end