oxrlib/tests/test_historical.py
Brett Smith 71893ace4d tests: Historical tests use more flexible rate matching.
This lets the tests be more flexible about how much precision is used in
rates when appropriate, and makes them ready to parametrize for
Beancount.
2020-05-17 10:44:03 -04:00

181 lines
6.9 KiB
Python

import argparse
import decimal
import io
import json
import re
import pytest
from . import any_date, relpath
import oxrlib.commands.historical as oxrhist
class FakeResponder:
def __init__(self, response_path):
self.response_path = response_path
def _respond(self, *args, **kwargs):
return open(self.response_path)
def __getattr__(self, name):
return self._respond
def should_cache(self):
return False
class FakeConfig:
def __init__(self, responder, argvars=None):
self.responder = responder
self.args = argparse.Namespace()
if argvars is not None:
for key in argvars:
setattr(self.args, key, argvars[key])
def get_loaders(self):
return self.responder
output = pytest.fixture(lambda: io.StringIO())
@pytest.fixture
def historical1_responder():
return FakeResponder(relpath('historical1.json').as_posix())
def build_config(
responder,
date,
amount=None,
from_currency=None,
to_currency=None,
ledger=False,
signed_currencies=None,
denomination=None,
base='USD',
):
return FakeConfig(responder, {
'date': date,
'base': base,
'amount': None if amount is None else decimal.Decimal(amount),
'from_currency': from_currency,
'to_currency': base if to_currency is None else to_currency,
'output_format': oxrhist.Formats['LEDGER' if ledger else 'RAW'],
'signed_currencies': [base] if signed_currencies is None else signed_currencies,
'denomination': denomination,
})
def lines_from_run(config, output):
oxrhist.run(config, output, output)
output.seek(0)
return iter(output)
def check_fx_amount(config, lines, amount, cost, fx_code, fx_sign=None, price=None):
if price is None:
price = cost
cost = re.escape(cost) + r'\d*'
price = re.escape(price) + r'\d*'
if fx_sign is not None and fx_code in config.args.signed_currencies:
rate_fmt = f'{re.escape(fx_sign)}{{}}'
else:
rate_fmt = f'{{}} {re.escape(fx_code)}'
pattern = r'^{} {{={}}} @ {}$'.format(
re.escape(amount),
rate_fmt.format(cost),
rate_fmt.format(price),
)
line = next(lines, "<EOF>")
assert re.match(pattern, line)
def test_rate_list(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date)
lines = lines_from_run(config, output)
assert next(lines).startswith('1 AED = 0.27229')
assert next(lines) == '1 USD = 3.67246 AED\n'
assert next(lines).startswith('1 ALL = 0.0069189')
assert next(lines) == '1 USD = 144.529793 ALL\n'
assert next(lines).startswith('1 ANG = 0.55865')
assert next(lines) == '1 USD = 1.79 ANG\n'
def test_one_rate(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date, from_currency='ANG')
lines = lines_from_run(config, output)
assert next(lines).startswith('1 ANG = 0.55865')
assert next(lines) == '1 USD = 1.79 ANG\n'
assert next(lines, None) is None
def test_conversion(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date, amount=10, from_currency='AED')
lines = lines_from_run(config, output)
assert next(lines) == '10.00 AED = 2.72 USD\n'
assert next(lines, None) is None
def test_back_conversion(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
amount=2, from_currency='USD', to_currency='ALL')
lines = lines_from_run(config, output)
assert next(lines) == '2.00 USD = 289 ALL\n'
assert next(lines, None) is None
def test_ledger_rate(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='ANG', ledger=True)
lines = lines_from_run(config, output)
check_fx_amount(config, lines, '1 ANG', '0.5586', 'USD', '$')
check_fx_amount(config, lines, '1 USD', '1.79', 'ANG')
assert next(lines, None) is None
def test_ledger_conversion(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='ALL', amount=300, ledger=True)
lines = lines_from_run(config, output)
check_fx_amount(config, lines, '300 ALL', '0.00691', 'USD', '$')
assert next(lines) == '$2.08\n'
assert next(lines, None) is None
def test_signed_currencies(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='AED', ledger=True, signed_currencies=['EUR'])
lines = lines_from_run(config, output)
check_fx_amount(config, lines, '1 AED', '0.272', 'USD', '$')
check_fx_amount(config, lines, '1 USD', '3.672', 'AED')
assert next(lines, None) is None
def test_denomination(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='ANG', to_currency='AED', amount=10,
ledger=True, denomination='USD')
lines = lines_from_run(config, output)
check_fx_amount(config, lines, '10.00 ANG', '0.558', 'USD', '$')
check_fx_amount(config, lines, '20.52 AED', '0.272', 'USD', '$')
assert next(lines, None) is None
def test_redundant_denomination(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='ANG', to_currency='USD', amount=10,
ledger=True, denomination='USD')
lines = lines_from_run(config, output)
check_fx_amount(config, lines, '10.00 ANG', '0.558', 'USD', '$')
assert next(lines) == '$5.59\n'
assert next(lines, None) is None
def test_from_denomination(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='USD', to_currency='ALL', amount=10,
ledger=True, denomination='USD')
lines = lines_from_run(config, output)
assert next(lines) == '$10.00\n'
check_fx_amount(config, lines, '1,445 ALL', '0.00691', 'USD', '$')
assert next(lines, None) is None
def test_rate_precision_added_as_needed(historical1_responder, output, any_date):
config = build_config(historical1_responder, any_date,
from_currency='RUB', to_currency='USD', amount=63805,
ledger=True, denomination='USD')
lines = lines_from_run(config, output)
# 63,805 / 57.0763 (the RUB rate) == $1,117.89
# But using the truncated rate: 63,805 * .01752 == $1,117.86
# Make sure the rate is specified with enough precision to get the
# correct conversion amount.
check_fx_amount(config, lines, '63,805.00 RUB', '0.0175204', 'USD', '$')
assert next(lines) == '$1,117.89\n'
assert next(lines, None) is None