active_merchant_ideal 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +2 -2
- data/VERSION +1 -1
- data/active_merchant_ideal.gemspec +3 -2
- data/lib/active_merchant_ideal/acquirers.yml +18 -0
- data/lib/active_merchant_ideal/ideal.rb +57 -17
- data/test/fixtures.yml +1 -1
- data/test/test_active_merchant_ideal.rb +53 -18
- metadata +4 -3
data/README.textile
CHANGED
@@ -90,7 +90,7 @@ h3. First configure the gateway
|
|
90
90
|
Put the following code in, for instance, an initializer:
|
91
91
|
|
92
92
|
<pre>
|
93
|
-
IdealGateway.
|
93
|
+
IdealGateway.acquirer = :ing # Other banks preloaded are :abnamro and :rabobank
|
94
94
|
IdealGateway.merchant_id = '00123456789'
|
95
95
|
IdealGateway.passphrase = 'the_private_key_passphrase'
|
96
96
|
|
@@ -181,7 +181,7 @@ In 2007 this code was refactored as a patch for the ActiveMerchant library, this
|
|
181
181
|
|
182
182
|
In 2009 Fingertips forked the ActiveMerchant library and added an iDEAL gateway (presumable based on the first ActiveMerchant patch) to a new ideal branch.
|
183
183
|
|
184
|
-
In 2010 this code was extracted and converted into a separate gem, so it can be more easily used in combination with the latest version of ActiveMerchant. This library is just an extraction, nothing more and nothing less. There are no fundamental changes between the code from the ideal branch and the code of this gem.
|
184
|
+
In 2010 this code was extracted and converted into a separate gem, so it can be more easily used in combination with the latest version of ActiveMerchant. This library is just an extraction, nothing more and nothing less. There are no fundamental changes between the code from the ideal branch and the code of this gem. Later that year "Sernin van de Krol":http://github/companeidos added support for ABN AMRO.
|
185
185
|
|
186
186
|
h2. Maintainer
|
187
187
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.5
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{active_merchant_ideal}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Soemirno Kartosoewito, Matthijs Kadijk, Aloy Duran, Frank Oxener"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-12-02}
|
13
13
|
s.description = %q{iDEAL payment gateway for ActiveMerchant (see http://www.ideal.nl and http://www.activemerchant.org/)}
|
14
14
|
s.email = %q{frank.oxener@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|
26
26
|
"active_merchant_ideal.gemspec",
|
27
27
|
"init.rb",
|
28
28
|
"lib/active_merchant_ideal.rb",
|
29
|
+
"lib/active_merchant_ideal/acquirers.yml",
|
29
30
|
"lib/active_merchant_ideal/ideal.rb",
|
30
31
|
"lib/active_merchant_ideal/ideal_response.rb",
|
31
32
|
"test/fixtures.yml",
|
@@ -0,0 +1,18 @@
|
|
1
|
+
ing:
|
2
|
+
live: https://ideal.secure-ing.com/ideal/iDeal
|
3
|
+
test: https://idealtest.secure-ing.com/ideal/iDeal
|
4
|
+
|
5
|
+
rabobank:
|
6
|
+
live: https://ideal.rabobank.nl/ideal/iDeal
|
7
|
+
test: https://idealtest.rabobank.nl/ideal/iDeal
|
8
|
+
|
9
|
+
abnamro:
|
10
|
+
live:
|
11
|
+
directory: https://idealm.abnamro.nl/nl/issuerInformation/getIssuerInformation.xml
|
12
|
+
transaction: https://idealm.abnamro.nl/nl/acquirerTrxRegistration/getAcquirerTrxRegistration.xml
|
13
|
+
status: https://idealm.abnamro.nl/nl/acquirerStatusInquiry/getAcquirerStatusInquiry.xml
|
14
|
+
|
15
|
+
test:
|
16
|
+
directory: https://itt.idealdesk.com/ITTEmulatorAcquirer/Directory.aspx
|
17
|
+
transaction: https://itt.idealdesk.com/ITTEmulatorAcquirer/Transaction.aspx
|
18
|
+
status: https://itt.idealdesk.com/ITTEmulatorAcquirer/Status.aspx
|
@@ -20,12 +20,20 @@ module ActiveMerchant #:nodoc:
|
|
20
20
|
API_VERSION = '1.1.0'
|
21
21
|
XML_NAMESPACE = 'http://www.idealdesk.com/Message'
|
22
22
|
|
23
|
+
def self.acquirers
|
24
|
+
config_file = File.dirname(__FILE__) + '/acquirers.yml'
|
25
|
+
File.exists?(config_file) ? YAML.load(File.read(config_file)) : {}
|
26
|
+
end
|
27
|
+
|
23
28
|
# Assigns the global iDEAL merchant id. Make sure to use a string with
|
24
29
|
# leading zeroes if needed.
|
25
30
|
cattr_accessor :merchant_id
|
26
31
|
|
27
32
|
# Assigns the passphrase that should be used for the merchant private_key.
|
28
33
|
cattr_accessor :passphrase
|
34
|
+
|
35
|
+
# Defines the whitespace behaviour, this is different for ABN AMRO. Possible values: :abn, :normal
|
36
|
+
cattr_accessor :whitespace_behaviour
|
29
37
|
|
30
38
|
# Loads the global merchant private_key from disk.
|
31
39
|
def self.private_key_file=(pkey_file)
|
@@ -63,25 +71,39 @@ module ActiveMerchant #:nodoc:
|
|
63
71
|
def self.ideal_certificate_file=(certificate_file)
|
64
72
|
self.ideal_certificate = File.read(certificate_file)
|
65
73
|
end
|
66
|
-
|
74
|
+
|
67
75
|
# Instantiates and assings a OpenSSL::X509::Certificate instance with the
|
68
76
|
# provided iDEAL certificate data.
|
69
77
|
def self.ideal_certificate=(certificate_data)
|
70
78
|
@ideal_certificate = OpenSSL::X509::Certificate.new(certificate_data)
|
71
79
|
end
|
72
|
-
|
80
|
+
|
73
81
|
# Returns the global merchant ideal_certificate.
|
74
82
|
def self.ideal_certificate
|
75
83
|
@ideal_certificate
|
76
84
|
end
|
77
85
|
|
78
86
|
# Assign the test and production urls for your iDeal acquirer.
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
87
|
+
# For ABN AMRO only assign the three separate directory, transaction and status urls.
|
88
|
+
cattr_accessor :live_url, :test_url
|
89
|
+
cattr_accessor :live_directory_url, :test_directory_url
|
90
|
+
cattr_accessor :live_transaction_url, :test_transaction_url
|
91
|
+
cattr_accessor :live_status_url, :test_status_url
|
92
|
+
|
93
|
+
# Set the correct acquirer url based on the specific Bank
|
94
|
+
# Currently supported arguments: :ing, :rabobank, :abnamro
|
95
|
+
def self.acquirer=(acquirer)
|
96
|
+
acquirer = acquirer.to_s
|
97
|
+
if self.acquirers.include?(acquirer)
|
98
|
+
if self.acquirers[acquirer]['live'].is_a?(Hash) #Assume acquirer is abnamro
|
99
|
+
self.acquirers[acquirer].each{|env, url| url.each{|sort,value| self.send("#{env}_#{sort}_url=",value)}}
|
100
|
+
self.whitespace_behaviour = :abnamro
|
101
|
+
else
|
102
|
+
self.live_url = self.acquirers[acquirer]['live']
|
103
|
+
self.test_url = self.acquirers[acquirer]['test']
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
85
107
|
|
86
108
|
# Returns the merchant `subID' being used for this IdealGateway instance.
|
87
109
|
# Defaults to 0.
|
@@ -99,8 +121,8 @@ module ActiveMerchant #:nodoc:
|
|
99
121
|
#
|
100
122
|
# When #test? returns +true+ the IdealGateway.test_url is used, otherwise
|
101
123
|
# the IdealGateway.live_url is used.
|
102
|
-
def acquirer_url
|
103
|
-
|
124
|
+
def acquirer_url(request_type)
|
125
|
+
self.class.send("#{test_or_live}#{sort_request(request_type)}_url")
|
104
126
|
end
|
105
127
|
|
106
128
|
# Sends a directory request to the acquirer and returns an
|
@@ -109,7 +131,7 @@ module ActiveMerchant #:nodoc:
|
|
109
131
|
#
|
110
132
|
# gateway.issuers.list # => [{ :id => '1006', :name => 'ABN AMRO Bank' }, …]
|
111
133
|
def issuers
|
112
|
-
post_data build_directory_request_body, IdealDirectoryResponse
|
134
|
+
post_data acquirer_url(:directory), build_directory_request_body, IdealDirectoryResponse
|
113
135
|
end
|
114
136
|
|
115
137
|
# Starts a purchase by sending an acquirer transaction request for the
|
@@ -157,7 +179,7 @@ module ActiveMerchant #:nodoc:
|
|
157
179
|
#
|
158
180
|
# See the IdealGateway class description for a more elaborate example.
|
159
181
|
def setup_purchase(money, options)
|
160
|
-
post_data build_transaction_request_body(money, options), IdealTransactionResponse
|
182
|
+
post_data acquirer_url(:transaction), build_transaction_request_body(money, options), IdealTransactionResponse
|
161
183
|
end
|
162
184
|
|
163
185
|
# Sends a acquirer status request for the specified +transaction_id+ and
|
@@ -177,13 +199,27 @@ module ActiveMerchant #:nodoc:
|
|
177
199
|
#
|
178
200
|
# See the IdealGateway class description for a more elaborate example.
|
179
201
|
def capture(transaction_id)
|
180
|
-
post_data build_status_request_body(:transaction_id => transaction_id), IdealStatusResponse
|
202
|
+
post_data acquirer_url(:status), build_status_request_body(:transaction_id => transaction_id), IdealStatusResponse
|
181
203
|
end
|
182
204
|
|
183
205
|
private
|
184
206
|
|
185
|
-
def
|
186
|
-
|
207
|
+
def abnamro?
|
208
|
+
self.class.whitespace_behaviour == :abnamro
|
209
|
+
end
|
210
|
+
|
211
|
+
# Set correct mode for acquirer_url
|
212
|
+
def test_or_live
|
213
|
+
test? ? 'test' : 'live'
|
214
|
+
end
|
215
|
+
|
216
|
+
# Determine type of request used by ABN AMRO
|
217
|
+
def sort_request(request_type)
|
218
|
+
request_type && abnamro? ? "_#{request_type.to_s}" : nil
|
219
|
+
end
|
220
|
+
|
221
|
+
def post_data(gateway_url, data, response_klass)
|
222
|
+
response_klass.new(ssl_post(gateway_url, data), :test => test?)
|
187
223
|
end
|
188
224
|
|
189
225
|
# This is the list of charaters that are not supported by iDEAL according
|
@@ -205,10 +241,14 @@ module ActiveMerchant #:nodoc:
|
|
205
241
|
Digest::SHA1.hexdigest(self.class.private_certificate.to_der).upcase
|
206
242
|
end
|
207
243
|
|
244
|
+
def strip_whitespace(str)
|
245
|
+
abnamro? ? str.gsub(/(\f|\n|\r|\t|\v)/m, '') : str.gsub(/\s/m,'')
|
246
|
+
end
|
247
|
+
|
208
248
|
# Creates a +tokenCode+ from the specified +message+.
|
209
249
|
def token_code(message)
|
210
|
-
signature = self.class.private_key.sign(OpenSSL::Digest::SHA1.new, message
|
211
|
-
Base64.encode64(signature)
|
250
|
+
signature = self.class.private_key.sign(OpenSSL::Digest::SHA1.new, strip_whitespace(message))
|
251
|
+
strip_whitespace(Base64.encode64(signature))
|
212
252
|
end
|
213
253
|
|
214
254
|
# Returns a string containing the current UTC time, formatted as per the
|
data/test/fixtures.yml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# You can also paste the contents of the key and certificates here,
|
2
2
|
# if you want to do so remove the “_file” part of the keys.
|
3
3
|
ideal_ing_postbank:
|
4
|
-
|
4
|
+
acquirer: ing
|
5
5
|
merchant_id: ID
|
6
6
|
passphrase: PRIVATE KEY PASSPHRASE
|
7
7
|
private_key_file: |--
|
@@ -10,9 +10,7 @@ module IdealTestCases
|
|
10
10
|
self.private_key = PRIVATE_KEY
|
11
11
|
self.private_certificate = PRIVATE_CERTIFICATE
|
12
12
|
self.ideal_certificate = IDEAL_CERTIFICATE
|
13
|
-
|
14
|
-
self.test_url = "https://idealtest.example.com:443/ideal/iDeal"
|
15
|
-
self.live_url = "https://ideal.example.com:443/ideal/iDeal"
|
13
|
+
self.acquirer = :ing
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
@@ -25,16 +23,34 @@ module IdealTestCases
|
|
25
23
|
:entrance_code => '1234'
|
26
24
|
}
|
27
25
|
|
28
|
-
###
|
29
|
-
#
|
30
|
-
# Actual test cases
|
31
|
-
#
|
32
|
-
|
33
26
|
class ClassMethodsTest < Test::Unit::TestCase
|
34
27
|
def test_merchant_id
|
35
28
|
assert_equal IdealGateway.merchant_id, '123456789'
|
36
29
|
end
|
37
30
|
|
31
|
+
def test_verify_live_url_for_ing
|
32
|
+
ActiveMerchant::Billing::IdealGateway.acquirer = :ing
|
33
|
+
assert_equal 'https://ideal.secure-ing.com/ideal/iDeal', IdealGateway.live_url
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_verify_live_url_for_rabobank
|
37
|
+
ActiveMerchant::Billing::IdealGateway.acquirer = :rabobank
|
38
|
+
assert_equal 'https://ideal.rabobank.nl/ideal/iDeal', IdealGateway.live_url
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_verify_live_urls_for_abnamro
|
42
|
+
ActiveMerchant::Billing::IdealGateway.acquirer = :abnamro
|
43
|
+
assert_equal 'https://idealm.abnamro.nl/nl/issuerInformation/getIssuerInformation.xml', IdealGateway.live_directory_url
|
44
|
+
assert_equal 'https://idealm.abnamro.nl/nl/acquirerTrxRegistration/getAcquirerTrxRegistration.xml', IdealGateway.live_transaction_url
|
45
|
+
assert_equal 'https://idealm.abnamro.nl/nl/acquirerStatusInquiry/getAcquirerStatusInquiry.xml', IdealGateway.live_status_url
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_acquirers
|
49
|
+
assert_equal 'https://ideal.rabobank.nl/ideal/iDeal', IdealGateway.acquirers['rabobank']['live']
|
50
|
+
assert_equal 'https://ideal.secure-ing.com/ideal/iDeal', IdealGateway.acquirers['ing']['live']
|
51
|
+
assert_equal 'https://idealm.abnamro.nl/nl/acquirerTrxRegistration/getAcquirerTrxRegistration.xml', IdealGateway.acquirers['abnamro']['live']['transaction']
|
52
|
+
end
|
53
|
+
|
38
54
|
def test_private_certificate_returns_a_loaded_Certificate_instance
|
39
55
|
assert_equal IdealGateway.private_certificate.to_text,
|
40
56
|
OpenSSL::X509::Certificate.new(PRIVATE_CERTIFICATE).to_text
|
@@ -63,12 +79,16 @@ module IdealTestCases
|
|
63
79
|
|
64
80
|
def test_returns_the_test_url_when_in_the_test_env
|
65
81
|
@gateway.stubs(:test?).returns(true)
|
66
|
-
assert_equal IdealGateway.
|
82
|
+
assert_equal IdealGateway.test_directory_url, @gateway.send(:acquirer_url,:directory)
|
83
|
+
assert_equal IdealGateway.test_transaction_url, @gateway.send(:acquirer_url,:transaction)
|
84
|
+
assert_equal IdealGateway.test_status_url, @gateway.send(:acquirer_url,:status)
|
67
85
|
end
|
68
86
|
|
69
87
|
def test_returns_the_live_url_when_not_in_the_test_env
|
70
88
|
@gateway.stubs(:test?).returns(false)
|
71
|
-
assert_equal IdealGateway.
|
89
|
+
assert_equal IdealGateway.live_directory_url, @gateway.send(:acquirer_url,:directory)
|
90
|
+
assert_equal IdealGateway.live_transaction_url, @gateway.send(:acquirer_url,:transaction)
|
91
|
+
assert_equal IdealGateway.live_status_url, @gateway.send(:acquirer_url,:status)
|
72
92
|
end
|
73
93
|
|
74
94
|
def test_returns_created_at_timestamp
|
@@ -112,6 +132,7 @@ module IdealTestCases
|
|
112
132
|
end
|
113
133
|
|
114
134
|
def test_token_code_generation
|
135
|
+
IdealGateway.whitespace_behaviour = nil
|
115
136
|
message = "Top\tsecret\tman.\nI could tell you, but then I'd have to kill you…"
|
116
137
|
stripped_message = message.gsub(/\s/m, '')
|
117
138
|
|
@@ -124,15 +145,29 @@ module IdealTestCases
|
|
124
145
|
assert_equal encoded_signature, @gateway.send(:token_code, message)
|
125
146
|
end
|
126
147
|
|
148
|
+
def test_token_code_generation_on_abn_amro
|
149
|
+
IdealGateway.whitespace_behaviour = :abnamro
|
150
|
+
message = "Top\tsecret\tman.\nI could tell you, but\r then I'd have to kill you…"
|
151
|
+
stripped_message = message.gsub(/(\f|\n|\r|\t|\v)/m, '')
|
152
|
+
|
153
|
+
sha1 = OpenSSL::Digest::SHA1.new
|
154
|
+
OpenSSL::Digest::SHA1.stubs(:new).returns(sha1)
|
155
|
+
|
156
|
+
signature = IdealGateway.private_key.sign(sha1, stripped_message)
|
157
|
+
encoded_signature = Base64.encode64(signature).strip.gsub(/\n/, '')
|
158
|
+
|
159
|
+
assert_equal encoded_signature, @gateway.send(:token_code, message)
|
160
|
+
end
|
161
|
+
|
127
162
|
def test_posts_data_with_ssl_to_acquirer_url_and_return_the_correct_response
|
128
163
|
IdealResponse.expects(:new).with('response', :test => true)
|
129
|
-
@gateway.expects(:ssl_post).with(@gateway.acquirer_url, 'data').returns('response')
|
130
|
-
@gateway.send(:post_data, 'data', IdealResponse)
|
164
|
+
@gateway.expects(:ssl_post).with(@gateway.acquirer_url(:directory), 'data').returns('response')
|
165
|
+
@gateway.send(:post_data, @gateway.acquirer_url(:directory), 'data', IdealResponse)
|
131
166
|
|
132
167
|
@gateway.stubs(:test?).returns(false)
|
133
168
|
IdealResponse.expects(:new).with('response', :test => false)
|
134
|
-
@gateway.expects(:ssl_post).with(@gateway.acquirer_url, 'data').returns('response')
|
135
|
-
@gateway.send(:post_data, 'data', IdealResponse)
|
169
|
+
@gateway.expects(:ssl_post).with(@gateway.acquirer_url(:directory), 'data').returns('response')
|
170
|
+
@gateway.send(:post_data, @gateway.acquirer_url(:directory), 'data', IdealResponse)
|
136
171
|
end
|
137
172
|
end
|
138
173
|
|
@@ -387,7 +422,7 @@ module IdealTestCases
|
|
387
422
|
|
388
423
|
def test_returns_a_list_with_only_one_issuer
|
389
424
|
@gateway.stubs(:build_directory_request_body).returns('the request body')
|
390
|
-
@gateway.expects(:ssl_post).with(@gateway.acquirer_url, 'the request body').returns(DIRECTORY_RESPONSE_WITH_ONE_ISSUER)
|
425
|
+
@gateway.expects(:ssl_post).with(@gateway.acquirer_url(:directory), 'the request body').returns(DIRECTORY_RESPONSE_WITH_ONE_ISSUER)
|
391
426
|
|
392
427
|
expected_issuers = [{ :id => '1006', :name => 'ABN AMRO Bank' }]
|
393
428
|
|
@@ -398,7 +433,7 @@ module IdealTestCases
|
|
398
433
|
|
399
434
|
def test_returns_list_of_issuers_from_response
|
400
435
|
@gateway.stubs(:build_directory_request_body).returns('the request body')
|
401
|
-
@gateway.expects(:ssl_post).with(@gateway.acquirer_url, 'the request body').returns(DIRECTORY_RESPONSE_WITH_MULTIPLE_ISSUERS)
|
436
|
+
@gateway.expects(:ssl_post).with(@gateway.acquirer_url(:directory), 'the request body').returns(DIRECTORY_RESPONSE_WITH_MULTIPLE_ISSUERS)
|
402
437
|
|
403
438
|
expected_issuers = [
|
404
439
|
{ :id => '1006', :name => 'ABN AMRO Bank' },
|
@@ -419,7 +454,7 @@ module IdealTestCases
|
|
419
454
|
@gateway = IdealGateway.new
|
420
455
|
|
421
456
|
@gateway.stubs(:build_transaction_request_body).with(4321, VALID_PURCHASE_OPTIONS).returns('the request body')
|
422
|
-
@gateway.expects(:ssl_post).with(@gateway.acquirer_url, 'the request body').returns(ACQUIRER_TRANSACTION_RESPONSE)
|
457
|
+
@gateway.expects(:ssl_post).with(@gateway.acquirer_url(:transaction), 'the request body').returns(ACQUIRER_TRANSACTION_RESPONSE)
|
423
458
|
|
424
459
|
@setup_purchase_response = @gateway.setup_purchase(4321, VALID_PURCHASE_OPTIONS)
|
425
460
|
end
|
@@ -494,7 +529,7 @@ module IdealTestCases
|
|
494
529
|
private
|
495
530
|
|
496
531
|
def expects_request_and_returns(str)
|
497
|
-
@gateway.expects(:ssl_post).with(@gateway.acquirer_url, 'the request body').returns(str)
|
532
|
+
@gateway.expects(:ssl_post).with(@gateway.acquirer_url(:status), 'the request body').returns(str)
|
498
533
|
end
|
499
534
|
end
|
500
535
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 5
|
9
|
+
version: 0.1.5
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Soemirno Kartosoewito, Matthijs Kadijk, Aloy Duran, Frank Oxener
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-12-02 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- active_merchant_ideal.gemspec
|
65
65
|
- init.rb
|
66
66
|
- lib/active_merchant_ideal.rb
|
67
|
+
- lib/active_merchant_ideal/acquirers.yml
|
67
68
|
- lib/active_merchant_ideal/ideal.rb
|
68
69
|
- lib/active_merchant_ideal/ideal_response.rb
|
69
70
|
- test/fixtures.yml
|