def test_hash_entries_with_duplicates(self): entries, _, __ = loader.load_string(""" 2014-08-01 price HOOL 603.10 USD """) hashes, errors = compare.hash_entries(entries) self.assertEqual(1, len(hashes)) entries, _, __ = loader.load_string(""" 2014-08-01 price HOOL 603.10 USD 2014-08-01 price HOOL 603.10 USD 2014-08-01 price HOOL 603.10 USD 2014-08-01 price HOOL 603.10 USD 2014-08-01 price HOOL 603.10 USD """) hashes, errors = compare.hash_entries(entries) self.assertEqual(1, len(hashes))
def test_extract_from_file__min_date(self): entries, _, __ = loader.load_string(""" 2016-02-01 * "A" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-02 * "B" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-03 * "C" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD """) imp = mock.MagicMock() imp.identify = mock.MagicMock(return_value=True) imp.extract = mock.MagicMock(return_value=entries) new_entries, dup_entries = extract.extract_from_file( '/tmp/blabla.ofx', imp, min_date=datetime.date(2016, 2, 2)) self.assertEqual(2, len(new_entries)) self.assertEqual( [datetime.date(2016, 2, 2), datetime.date(2016, 2, 3)], [entry.date for entry in new_entries]) self.assertEqual([], dup_entries)
def setUp(self, entries, _, __): """ ;; Existing file. 2015-01-05 price HDV 75.56 USD 2015-01-23 price HDV 77.34 USD 2015-02-06 price HDV 77.16 USD 2015-02-12 price HDV 78.17 USD 2015-05-01 price HDV 77.48 USD 2015-06-02 price HDV 76.33 USD 2015-06-29 price HDV 73.74 USD 2015-07-06 price HDV 73.79 USD 2015-08-11 price HDV 74.19 USD 2015-09-04 price HDV 68.98 USD """ self.entries = entries # New entries. self.price_entries, _, __ = loader.load_string(""" 2015-01-27 price HDV 76.83 USD 2015-02-06 price HDV 77.16 USD 2015-02-19 price HDV 77.5 USD 2015-06-02 price HDV 76.33 USD 2015-06-19 price HDV 76 USD 2015-07-06 price HDV 73.79 USD 2015-07-31 price HDV 74.64 USD 2015-08-11 price HDV 74.20 USD ;; Different """, dedent=True)
def test_extract_from_file__explicitly_marked_duplicates_entries(self): entries, _, __ = loader.load_string(""" 2016-02-01 * "A" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-02 * "B" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD """) entries[1].meta[extract.DUPLICATE_META] = True imp = mock.MagicMock() imp.identify = mock.MagicMock(return_value=True) imp.extract = mock.MagicMock(return_value=entries) new_entries, dup_entries = extract.extract_from_file( '/tmp/blabla.ofx', imp, []) self.assertEqual(1, len(dup_entries)) self.assertEqual( [datetime.date(2016, 2, 1), datetime.date(2016, 2, 2)], [entry.date for entry in new_entries]) self.assertEqual([datetime.date(2016, 2, 2)], [entry.date for entry in dup_entries])
def test_extract_from_file__ensure_sorted(self): entries, _, __ = loader.load_string(""" 2016-02-03 * "C" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-01 * "A" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-02 * "B" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD """) imp = mock.MagicMock() imp.identify = mock.MagicMock(return_value=True) imp.extract = mock.MagicMock(return_value=entries) new_entries, dup_entries = extract.extract_from_file( '/tmp/blabla.ofx', imp) self.assertEqual(3, len(entries)) self.assertTrue( misc_utils.is_sorted(new_entries, key=lambda entry: entry.date)) self.assertEqual([], dup_entries)
def test_interline_spacing(self): input_text = textwrap.dedent("""\ 2014-01-01 open Assets:Account1 2014-01-01 open Assets:Account2 2014-01-01 open Assets:Cash 2014-06-08 * Assets:Account1 111.00 BEAN Assets:Cash 2014-06-08 * "Narration" Assets:Account1 111.00 BEAN Assets:Cash 2014-06-08 * "Payee" "Narration" Assets:Account2 111.00 BEAN Assets:Cash 2014-10-01 close Assets:Account2 2014-10-11 price BEAN 10 USD 2014-10-12 price BEAN 11 USD 2014-10-13 price BEAN 11 USD """) entries, _, __ = loader.load_string(input_text) oss = io.StringIO() printer.print_entries(entries, file=oss) expected_classes = characterize_spaces(input_text) actual_classes = characterize_spaces(oss.getvalue()) self.assertEqual(expected_classes, actual_classes)
def test_extract_from_file__existing_entries(self): entries, _, __ = loader.load_string(""" 2016-02-01 * "A" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-02 * "B" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-03 * "C" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD 2016-02-04 * "D" Assets:Account1 10.00 USD Assets:Account2 -10.00 USD """) imp = mock.MagicMock() imp.identify = mock.MagicMock(return_value=True) imp.extract = mock.MagicMock(return_value=[entries[1], entries[3]]) new_entries = extract.extract_from_file( '/tmp/blabla.ofx', imp, entries) self.assertEqual(2, len(new_entries)) self.assertEqual([datetime.date(2016, 2, 2), datetime.date(2016, 2, 4)], [entry.date for entry in new_entries]) # Check that the entries have also been marked. marked_entries = [entry for entry in new_entries if extract.DUPLICATE_META in entry.meta] self.assertEqual(new_entries, marked_entries)
def test_generate(self): rv = self.run_with_args(example.main) self.assertTrue(rv.stdout) loaded_entries, errors, _ = loader.load_string( rv.stdout, extra_validations=validation.HARDCORE_VALIDATIONS) self.assertFalse(errors)
def test_get_values_meta__multi(self): entries, _, options_map = loader.load_string(TEST_INPUT) commodity_map = getters.get_commodity_map(entries, options_map) values = getters.get_values_meta(commodity_map, 'name', 'ticker') self.assertEqual({'HOOL': ('Hooli Corp.', 'NYSE:HOOLI'), 'PIPA': ('Pied Piper', None), 'USD': (None, None)}, values)
def test_get_values_meta__single(self): entries, _, options_map = loader.load_string(TEST_INPUT) commodity_map = getters.get_commodity_map(entries, options_map) values = getters.get_values_meta(commodity_map, 'name', default='BLA') self.assertEqual({'USD': 'BLA', 'PIPA': 'Pied Piper', 'HOOL': 'Hooli Corp.'}, values)
def test_get_accounts(self): entries = loader.load_string(TEST_INPUT)[0] accounts = getters.get_accounts(entries) self.assertEqual( { 'Assets:US:Cash', 'Assets:US:Credit-Card', 'Expenses:Grocery', 'Expenses:Coffee', 'Expenses:Restaurant' }, accounts)
def test_get_entry_accounts(self): entries = loader.load_string(TEST_INPUT)[0] accounts = getters.get_entry_accounts( next(entry for entry in entries if isinstance(entry, data.Transaction))) self.assertEqual( {'Assets:US:Cash', 'Expenses:Grocery', 'Expenses:Restaurant'}, accounts)
def test_string_latin1(self): utf8_bytes = textwrap.dedent(""" 2015-01-01 open Assets:Something 2015-05-23 note Assets:Something "¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼ " """).encode('latin1') entries, errors, options_map = loader.load_string(utf8_bytes, encoding='latin1') self.assertFalse(errors)
def test_renamed_plugin_warnings(self, warn): with test_utils.capture('stderr'): entries, errors, options_map = loader.load_string(""" plugin "beancount.ops.auto_accounts" """, dedent=True) self.assertTrue(warn.called) self.assertFalse(errors)
def test_get_account_components(self): entries = loader.load_string(TEST_INPUT)[0] components = getters.get_account_components(entries) expected_components = { 'US', 'Assets', 'Restaurant', 'Grocery', 'Cash', 'Coffee', 'Expenses', 'Credit-Card' } self.assertEqual(sorted(expected_components), components)
def is_processed(variant, input_txns, errors, config, input_txn_text, setup_txns_text, output_txns): input_txns[:], _, _ = load_string(setup_txns_text + input_txn_text) if variant == 'depr': prefix_plugin_text = 'plugin "beancount_interpolate.depreciate" "' + config.strip( '\n') + '"\n' elif variant == 'recur': prefix_plugin_text = 'plugin "beancount_interpolate.recur" "' + config.strip( '\n') + '"\n' elif variant == 'split': prefix_plugin_text = 'plugin "beancount_interpolate.split" "' + config.strip( '\n') + '"\n' elif variant == 'spread': prefix_plugin_text = 'plugin "beancount_interpolate.spread" "' + config.strip( '\n') + '"\n' elif variant == 'all': prefix_plugin_text = 'plugin "beancount_interpolate.depreciate" "' + config.strip( '\n') + '"\n' prefix_plugin_text = prefix_plugin_text + 'plugin "beancount_interpolate.recur" "' + config.strip( '\n') + '"\n' prefix_plugin_text = prefix_plugin_text + 'plugin "beancount_interpolate.split" "' + config.strip( '\n') + '"\n' prefix_plugin_text = prefix_plugin_text + 'plugin "beancount_interpolate.spread" "' + config.strip( '\n') + '"\n' else: raise RuntimeError('Unknown variant: "{}".'.format(variant)) full_text = prefix_plugin_text + setup_txns_text + input_txn_text print( '\nInput (full & raw):\n------------------------------------------------' ) print(full_text + '\n') output_txns[:], errors[:], _ = load_string(full_text) print( '\nOutput (Transactions):\n------------------------------------------------\n' ) for txn in output_txns: print(printer.format_entry(txn)) print( '\nOutput (Errors):\n------------------------------------------------\n' ) for error in errors: print(printer.format_error(error))
def test_hash_entries(self): previous_hashes = None for _ in range(64): entries, errors, options_map = loader.load_string(TEST_INPUT) hashes, errors = compare.hash_entries(entries) self.assertFalse(errors) if previous_hashes is None: previous_hashes = hashes else: self.assertEqual(previous_hashes.keys(), hashes.keys())
def test_get_commodities_map(self): entries, _, options_map = loader.load_string(TEST_INPUT) commodity_map = getters.get_commodity_map(entries, options_map) self.assertEqual({'HOOL', 'PIPA', 'USD'}, commodity_map.keys()) self.assertTrue(all(isinstance(value, data.Commodity) for value in commodity_map.values())) self.assertEqual(commodity_map['HOOL'], next(entry for entry in entries if isinstance(entry, data.Commodity)))
def test_generate(self): # Basic test that calls out the generator. with test_utils.capture('stdout', 'stderr') as (stdout, _): result = test_utils.run_with_args(example.main, []) self.assertEqual(0, result, str(result)) file_contents = stdout.getvalue() self.assertTrue(file_contents) loaded_entries, errors, _ = loader.load_string( file_contents, extra_validations=validation.HARDCORE_VALIDATIONS) self.assertFalse(errors)
def test_forecast(self): input_text = textwrap.dedent(""" plugin "beancount.plugins.forecast" 2011-01-01 open Expenses:Restaurant 2011-01-01 open Assets:Cash 2011-05-17 # "Something [MONTHLY UNTIL 2011-12-31]" Expenses:Restaurant 50.02 USD Assets:Cash """) entries, errors, __ = loader.load_string(input_text) self.assertFalse(errors) self.assertEqualEntries( """ 2011-01-01 open Expenses:Restaurant 2011-01-01 open Assets:Cash 2011-05-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-06-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-07-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-08-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-09-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-10-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-11-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD 2011-12-17 # "Something" Expenses:Restaurant 50.02 USD Assets:Cash -50.02 USD """, entries)
def bean_to_json(bean_str: str): if 'beancount.plugins.auto_accounts' not in bean_str: bean_str = 'plugin "beancount.plugins.auto_accounts"\n' + bean_str entries, errors, options = loader.load_string(bean_str) data = { "variant": "beancount", "version": "2.2.1", "entries": list(map(wrap_entry, entries)), "errors": errors, "options": options, } return json_dumps_decimal(data), data
def test_verify_document_files_exist(self): entries, _, options_map = loader.load_string(textwrap.dedent(""" option "plugin_processing_mode" "raw" 2014-06-08 document Assets:US:Bank:Checking "ROOT/Assets/US/Bank/Checking/2014-06-08.bank-statement.pdf" 2014-07-01 document Assets:US:Bank:Savings "ROOT/Assets/US/Bank/Savings/2014-07-01.savings.pdf" 2014-07-10 document Assets:US:Bank:Savings "ROOT/Assets/US/Bank/Savings/2014-07-10.something-else.pdf" """).replace('ROOT', self.root)) _, errors = documents.verify_document_files_exist(entries, options_map) self.assertEqual(1, len(errors)) document_error = errors[0] self.assertTrue( document_error.entry.filename.endswith('2014-07-10.something-else.pdf'))
def test_tolerances__number_on_cost_fail_to_succ(self): # An example of a transaction that would fail without the inferred # tolerances and succeed with them. input_string = textwrap.dedent(""" plugin "beancount.plugins.auto_accounts" 2014-02-25 * Assets:Account3 5.111 VHT {1000.00 USD} Assets:Account4 -5110.80 USD """) input_option = textwrap.dedent(""" option "infer_tolerance_from_cost" "True" """) entries, errors, options_map = loader.load_string(input_string) self.assertFalse(options_map["infer_tolerance_from_cost"]) self.assertEqual(1, len(errors)) self.assertRegex(errors[0].message, 'Transaction does not balance:.*0.20000 USD') entries, errors, options_map = loader.load_string(input_option + input_string) self.assertTrue(options_map["infer_tolerance_from_cost"]) self.assertFalse(errors)
def main(): argparser = argparse.ArgumentParser(description=__doc__) argparser.add_argument('infile', type=argparse.FileType('r'), help='Filename or "-" for stdin') args = argparser.parse_args() # Read input from stdin or a given filename. entries, errors, options = loader.load_string(args.infile.read()) # Print out sorted entries. for entry in data.sorted(entries): printer.print_entry(entry)
def test_render_missing(self): # We want to make sure we never render with scientific notation. input_string = textwrap.dedent(""" 2019-01-19 * "Fitness First" "Last training session" Expenses:Sports:Gym:Martin Assets:Martin:Cash """) entries, errors, options_map = loader.load_string(input_string) txn = errors[0].entry oss = io.StringIO() printer.print_entry(txn, file=oss)
def test_find_similar_entries(self, entries, _, __): """ plugin "beancount.plugins.auto_accounts" 2016-01-03 * Expenses:Tips 1.03 USD Assets:Other 2016-01-04 * Expenses:Coffee 1.04 USD Assets:Other 2016-01-05 * Expenses:Restaurant 1.05 USD Assets:Other 2016-01-06 * Expenses:Groceries 1.06 USD Assets:Other 2016-01-07 * Expenses:Alcohol 1.07 USD Assets:Other 2016-01-08 * Expenses:Smoking 1.08 USD Assets:Other 2016-01-09 * Expenses:Taxi 1.09 USD Assets:Other """ new_entries, _, __ = loader.load_string(""" plugin "beancount.plugins.auto_accounts" 2016-01-06 * Expenses:Groceries 1.06 USD Assets:Other """) for days, num_comparisons in [(0, 1), (1, 1), (2, 1)]: duplicates = similar.find_similar_entries(new_entries, entries, lambda e1, e2: True, window_days=days) self.assertEqual(num_comparisons, len(duplicates)) duplicates = similar.find_similar_entries(new_entries, entries, lambda e1, e2: False, window_days=days) self.assertEqual(0, len(duplicates))
def test_compare_entries(self): entries1, _, __ = loader.load_string(TEST_INPUT) entries2, _, __ = loader.load_string(TEST_INPUT) # Check two equal sets. same, missing1, missing2 = compare.compare_entries(entries1, entries2) self.assertTrue(same) self.assertFalse(missing1) self.assertFalse(missing2) # First > Second. same, missing1, missing2 = compare.compare_entries( entries1, entries2[:-1]) self.assertFalse(same) self.assertTrue(missing1) self.assertFalse(missing2) self.assertEqual(1, len(missing1)) self.assertTrue(isinstance(missing1.pop(), data.Close)) # First < Second. same, missing1, missing2 = compare.compare_entries( entries1[:-1], entries2) self.assertFalse(same) self.assertFalse(missing1) self.assertTrue(missing2) self.assertEqual(1, len(missing2)) self.assertTrue(isinstance(missing2.pop(), data.Close)) # Both have missing. same, missing1, missing2 = compare.compare_entries( entries1[1:], entries2[:-1]) self.assertFalse(same) self.assertTrue(missing1) self.assertTrue(missing2) self.assertEqual(1, len(missing1)) self.assertTrue(isinstance(missing1.pop(), data.Close)) self.assertEqual(1, len(missing2)) self.assertTrue(isinstance(missing2.pop(), data.Open))
def test_get_account_open_close(self): entries = loader.load_string(TEST_INPUT)[0] ocmap = getters.get_account_open_close(entries) self.assertEqual(5, len(ocmap)) def mapfound(account_name): open, close = ocmap[account_name] return (open is not None, close is not None) self.assertEqual(mapfound('Assets:US:Cash'), (True, True)) self.assertEqual(mapfound('Assets:US:Credit-Card'), (True, True)) self.assertEqual(mapfound('Expenses:Grocery'), (True, False)) self.assertEqual(mapfound('Expenses:Coffee'), (True, False)) self.assertEqual(mapfound('Expenses:Restaurant'), (True, False))
def assertRoundTrip(self, entries1, errors1): self.assertFalse(errors1) # Print out the entries and parse them back in. oss1 = io.StringIO() oss1.write('option "plugin_processing_mode" "raw"\n') printer.print_entries(entries1, file=oss1) entries2, errors, __ = loader.load_string(oss1.getvalue()) self.assertEqualEntries(entries1, entries2) self.assertFalse(errors) # Print out those reparsed and parse them back in. oss2 = io.StringIO() oss2.write('option "plugin_processing_mode" "raw"\n') printer.print_entries(entries2, file=oss2) entries3, errors, __ = loader.load_string(oss2.getvalue()) self.assertEqualEntries(entries1, entries3) self.assertFalse(errors) # Compare the two output texts. self.assertEqual(oss2.getvalue(), oss1.getvalue())
def newly_generated_txns(output_txns, correctly_generated_txn_text): # Get transactions from output of plugin (should be tagged appropriately) transactions = [txn for txn in output_txns if isinstance(txn, Transaction)] # Get correctly generated transactions from feature file correctly_generated_txns, _, _ = load_string(correctly_generated_txn_text) has_correct, missing_entries = includes_entries(correctly_generated_txns, transactions) print("Missing entries: {}".format(len(missing_entries))) assert has_correct
def test_link_statements_missing(tmpdir): sample_folder = tmpdir.mkdir('fava_plugins').mkdir('documents') bfile = dedent(""" option "documents" "{}" plugin "fava.plugins.link_statements" 2016-10-31 open Expenses:Foo 2016-10-31 open Assets:Cash 2016-11-01 * "Foo" "Bar" statement: "test/Foobar.pdf" Expenses:Foo 100 EUR Assets:Cash """.format(sample_folder)) entries, errors, _ = load_string(bfile) assert len(errors) == 1 assert isinstance(errors[0], StatementDocumentError) assert len(entries) == 3
def test_link_documents_missing(tmpdir): sample_folder = tmpdir.mkdir('fava_plugins').mkdir('documents') bfile = _format(""" option "documents" "{}" plugin "fava.plugins.link_documents" 2016-10-31 open Expenses:Foo 2016-10-31 open Assets:Cash 2016-11-01 * "Foo" "Bar" document: "{}" Expenses:Foo 100 EUR Assets:Cash """, (sample_folder, os.path.join('test', 'Foobar.pdf'))) entries, errors, _ = load_string(bfile) assert len(errors) == 1 assert isinstance(errors[0], DocumentError) assert len(entries) == 3
def test_upcoming_events(): entries, _, _ = load_string('{} event "some_event" "test"\n' '2012-12-12 event "test" "test"'.format( str(datetime.date.today()))) assert len(upcoming_events(entries, 1)) == 1
def load_doc(request): return load_string(request.function.__doc__, dedent=True)