|
16 | 16 | """Unit tests for google.cloud.firestore_v1.bson classes.""" |
17 | 17 |
|
18 | 18 | import copy |
| 19 | +import decimal |
19 | 20 | import pickle |
20 | 21 | import re |
21 | 22 |
|
22 | 23 | import pytest |
23 | 24 |
|
24 | 25 | from google.cloud.firestore_v1.bson import ( |
25 | 26 | BSONBinary, |
| 27 | + BSONDecimal128, |
26 | 28 | BSONInt32, |
27 | 29 | BSONMaxKey, |
28 | 30 | BSONMinKey, |
@@ -482,3 +484,98 @@ def test_bson_regex_copy(): |
482 | 484 | def test_bson_regex_pickle(): |
483 | 485 | rx = BSONRegex("^abc", options="i") |
484 | 486 | assert pickle.loads(pickle.dumps(rx)) == rx |
| 487 | + |
| 488 | + |
| 489 | +def test_bson_decimal128_valid(): |
| 490 | + dec1 = BSONDecimal128("123.45") |
| 491 | + assert dec1.value == "123.45" |
| 492 | + assert dec1.to_decimal == decimal.Decimal("123.45") |
| 493 | + assert dec1._to_map_value() == {"__decimal128__": "123.45"} |
| 494 | + assert repr(dec1) == "BSONDecimal128('123.45')" |
| 495 | + assert str(dec1) == "123.45" |
| 496 | + |
| 497 | + dec2 = BSONDecimal128(42) |
| 498 | + assert dec2.value == "42" |
| 499 | + |
| 500 | + dec3 = BSONDecimal128(1.5) |
| 501 | + assert dec3.value == "1.5" |
| 502 | + |
| 503 | + dec4 = BSONDecimal128(decimal.Decimal("99.99")) |
| 504 | + assert dec4.value == "99.99" |
| 505 | + |
| 506 | + dec5 = BSONDecimal128(dec1) |
| 507 | + assert dec5.value == "123.45" |
| 508 | + |
| 509 | + |
| 510 | +def test_bson_decimal128_special_values(): |
| 511 | + nan_dec = BSONDecimal128("NaN") |
| 512 | + assert nan_dec.value == "NaN" |
| 513 | + assert nan_dec._to_map_value() == {"__decimal128__": "NaN"} |
| 514 | + |
| 515 | + inf_dec = BSONDecimal128("Infinity") |
| 516 | + assert inf_dec.value == "Infinity" |
| 517 | + |
| 518 | + neg_inf_dec = BSONDecimal128("-Infinity") |
| 519 | + assert neg_inf_dec.value == "-Infinity" |
| 520 | + |
| 521 | + |
| 522 | +@pytest.mark.parametrize( |
| 523 | + "val_input, exc_type, match_msg", |
| 524 | + [ |
| 525 | + (True, TypeError, "value must be a Decimal, str, int, or float"), |
| 526 | + (False, TypeError, "value must be a Decimal, str, int, or float"), |
| 527 | + ([1, 2], TypeError, "value must be a Decimal, str, int, or float"), |
| 528 | + ], |
| 529 | +) |
| 530 | +def test_bson_decimal128_invalid_inputs(val_input, exc_type, match_msg): |
| 531 | + with pytest.raises(exc_type, match=match_msg): |
| 532 | + BSONDecimal128(val_input) |
| 533 | + |
| 534 | + |
| 535 | +def test_bson_decimal128_equality(): |
| 536 | + d1 = BSONDecimal128("123.45") |
| 537 | + d2 = BSONDecimal128("123.45") |
| 538 | + d3 = BSONDecimal128("678.90") |
| 539 | + assert d1 == d2 |
| 540 | + assert d1 != d3 |
| 541 | + assert d1 == decimal.Decimal("123.45") |
| 542 | + assert d1 != "123.45" |
| 543 | + |
| 544 | + # Transitivity test: BSONDecimal128("1.0") == Decimal("1") == BSONDecimal128("1") |
| 545 | + d_trail = BSONDecimal128("1.0") |
| 546 | + d_int = BSONDecimal128("1") |
| 547 | + dec_int = decimal.Decimal("1") |
| 548 | + assert d_trail == dec_int |
| 549 | + assert d_int == dec_int |
| 550 | + assert d_trail == d_int # Transitivity enforced! |
| 551 | + |
| 552 | + nan1 = BSONDecimal128("NaN") |
| 553 | + nan2 = BSONDecimal128("NaN") |
| 554 | + assert nan1 == nan2 |
| 555 | + |
| 556 | + |
| 557 | +def test_bson_decimal128_hash_and_dict_key(): |
| 558 | + d1 = BSONDecimal128("123.45") |
| 559 | + d2 = BSONDecimal128("123.45") |
| 560 | + dec_val = decimal.Decimal("123.45") |
| 561 | + |
| 562 | + # Hash invariant test: if a == b, then hash(a) == hash(b) |
| 563 | + assert hash(d1) == hash(d2) |
| 564 | + assert hash(d1) == hash(dec_val) |
| 565 | + assert len({d1, d2, dec_val}) == 1 |
| 566 | + |
| 567 | + nan1 = BSONDecimal128("NaN") |
| 568 | + nan2 = BSONDecimal128("NaN") |
| 569 | + assert hash(nan1) == hash(nan2) |
| 570 | + assert len({nan1, nan2}) == 1 |
| 571 | + |
| 572 | + |
| 573 | +def test_bson_decimal128_copy(): |
| 574 | + d = BSONDecimal128("123.45") |
| 575 | + assert copy.copy(d) == d |
| 576 | + assert copy.deepcopy(d) == d |
| 577 | + |
| 578 | + |
| 579 | +def test_bson_decimal128_pickle(): |
| 580 | + d = BSONDecimal128("123.45") |
| 581 | + assert pickle.loads(pickle.dumps(d)) == d |
0 commit comments