coop_to_ofx 1.0.1
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/README.rdoc +38 -0
- data/Rakefile +141 -0
- data/bin/coop_to_ofx +60 -0
- data/lib/coop_scraper/base.rb +8 -0
- data/lib/coop_scraper/credit_card.rb +94 -0
- data/lib/coop_scraper/current_account.rb +92 -0
- data/lib/coop_scraper/version.rb +12 -0
- data/lib/coop_scraper.rb +2 -0
- data/lib/ofx/statement/base.rb +53 -0
- data/lib/ofx/statement/credit_card.rb +15 -0
- data/lib/ofx/statement/current_account.rb +14 -0
- data/lib/ofx/statement/output/base.rb +131 -0
- data/lib/ofx/statement/output/builder.rb +76 -0
- data/lib/ofx/statement/output/credit_card.rb +31 -0
- data/lib/ofx/statement/output/current_account.rb +29 -0
- data/lib/ofx/statement/transaction.rb +52 -0
- data/lib/ofx/statement.rb +3 -0
- data/lib/ofx.rb +1 -0
- data/spec/coop_scraper/base_spec.rb +15 -0
- data/spec/coop_scraper/credit_card_spec.rb +229 -0
- data/spec/coop_scraper/current_account_spec.rb +154 -0
- data/spec/fixtures/credit_card/cc_statement_fixture.html +927 -0
- data/spec/fixtures/credit_card/foreign_transaction_fixture.html +447 -0
- data/spec/fixtures/credit_card/interest_transaction_fixture.html +438 -0
- data/spec/fixtures/credit_card/maybe.txt +43 -0
- data/spec/fixtures/credit_card/merchandise_interest_fixture.html +0 -0
- data/spec/fixtures/credit_card/normal_transaction_fixture.html +439 -0
- data/spec/fixtures/credit_card/overlimit_charge_fixture.html +446 -0
- data/spec/fixtures/credit_card/payment_in_transaction_fixture.html +439 -0
- data/spec/fixtures/credit_card/simple_cc_statement.ofx +43 -0
- data/spec/fixtures/credit_card/statement_with_interest_line_fixture.html +452 -0
- data/spec/fixtures/current_account/cash_point_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/current_account_fixture.html +420 -0
- data/spec/fixtures/current_account/current_account_fixture.ofx +83 -0
- data/spec/fixtures/current_account/debit_interest_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/no_transactions_fixture.html +364 -0
- data/spec/fixtures/current_account/normal_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/payment_in_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/service_charge_transaction_fixture.html +372 -0
- data/spec/fixtures/current_account/transfer_transaction_fixture.html +372 -0
- data/spec/ofx/statement/base_spec.rb +116 -0
- data/spec/ofx/statement/credit_card_spec.rb +20 -0
- data/spec/ofx/statement/current_account_spec.rb +20 -0
- data/spec/ofx/statement/output/base_spec.rb +249 -0
- data/spec/ofx/statement/output/builder_spec.rb +38 -0
- data/spec/ofx/statement/output/credit_card_spec.rb +84 -0
- data/spec/ofx/statement/output/current_account_spec.rb +81 -0
- data/spec/ofx/statement/transaction_spec.rb +76 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +36 -0
- metadata +172 -0
@@ -0,0 +1,249 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
|
+
|
3
|
+
describe OFX::Statement::Output::Base do
|
4
|
+
before(:all) do
|
5
|
+
@base = OFX::Statement::Output::Base.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "creating a builder" do
|
9
|
+
it "should pick the right output mechanism for OFX 1 when asked" do
|
10
|
+
@base.create_builder(:ofx1).should be_instance_of(OFX::Statement::Output::Builder::OFX1)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should pick the right output mechanism for OFX 2 when asked" do
|
14
|
+
@base.create_builder(:ofx2).should be_instance_of(OFX::Statement::Output::Builder::OFX2)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should pick OFX 2 by default" do
|
18
|
+
@base.create_builder.should be_instance_of(OFX::Statement::Output::Builder::OFX2)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "outputting OFX" do
|
23
|
+
describe "making OFX datetimes from Time objects" do
|
24
|
+
it "should be able to turn a Time object into a YYYYMMDD string" do
|
25
|
+
@base.time_to_ofx_dta(Time.utc('2009', '02', '03')).should == '20090203'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to turn a Time object into a YYYYMMDDHHMMSS string" do
|
29
|
+
@base.time_to_ofx_dta(Time.utc('2009', '02', '03', '14', '26', '15'), true).should == '20090203142615'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "FITIDs" do
|
34
|
+
it "should use an invocation-persistent store to ensure FITID uniqueness and repeatability" do
|
35
|
+
@base.fitid_hash.should == @base.fitid_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "generating" do
|
39
|
+
before(:each) do
|
40
|
+
@fitid_hash = {}
|
41
|
+
@base.stubs(:fitid_hash).returns(@fitid_hash)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should generate a FITID based on the date of the transaction" do
|
45
|
+
@base.generate_fitid(Time.utc('2009', '2', '3')).should == '200902031'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should generate sequential FITIDs for two transaction on the same date" do
|
49
|
+
@base.generate_fitid(Time.utc('2009', '2', '3')).should == '200902031'
|
50
|
+
@base.generate_fitid(Time.utc('2009', '2', '3')).should == '200902032'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "TRNTYPE" do
|
56
|
+
{
|
57
|
+
:debit => "DEBIT", :credit => "CREDIT", :interest => "INT", :dividend => "DIV",
|
58
|
+
:fee => "FEE", :service_charge => "SRVCHG", :deposit => "DEP", :atm => "ATM",
|
59
|
+
:point_of_sale => "POS", :transfer => "XFER", :cheque => "CHECK", :check => "CHECK",
|
60
|
+
:payment => "PAYMENT", :cash => "CASH", :direct_deposit => "DIRECTDEP",
|
61
|
+
:direct_debit => "DIRECTDEBIT", :repeating_payment => "REPEATPMT",
|
62
|
+
:standing_order => "REPEATPMT", :other => "OTHER"
|
63
|
+
}.each do |input, expected|
|
64
|
+
it "should return #{expected} for a trntype of #{input.inspect}" do
|
65
|
+
@base.generate_trntype(input).should == expected
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "components" do
|
71
|
+
before(:each) do
|
72
|
+
@builder = Builder::XmlMarkup.new
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "OFX wrapper" do
|
76
|
+
it "should be able to generate the correct OFX root element" do
|
77
|
+
@base.ofx_block(@builder)
|
78
|
+
output = Hpricot(@builder.target!)
|
79
|
+
|
80
|
+
output.at('/OFX').should_not be_nil
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should yield a child node builder so that document generation can continue" do
|
84
|
+
@base.ofx_block(@builder) { |node| node.fnord }
|
85
|
+
output = Hpricot(@builder.target!)
|
86
|
+
|
87
|
+
output.at('/OFX/fnord').should_not be_nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "signon block" do
|
92
|
+
before(:each) do
|
93
|
+
@t = Time.utc('2009', '1', '8', '12', '13', '14')
|
94
|
+
@statement = stub('Statement', :server_response_time => @t, :language => 'ENG')
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should be able to generate a sensible Signon Message Set, with Signon response block" do
|
98
|
+
|
99
|
+
@base.signon_block(@builder, @statement)
|
100
|
+
output = Hpricot(@builder.target!)
|
101
|
+
|
102
|
+
output.at('/SIGNONMSGSRSV1/SONRS').should_not be_nil
|
103
|
+
output.at('/SIGNONMSGSRSV1/SONRS/STATUS/CODE').inner_text.should == '0'
|
104
|
+
output.at('/SIGNONMSGSRSV1/SONRS/STATUS/SEVERITY').inner_text.should == 'INFO'
|
105
|
+
output.at('/SIGNONMSGSRSV1/SONRS/DTSERVER').inner_text.should == '20090108121314'
|
106
|
+
output.at('/SIGNONMSGSRSV1/SONRS/LANGUAGE').inner_text.should == 'ENG'
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should yield a child node builder so that document generation can continue" do
|
110
|
+
@base.signon_block(@builder, @statement) { |node| node.fnord }
|
111
|
+
output = Hpricot(@builder.target!)
|
112
|
+
|
113
|
+
output.at('/SIGNONMSGSRSV1/fnord').should_not be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "ledger balance block" do
|
118
|
+
it "should produce the right bits for a ledger balance block" do
|
119
|
+
statement = stub('Statement', :ledger_balance => '350.00', :date => Time.utc('2009', '2', '3'))
|
120
|
+
@base.ledger_balance_block(@builder, statement)
|
121
|
+
output = Hpricot(@builder.target!)
|
122
|
+
|
123
|
+
output.at('/LEDGERBAL/BALAMT').inner_text.should == "350.00"
|
124
|
+
output.at('/LEDGERBAL/DTASOF').inner_text.should == "20090203"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "transaction list block" do
|
129
|
+
before(:each) do
|
130
|
+
@t = Time.utc('2009', '1', '8', '12', '13', '14')
|
131
|
+
@t_end = Time.utc('2009', '1', '9', '12', '13', '14')
|
132
|
+
@statement = stub('Statement', :start_date => @t, :end_date => @t_end)
|
133
|
+
@base.stubs(:fitid_hash).returns({})
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should generate a block with the right values" do
|
137
|
+
@base.transaction_list(@builder, @statement)
|
138
|
+
output = Hpricot(@builder.target!)
|
139
|
+
|
140
|
+
output.at('/BANKTRANLIST/DTSTART').inner_text.should == "20090108"
|
141
|
+
output.at('/BANKTRANLIST/DTEND').inner_text.should == "20090109"
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should should yield for child node generation in the right place" do
|
145
|
+
@base.transaction_list(@builder, @statement) { |child| child.fnord }
|
146
|
+
output = Hpricot(@builder.target!)
|
147
|
+
|
148
|
+
output.at('/BANKTRANLIST/fnord').should_not be_nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "transaction block" do
|
153
|
+
before(:each) do
|
154
|
+
@t = Time.utc('2009', '1', '8', '12', '13', '14')
|
155
|
+
@base.stubs(:fitid_hash).returns({})
|
156
|
+
end
|
157
|
+
|
158
|
+
it "should be able to generate a transaction block from a debit transaction object without currency conversion details" do
|
159
|
+
transaction = OFX::Statement::Transaction.new("-350.00", @t, "A nice thing wot I bought")
|
160
|
+
transaction.stubs(:fitid).returns("PROPER:FIT:ID")
|
161
|
+
@base.transaction_block(@builder, transaction)
|
162
|
+
output = Hpricot(@builder.target!)
|
163
|
+
|
164
|
+
output.at('/STMTTRN/TRNTYPE').inner_text.should == "DEBIT"
|
165
|
+
output.at('/STMTTRN/DTPOSTED').inner_text.should == "20090108"
|
166
|
+
output.at('/STMTTRN/TRNAMT').inner_text.should == "-350.00"
|
167
|
+
output.at('/STMTTRN/FITID').inner_text.should == "PROPER:FIT:ID"
|
168
|
+
output.at('/STMTTRN/NAME').inner_text.should == "A nice thing wot I bought"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should be able to generate a transaction block from a credit transaction object without currency conversion" do
|
172
|
+
transaction = OFX::Statement::Transaction.new("350.00", @t, "A nice sum wot I was given")
|
173
|
+
transaction.stubs(:fitid).returns("PROPER:FIT:ID")
|
174
|
+
@base.transaction_block(@builder, transaction)
|
175
|
+
output = Hpricot(@builder.target!)
|
176
|
+
|
177
|
+
output.at('/STMTTRN/TRNTYPE').inner_text.should == "CREDIT"
|
178
|
+
output.at('/STMTTRN/DTPOSTED').inner_text.should == "20090108"
|
179
|
+
output.at('/STMTTRN/TRNAMT').inner_text.should == "350.00"
|
180
|
+
output.at('/STMTTRN/FITID').inner_text.should == "PROPER:FIT:ID"
|
181
|
+
output.at('/STMTTRN/NAME').inner_text.should == "A nice sum wot I was given"
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should be able to generate a transaction block from a debit transaction object with a memo / currency conversion info" do
|
185
|
+
transaction = OFX::Statement::Transaction.new("-350.00", @t, "A nice thing wot I bought", {:memo => "plenty USD wonga"})
|
186
|
+
transaction.stubs(:fitid).returns("PROPER:FIT:ID")
|
187
|
+
@base.transaction_block(@builder, transaction)
|
188
|
+
output = Hpricot(@builder.target!)
|
189
|
+
|
190
|
+
output.at('/STMTTRN/TRNTYPE').inner_text.should == "DEBIT"
|
191
|
+
output.at('/STMTTRN/DTPOSTED').inner_text.should == "20090108"
|
192
|
+
output.at('/STMTTRN/TRNAMT').inner_text.should == "-350.00"
|
193
|
+
output.at('/STMTTRN/FITID').inner_text.should == "PROPER:FIT:ID"
|
194
|
+
output.at('/STMTTRN/NAME').inner_text.should == "A nice thing wot I bought"
|
195
|
+
output.at('/STMTTRN/MEMO').inner_text.should == "plenty USD wonga"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "serialising" do
|
201
|
+
before(:each) do
|
202
|
+
@base.stubs(:ofx_block)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should pass through format correctly" do
|
206
|
+
stub_builder = stub('Builder')
|
207
|
+
stub_builder.stubs(:ofx_stanza!)
|
208
|
+
stub_builder.stubs(:target!)
|
209
|
+
@base.expects(:create_builder).with(:format).returns(stub_builder)
|
210
|
+
|
211
|
+
@base.serialise(nil, :format)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should invoke ofx_stanza! on the builder" do
|
215
|
+
mock_builder = mock('Builder')
|
216
|
+
@base.stubs(:create_builder).with(:format).returns(mock_builder)
|
217
|
+
mock_builder.stubs(:target!)
|
218
|
+
|
219
|
+
mock_builder.expects(:ofx_stanza!)
|
220
|
+
|
221
|
+
@base.serialise(nil, :format)
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should invoke ofx_block passing in builder" do
|
225
|
+
stub_builder = stub('Builder')
|
226
|
+
@base.stubs(:create_builder).with(:format).returns(stub_builder)
|
227
|
+
stub_builder.stubs(:ofx_stanza!)
|
228
|
+
stub_builder.stubs(:target!)
|
229
|
+
|
230
|
+
@base.expects(:ofx_block).with(stub_builder)
|
231
|
+
|
232
|
+
|
233
|
+
@base.serialise(:statement, :format)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should return a string" do
|
237
|
+
stub_builder = stub('Builder')
|
238
|
+
@base.stubs(:create_builder).with(:format).returns(stub_builder)
|
239
|
+
@base.stubs(:ofx_block).with(stub_builder)
|
240
|
+
stub_builder.stubs(:ofx_stanza!)
|
241
|
+
|
242
|
+
stub_builder.expects(:target!).returns("output")
|
243
|
+
|
244
|
+
|
245
|
+
@base.serialise(:statement, :format).should == "output"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
|
+
|
3
|
+
describe OFX::Statement::Output::Builder do
|
4
|
+
describe OFX::Statement::Output::Builder::OFX2 do
|
5
|
+
it "should generate the proper OFX 2 XML declaration stuff when asked" do
|
6
|
+
builder = OFX::Statement::Output::Builder::OFX2.new(:indent => 2)
|
7
|
+
builder.ofx_stanza!
|
8
|
+
result = builder.target!
|
9
|
+
result.should =~ /<\?xml version="1.0" encoding="UTF-8"\?>/
|
10
|
+
result.should =~ /<\?OFX (?:(?:OLDFILEUID="NONE"|NEWFILEUID="NONE"|OFXHEADER="200"|VERSION="203"|SECURITY="NONE") ?)+\?>/
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe OFX::Statement::Output::Builder::OFX1 do
|
15
|
+
it "should generate the proper OFX 1 SGML header stuff when asked" do
|
16
|
+
builder = OFX::Statement::Output::Builder::OFX1.new(:indent => 2)
|
17
|
+
builder.ofx_stanza!
|
18
|
+
result = builder.target!
|
19
|
+
result.should == <<-EOH
|
20
|
+
OFXHEADER:100
|
21
|
+
DATA:OFXSGML
|
22
|
+
VERSION:103
|
23
|
+
SECURITY:NONE
|
24
|
+
ENCODING:USASCII
|
25
|
+
CHARSET:NONE
|
26
|
+
COMPRESSION:NONE
|
27
|
+
OLDFILEUID:NONE
|
28
|
+
NEWFILEUID:NONE
|
29
|
+
EOH
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should generate empty tags without the trailing slash" do
|
33
|
+
builder = OFX::Statement::Output::Builder::OFX1.new
|
34
|
+
builder.my_tag
|
35
|
+
builder.target!.should == "<my_tag>"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
|
+
|
3
|
+
describe OFX::Statement::Output::CreditCard do
|
4
|
+
describe "generating OFX" do
|
5
|
+
before(:each) do
|
6
|
+
@output = OFX::Statement::Output::CreditCard.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "components" do
|
10
|
+
before(:each) do
|
11
|
+
@builder = Builder::XmlMarkup.new
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "credit card message set wrapper" do
|
15
|
+
it "should be able to generate the correct OFX root element" do
|
16
|
+
@output.message_set_block(@builder)
|
17
|
+
output = Hpricot(@builder.target!)
|
18
|
+
|
19
|
+
output.at('/CREDITCARDMSGSETV1').should_not be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should yield a child node builder so that document generation can continue" do
|
23
|
+
@output.message_set_block(@builder) { |node| node.fnord }
|
24
|
+
output = Hpricot(@builder.target!)
|
25
|
+
|
26
|
+
output.at('/CREDITCARDMSGSETV1/fnord').should_not be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "credit card statement block" do
|
31
|
+
before(:each) do
|
32
|
+
@statement = stub('Statement', :start_date => Time.utc('2009', '1', '7', '12', '13', '14'),
|
33
|
+
:end_date => Time.utc('2009', '1', '8', '12', '13', '14'),
|
34
|
+
:date => Time.utc('2009', '1', '8', '12', '13', '14'),
|
35
|
+
:currency => 'GBP', :account_number => "1234123412341234",
|
36
|
+
:available_credit => '305.00', :ledger_balance => "-1551.90")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should be able to generate the correct Statement block" do
|
40
|
+
@output.statement_block(@builder, @statement)
|
41
|
+
output = Hpricot(@builder.target!)
|
42
|
+
|
43
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/CURDEF').should_not be_nil
|
44
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/CURDEF').inner_text.should == "GBP"
|
45
|
+
|
46
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/CCACCTFROM').should_not be_nil
|
47
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/CCACCTFROM/ACCTID').inner_text.should == "1234123412341234"
|
48
|
+
|
49
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/LEDGERBAL').should_not be_nil
|
50
|
+
|
51
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/AVAILBAL').should_not be_nil
|
52
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/AVAILBAL/BALAMT').inner_text.should == "305.00"
|
53
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/AVAILBAL/DTASOF').inner_text.should == "20090108"
|
54
|
+
|
55
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/BANKTRANLIST').should_not be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should yield a child node builder so that document generation can continue" do
|
59
|
+
@output.statement_block(@builder, @statement) { |node| node.fnord }
|
60
|
+
output = Hpricot(@builder.target!)
|
61
|
+
|
62
|
+
output.at('/CCSTMTTRNRS/CCSTMTRS/BANKTRANLIST/fnord').should_not be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should be able to generate a correct OFX file" do
|
68
|
+
transaction = OFX::Statement::Transaction.new("-15.00", Time.utc('2009', '1', '5'), "COMP HSE FILE-DOM INTERNET GB")
|
69
|
+
statement = OFX::Statement::CreditCard.new
|
70
|
+
statement.server_response_time = Time.utc('2009', '2', '6', '18', '35', '56')
|
71
|
+
statement.account_number = '1234123412341234'
|
72
|
+
statement.start_date = Time.utc('2009', '1', '1')
|
73
|
+
statement.end_date = Time.utc('2009', '2', '3')
|
74
|
+
statement.date = Time.utc('2009', '2', '3')
|
75
|
+
statement.ledger_balance = "-1551.90"
|
76
|
+
statement.available_credit = "305.00"
|
77
|
+
statement << transaction
|
78
|
+
|
79
|
+
output_doc = Hpricot(@output.serialise(statement))
|
80
|
+
output_doc.search('/OFX/SIGNONMSGSRSV1/SONRS/STATUS/CODE').should_not be_empty
|
81
|
+
output_doc.search('/OFX/SIGNONMSGSRSV1/CREDITCARDMSGSETV1/CCSTMTTRNRS/CCSTMTRS/BANKTRANLIST/STMTTRN/TRNAMT').should_not be_empty
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
2
|
+
|
3
|
+
describe OFX::Statement::Output::CurrentAccount do
|
4
|
+
describe "generating OFX" do
|
5
|
+
before(:each) do
|
6
|
+
@output = OFX::Statement::Output::CurrentAccount.new
|
7
|
+
end
|
8
|
+
describe "components" do
|
9
|
+
before(:each) do
|
10
|
+
@builder = Builder::XmlMarkup.new
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "bank account message set wrapper" do
|
14
|
+
it "should be able to generate the correct bank account message set element" do
|
15
|
+
@output.message_set_block(@builder)
|
16
|
+
output = Hpricot(@builder.target!)
|
17
|
+
|
18
|
+
output.at('/BANKMSGSETV1').should_not be_nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should yield a child node builder so that document generation can continue" do
|
22
|
+
@output.message_set_block(@builder) { |node| node.fnord }
|
23
|
+
output = Hpricot(@builder.target!)
|
24
|
+
|
25
|
+
output.at('/BANKMSGSETV1/fnord').should_not be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "current account statement block" do
|
30
|
+
before(:each) do
|
31
|
+
@statement = stub('Statement', :start_date => Time.utc('2009', '1', '7', '12', '13', '14'),
|
32
|
+
:end_date => Time.utc('2009', '1', '8', '12', '13', '14'),
|
33
|
+
:date => Time.utc('2009', '1', '8', '12', '13', '14'),
|
34
|
+
:currency => 'GBP', :account_number => "12341234",
|
35
|
+
:sort_code => '089273', :ledger_balance => "-1551.90")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be able to generate the correct Statement block" do
|
39
|
+
@output.statement_block(@builder, @statement)
|
40
|
+
output = Hpricot(@builder.target!)
|
41
|
+
|
42
|
+
output.at('/STMTTRNRS/STMTRS/CURDEF').should_not be_nil
|
43
|
+
output.at('/STMTTRNRS/STMTRS/CURDEF').inner_text.should == "GBP"
|
44
|
+
|
45
|
+
output.at('/STMTTRNRS/STMTRS/BANKACCTFROM').should_not be_nil
|
46
|
+
output.at('/STMTTRNRS/STMTRS/BANKACCTFROM/BANKID').inner_text.should == "089273"
|
47
|
+
output.at('/STMTTRNRS/STMTRS/BANKACCTFROM/ACCTID').inner_text.should == "12341234"
|
48
|
+
output.at('/STMTTRNRS/STMTRS/BANKACCTFROM/ACCTTYPE').inner_text.should == "CHECKING"
|
49
|
+
|
50
|
+
output.at('/STMTTRNRS/STMTRS/LEDGERBAL').should_not be_nil
|
51
|
+
|
52
|
+
output.at('/STMTTRNRS/STMTRS/BANKTRANLIST').should_not be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should yield a child node builder so that document generation can continue" do
|
56
|
+
@output.statement_block(@builder, @statement) { |node| node.fnord }
|
57
|
+
output = Hpricot(@builder.target!)
|
58
|
+
|
59
|
+
output.at('/STMTTRNRS/STMTRS/BANKTRANLIST/fnord').should_not be_nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be able to generate a correct OFX file" do
|
64
|
+
transaction = OFX::Statement::Transaction.new("-15.00", Time.utc('2009', '1', '5'), "COMP HSE FILE-DOM INTERNET GB")
|
65
|
+
statement = OFX::Statement::CurrentAccount.new
|
66
|
+
statement.server_response_time = Time.utc('2009', '2', '6', '18', '35', '56')
|
67
|
+
statement.account_number = '12341234'
|
68
|
+
statement.start_date = Time.utc('2009', '1', '1')
|
69
|
+
statement.end_date = Time.utc('2009', '2', '3')
|
70
|
+
statement.date = Time.utc('2009', '2', '3')
|
71
|
+
statement.ledger_balance = "-1551.90"
|
72
|
+
statement.sort_code = "089273"
|
73
|
+
statement << transaction
|
74
|
+
|
75
|
+
output_doc = Hpricot(@output.serialise(statement))
|
76
|
+
output_doc.search('/OFX/SIGNONMSGSRSV1/SONRS/STATUS/CODE').should_not be_empty
|
77
|
+
output_doc.search('/OFX/SIGNONMSGSRSV1/BANKMSGSETV1/STMTTRNRS/STMTRS/BANKTRANLIST/STMTTRN/TRNAMT').should_not be_empty
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe OFX::Statement::Transaction do
|
4
|
+
describe "instantiating" do
|
5
|
+
it "should require amount, date, details, and allow an options hash" do
|
6
|
+
OFX::Statement::Transaction.new("350", Time.now, "Details", {:memo => "Memo"}).should be_instance_of(OFX::Statement::Transaction)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "instances" do
|
11
|
+
before(:each) do
|
12
|
+
@t = Time.now
|
13
|
+
@transaction = OFX::Statement::Transaction.new("350.00", @t, "Details", {:memo => "Memo"})
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should report its amount" do
|
17
|
+
@transaction.amount.should == "350.00"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should report its date" do
|
21
|
+
@transaction.date.should == @t
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should report its name" do
|
25
|
+
@transaction.name.should == "Details"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should report its memo" do
|
29
|
+
@transaction.memo.should == "Memo"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should should allow its statement to be set and retrieved" do
|
33
|
+
@transaction.statement = :statement
|
34
|
+
@transaction.statement.should == :statement
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be able to get a sane FITID" do
|
38
|
+
mock_statement = mock('Statement')
|
39
|
+
@transaction.stubs(:statement).returns(mock_statement)
|
40
|
+
|
41
|
+
mock_statement.expects(:fitid_for).with(@transaction).returns(:sane_fitid)
|
42
|
+
|
43
|
+
@transaction.fitid.should == :sane_fitid
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "memo" do
|
47
|
+
it "should report that it has a memo" do
|
48
|
+
@transaction.has_memo?.should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should report that it does not have a memo" do
|
52
|
+
transaction = OFX::Statement::Transaction.new("350.00", @t, "Details")
|
53
|
+
|
54
|
+
transaction.has_memo?.should be_false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "basic transaction types" do
|
59
|
+
it "should default to :credit for a positive amount" do
|
60
|
+
@transaction.trntype.should == :credit
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should default to debit for a negative amount" do
|
64
|
+
transaction = OFX::Statement::Transaction.new("-350.00", @t, "Details")
|
65
|
+
|
66
|
+
transaction.trntype.should == :debit
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should allow the type to be set in the options hash" do
|
70
|
+
transaction = OFX::Statement::Transaction.new("-350.00", @t, "Details", :trntype => :credit)
|
71
|
+
|
72
|
+
transaction.trntype.should == :credit
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mocha'
|
3
|
+
$:.push(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
4
|
+
Spec::Runner.configure do |config|
|
5
|
+
config.mock_with :mocha
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'ofx'
|
9
|
+
require 'coop_scraper'
|
10
|
+
require 'hpricot'
|
11
|
+
|
12
|
+
def full_fixture_path(fixture_dir, fixture_filename)
|
13
|
+
File.dirname(__FILE__) + "/fixtures/#{fixture_dir}/" + fixture_filename
|
14
|
+
end
|
15
|
+
|
16
|
+
def read_fixture(fixture_filename)
|
17
|
+
File.read(fixture_path(fixture_filename))
|
18
|
+
end
|
19
|
+
|
20
|
+
class Class
|
21
|
+
def publicize_methods
|
22
|
+
saved_private_instance_methods = self.private_instance_methods
|
23
|
+
saved_protected_instance_methods = self.protected_instance_methods
|
24
|
+
self.class_eval do
|
25
|
+
public *saved_private_instance_methods
|
26
|
+
public *saved_protected_instance_methods
|
27
|
+
end
|
28
|
+
|
29
|
+
yield
|
30
|
+
|
31
|
+
self.class_eval do
|
32
|
+
private *saved_private_instance_methods
|
33
|
+
protected *saved_protected_instance_methods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|