from decimal import Decimal from random import randrange import pytest from t_tech.invest import Quotation from t_tech.invest.utils import decimal_to_quotation, quotation_to_decimal MAX_UNITS = 999_999_999_999 MAX_NANO = 999_999_999 @pytest.fixture() def quotation(request) -> Quotation: raw = request.param return Quotation(units=raw["units"], nano=raw["nano"]) class TestQuotationArithmetic: @pytest.mark.parametrize( ("quotation", "decimal"), [ ({"units": 114, "nano": 250000000}, Decimal("114.25")), ({"units": -200, "nano": -200000000}, Decimal("-200.20")), ({"units": -0, "nano": -10000000}, Decimal("-0.01")), ], indirect=["quotation"], ) def test_quotation_to_decimal(self, quotation: Quotation, decimal: Decimal): actual = quotation_to_decimal(quotation) assert actual == decimal @pytest.mark.parametrize( ("quotation", "decimal"), [ ({"units": 114, "nano": 250000000}, Decimal("114.25")), ({"units": -200, "nano": -200000000}, Decimal("-200.20")), ({"units": -0, "nano": -10000000}, Decimal("-0.01")), ], indirect=["quotation"], ) def test_decimal_to_quotation(self, decimal: Decimal, quotation: Quotation): actual = decimal_to_quotation(decimal) assert actual.units == quotation.units assert actual.nano == quotation.nano @pytest.mark.parametrize( ("quotation_left", "quotation_right"), [ ( Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), ), ( Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), Quotation(units=0, nano=0), ), ( Quotation(units=0, nano=0), Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), ), ( Quotation(units=-0, nano=-200000000), Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), ), ( Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), Quotation(units=-0, nano=-200000000), ), ( Quotation( units=MAX_UNITS, nano=MAX_NANO, ), Quotation( units=MAX_UNITS, nano=MAX_NANO, ), ), ], ) @pytest.mark.parametrize( "operation", [ lambda x, y: x - y, lambda x, y: x + y, ], ) def test_operations( self, quotation_left: Quotation, quotation_right: Quotation, operation ): decimal_left = quotation_to_decimal(quotation_left) decimal_right = quotation_to_decimal(quotation_right) quotation = operation(quotation_left, quotation_right) expected_decimal = operation(decimal_left, decimal_right) actual_decimal = quotation_to_decimal(quotation) assert actual_decimal == expected_decimal @pytest.mark.parametrize( ("quotation_left", "quotation_right"), [ ( Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), ), ( Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), Quotation(units=0, nano=0), ), ( Quotation(units=0, nano=0), Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), ), ( Quotation(units=0, nano=0), Quotation(units=0, nano=0), ), ( Quotation(units=-10, nano=0), Quotation(units=10, nano=0), ), ( Quotation(units=0, nano=-200000000), Quotation(units=0, nano=-200000000), ), ( Quotation( units=MAX_UNITS, nano=MAX_NANO, ), Quotation( units=MAX_UNITS, nano=MAX_NANO, ), ), ], ) @pytest.mark.parametrize( "comparator", [ lambda x, y: x > y, lambda x, y: x >= y, lambda x, y: x < y, lambda x, y: x <= y, lambda x, y: x == y, lambda x, y: x != y, lambda y, x: x > y, lambda y, x: x >= y, lambda y, x: x < y, lambda y, x: x <= y, lambda y, x: x == y, lambda y, x: x != y, ], ) def test_comparison( self, quotation_left: Quotation, quotation_right: Quotation, comparator ): decimal_left = quotation_to_decimal(quotation_left) decimal_right = quotation_to_decimal(quotation_right) actual_comparison = comparator(quotation_left, quotation_right) expected_comparison = comparator(decimal_left, decimal_right) assert actual_comparison == expected_comparison @pytest.mark.parametrize( "quotation", [ Quotation( units=randrange(-MAX_UNITS, MAX_UNITS), nano=randrange(-MAX_NANO, MAX_NANO), ), Quotation( units=randrange(-MAX_UNITS, 0), nano=randrange(-MAX_NANO, 0), ), Quotation( units=randrange(0, MAX_UNITS), nano=randrange(0, MAX_NANO), ), ], ) def test_abs(self, quotation: Quotation): decimal = quotation_to_decimal(quotation) actual = abs(decimal) expected = abs(decimal) assert actual == expected @pytest.mark.parametrize( ("units", "nano"), [ (-MAX_UNITS, MAX_NANO * 1000), (MAX_UNITS, -MAX_NANO + 1123123), (0, MAX_NANO + 1121201203123), (MAX_UNITS * 100, -MAX_UNITS - 121201203123), ], ) def test_nano_overfill_transfers(self, units: int, nano: int): quotation = Quotation(units=units, nano=nano) if abs(nano) >= 1e9: assert quotation.nano < 1e9 assert quotation.units - units == nano // 1_000_000_000 assert quotation.nano == nano % 1_000_000_000