|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from decimal import Decimal |
| 3 | +from decimal import ROUND_UP, Decimal, Inexact, Overflow, Rounded, localcontext |
4 | 4 | from typing import Any |
5 | 5 |
|
6 | 6 | import pytest |
@@ -65,3 +65,76 @@ def test_deserializer_error(): |
65 | 65 | ) |
66 | 66 | def test_deserializer_keeps_value_of_numbers_with_38_digits_of_precision(value): |
67 | 67 | assert TypeDeserializer().deserialize({"N": value}) == Decimal(value) |
| 68 | + |
| 69 | + |
| 70 | +@pytest.mark.parametrize( |
| 71 | + "value", |
| 72 | + [ |
| 73 | + "9" * 39, |
| 74 | + "-" + "9" * 39, |
| 75 | + "1" * 37 + "25", |
| 76 | + "1" * 37 + "35", |
| 77 | + "1." + "2" * 38, |
| 78 | + "123456789012345678901234567890123456789000", |
| 79 | + "123456789012345678901234567890123456789E-100", |
| 80 | + "1" * 39 + "E-9999999", |
| 81 | + ], |
| 82 | +) |
| 83 | +def test_deserializer_rejects_inexact_numbers(value): |
| 84 | + with pytest.raises(Inexact): |
| 85 | + TypeDeserializer().deserialize({"N": value}) |
| 86 | + |
| 87 | + |
| 88 | +@pytest.mark.parametrize("value", ["0E+0", "0E-100", "0e-130", "000E+5", "+0E-10", "-0E-100"]) |
| 89 | +def test_deserializer_preserves_scientific_zero(value): |
| 90 | + assert TypeDeserializer().deserialize({"N": value}) == Decimal(0) |
| 91 | + |
| 92 | + |
| 93 | +@pytest.mark.parametrize("value", ["", ".", "0", "000", "000.", "0.000", "-0", "+0"]) |
| 94 | +def test_deserializer_preserves_existing_zero_handling(value): |
| 95 | + assert TypeDeserializer().deserialize({"N": value}) == Decimal(0) |
| 96 | + |
| 97 | + |
| 98 | +@pytest.mark.parametrize( |
| 99 | + "value", |
| 100 | + [ |
| 101 | + "00012345678901234567890123456789012345678000", |
| 102 | + "-12345678901234567890123456789012345678000", |
| 103 | + "1.2345678901234567890123456789012345678000", |
| 104 | + "12345678901234567890123456789012345678000E-40", |
| 105 | + "1000000000000000000000000000000000000000E-167", |
| 106 | + "99999999999999999999999999999999999999000E+85", |
| 107 | + ], |
| 108 | +) |
| 109 | +def test_deserializer_removes_only_exact_trailing_zeros(value): |
| 110 | + # Application Decimal settings must not affect deserialization. |
| 111 | + with localcontext() as context: |
| 112 | + context.prec = 6 |
| 113 | + context.rounding = ROUND_UP |
| 114 | + context.traps[Inexact] = True |
| 115 | + context.traps[Rounded] = True |
| 116 | + assert TypeDeserializer().deserialize({"N": value}) == Decimal(value) |
| 117 | + |
| 118 | + |
| 119 | +@pytest.mark.parametrize( |
| 120 | + "value", |
| 121 | + [ |
| 122 | + {"NS": ["1", "9" * 39]}, |
| 123 | + {"L": [{"N": "9" * 39}]}, |
| 124 | + {"M": {"amount": {"N": "9" * 39}}}, |
| 125 | + ], |
| 126 | +) |
| 127 | +def test_deserializer_rejects_inexact_numbers_in_collections(value): |
| 128 | + with pytest.raises(Inexact): |
| 129 | + TypeDeserializer().deserialize(value) |
| 130 | + |
| 131 | + |
| 132 | +def test_deserializer_keeps_distinct_number_set_values(): |
| 133 | + values = ["1.2345678901234567890123456789012345677", "1.2345678901234567890123456789012345678"] |
| 134 | + |
| 135 | + assert TypeDeserializer().deserialize({"NS": values}) == {Decimal(value) for value in values} |
| 136 | + |
| 137 | + |
| 138 | +def test_deserializer_normalization_preserves_overflow_errors(): |
| 139 | + with pytest.raises(Overflow): |
| 140 | + TypeDeserializer().deserialize({"N": "1" * 38 + "00E+9999999"}) |
0 commit comments