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.
Files changed (51) hide show
  1. data/README.rdoc +38 -0
  2. data/Rakefile +141 -0
  3. data/bin/coop_to_ofx +60 -0
  4. data/lib/coop_scraper/base.rb +8 -0
  5. data/lib/coop_scraper/credit_card.rb +94 -0
  6. data/lib/coop_scraper/current_account.rb +92 -0
  7. data/lib/coop_scraper/version.rb +12 -0
  8. data/lib/coop_scraper.rb +2 -0
  9. data/lib/ofx/statement/base.rb +53 -0
  10. data/lib/ofx/statement/credit_card.rb +15 -0
  11. data/lib/ofx/statement/current_account.rb +14 -0
  12. data/lib/ofx/statement/output/base.rb +131 -0
  13. data/lib/ofx/statement/output/builder.rb +76 -0
  14. data/lib/ofx/statement/output/credit_card.rb +31 -0
  15. data/lib/ofx/statement/output/current_account.rb +29 -0
  16. data/lib/ofx/statement/transaction.rb +52 -0
  17. data/lib/ofx/statement.rb +3 -0
  18. data/lib/ofx.rb +1 -0
  19. data/spec/coop_scraper/base_spec.rb +15 -0
  20. data/spec/coop_scraper/credit_card_spec.rb +229 -0
  21. data/spec/coop_scraper/current_account_spec.rb +154 -0
  22. data/spec/fixtures/credit_card/cc_statement_fixture.html +927 -0
  23. data/spec/fixtures/credit_card/foreign_transaction_fixture.html +447 -0
  24. data/spec/fixtures/credit_card/interest_transaction_fixture.html +438 -0
  25. data/spec/fixtures/credit_card/maybe.txt +43 -0
  26. data/spec/fixtures/credit_card/merchandise_interest_fixture.html +0 -0
  27. data/spec/fixtures/credit_card/normal_transaction_fixture.html +439 -0
  28. data/spec/fixtures/credit_card/overlimit_charge_fixture.html +446 -0
  29. data/spec/fixtures/credit_card/payment_in_transaction_fixture.html +439 -0
  30. data/spec/fixtures/credit_card/simple_cc_statement.ofx +43 -0
  31. data/spec/fixtures/credit_card/statement_with_interest_line_fixture.html +452 -0
  32. data/spec/fixtures/current_account/cash_point_transaction_fixture.html +372 -0
  33. data/spec/fixtures/current_account/current_account_fixture.html +420 -0
  34. data/spec/fixtures/current_account/current_account_fixture.ofx +83 -0
  35. data/spec/fixtures/current_account/debit_interest_transaction_fixture.html +372 -0
  36. data/spec/fixtures/current_account/no_transactions_fixture.html +364 -0
  37. data/spec/fixtures/current_account/normal_transaction_fixture.html +372 -0
  38. data/spec/fixtures/current_account/payment_in_transaction_fixture.html +372 -0
  39. data/spec/fixtures/current_account/service_charge_transaction_fixture.html +372 -0
  40. data/spec/fixtures/current_account/transfer_transaction_fixture.html +372 -0
  41. data/spec/ofx/statement/base_spec.rb +116 -0
  42. data/spec/ofx/statement/credit_card_spec.rb +20 -0
  43. data/spec/ofx/statement/current_account_spec.rb +20 -0
  44. data/spec/ofx/statement/output/base_spec.rb +249 -0
  45. data/spec/ofx/statement/output/builder_spec.rb +38 -0
  46. data/spec/ofx/statement/output/credit_card_spec.rb +84 -0
  47. data/spec/ofx/statement/output/current_account_spec.rb +81 -0
  48. data/spec/ofx/statement/transaction_spec.rb +76 -0
  49. data/spec/spec.opts +2 -0
  50. data/spec/spec_helper.rb +36 -0
  51. 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
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format specdoc
@@ -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