|
23 | 23 | from botocore.exceptions import ParamValidationError |
24 | 24 | from botocore.model import ServiceModel |
25 | 25 | from botocore.serialize import SERIALIZERS |
| 26 | +from botocore.serialize import ( |
| 27 | + TIMESTAMP_PRECISION_DEFAULT, |
| 28 | + TIMESTAMP_PRECISION_MILLISECOND, |
| 29 | +) |
26 | 30 |
|
27 | 31 | from tests import unittest |
28 | 32 |
|
@@ -614,3 +618,120 @@ def test_restxml_serializes_unicode(self): |
614 | 618 | self.serialize_to_request(params) |
615 | 619 | except UnicodeEncodeError: |
616 | 620 | self.fail("RestXML serializer failed to serialize unicode text.") |
| 621 | + |
| 622 | + |
| 623 | +class TestTimestampPrecisionParameter(unittest.TestCase): |
| 624 | + def setUp(self): |
| 625 | + self.model = { |
| 626 | + 'metadata': {'protocol': 'query', 'apiVersion': '2014-01-01'}, |
| 627 | + 'documentation': '', |
| 628 | + 'operations': { |
| 629 | + 'TestOperation': { |
| 630 | + 'name': 'TestOperation', |
| 631 | + 'http': { |
| 632 | + 'method': 'POST', |
| 633 | + 'requestUri': '/', |
| 634 | + }, |
| 635 | + 'input': {'shape': 'InputShape'}, |
| 636 | + } |
| 637 | + }, |
| 638 | + 'shapes': { |
| 639 | + 'InputShape': { |
| 640 | + 'type': 'structure', |
| 641 | + 'members': { |
| 642 | + 'UnixTimestamp': {'shape': 'UnixTimestampType'}, |
| 643 | + 'IsoTimestamp': {'shape': 'IsoTimestampType'}, |
| 644 | + 'Rfc822Timestamp': {'shape': 'Rfc822TimestampType'}, |
| 645 | + }, |
| 646 | + }, |
| 647 | + 'IsoTimestampType': { |
| 648 | + 'type': 'timestamp', |
| 649 | + "timestampFormat": "iso8601", |
| 650 | + }, |
| 651 | + 'UnixTimestampType': { |
| 652 | + 'type': 'timestamp', |
| 653 | + "timestampFormat": "unixTimestamp", |
| 654 | + }, |
| 655 | + 'Rfc822TimestampType': { |
| 656 | + 'type': 'timestamp', |
| 657 | + "timestampFormat": "rfc822", |
| 658 | + }, |
| 659 | + }, |
| 660 | + } |
| 661 | + self.service_model = ServiceModel(self.model) |
| 662 | + |
| 663 | + def serialize_to_request( |
| 664 | + self, input_params, timestamp_precision=TIMESTAMP_PRECISION_DEFAULT |
| 665 | + ): |
| 666 | + request_serializer = serialize.create_serializer( |
| 667 | + self.service_model.metadata['protocol'], |
| 668 | + timestamp_precision=timestamp_precision, |
| 669 | + ) |
| 670 | + return request_serializer.serialize_to_request( |
| 671 | + input_params, self.service_model.operation_model('TestOperation') |
| 672 | + ) |
| 673 | + |
| 674 | + def test_second_precision_maintains_existing_behavior(self): |
| 675 | + test_datetime = datetime.datetime(2024, 1, 1, 12, 0, 0, 123456) |
| 676 | + request = self.serialize_to_request( |
| 677 | + {'UnixTimestamp': test_datetime, 'IsoTimestamp': test_datetime} |
| 678 | + ) |
| 679 | + # To maintain backwards compatibility, unix should not include milliseconds by default |
| 680 | + self.assertEqual(1704110400, request['body']['UnixTimestamp']) |
| 681 | + |
| 682 | + # ISO always supported microseconds, so we need to continue supporting this |
| 683 | + self.assertEqual( |
| 684 | + '2024-01-01T12:00:00.123456Z', |
| 685 | + request['body']['IsoTimestamp'], |
| 686 | + ) |
| 687 | + |
| 688 | + def test_millisecond_precision_serialization(self): |
| 689 | + test_datetime = datetime.datetime(2024, 1, 1, 12, 0, 0, 123456) |
| 690 | + |
| 691 | + # Check that millisecond precision is used when it is opted in to via the input param |
| 692 | + request = self.serialize_to_request( |
| 693 | + {'UnixTimestamp': test_datetime, 'IsoTimestamp': test_datetime}, |
| 694 | + TIMESTAMP_PRECISION_MILLISECOND, |
| 695 | + ) |
| 696 | + self.assertEqual(1704110400.123, request['body']['UnixTimestamp']) |
| 697 | + self.assertEqual( |
| 698 | + '2024-01-01T12:00:00.123Z', |
| 699 | + request['body']['IsoTimestamp'], |
| 700 | + ) |
| 701 | + |
| 702 | + def test_millisecond_precision_with_zero_microseconds(self): |
| 703 | + test_datetime = datetime.datetime(2024, 1, 1, 12, 0, 0, 0) |
| 704 | + |
| 705 | + request = self.serialize_to_request( |
| 706 | + {'UnixTimestamp': test_datetime, 'IsoTimestamp': test_datetime}, |
| 707 | + TIMESTAMP_PRECISION_MILLISECOND, |
| 708 | + ) |
| 709 | + self.assertEqual(1704110400.0, request['body']['UnixTimestamp']) |
| 710 | + self.assertEqual( |
| 711 | + '2024-01-01T12:00:00.000Z', |
| 712 | + request['body']['IsoTimestamp'], |
| 713 | + ) |
| 714 | + |
| 715 | + def test_rfc822_timestamp_always_uses_second_precision(self): |
| 716 | + # RFC822 format doesn't support sub-second precision. |
| 717 | + test_datetime = datetime.datetime(2024, 1, 1, 12, 0, 0, 123456) |
| 718 | + request_second = self.serialize_to_request( |
| 719 | + {'Rfc822Timestamp': test_datetime}, |
| 720 | + ) |
| 721 | + request_milli = self.serialize_to_request( |
| 722 | + {'Rfc822Timestamp': test_datetime}, TIMESTAMP_PRECISION_MILLISECOND |
| 723 | + ) |
| 724 | + self.assertEqual( |
| 725 | + request_second['body']['Rfc822Timestamp'], |
| 726 | + request_milli['body']['Rfc822Timestamp'], |
| 727 | + ) |
| 728 | + self.assertIn('2024', request_second['body']['Rfc822Timestamp']) |
| 729 | + self.assertIn('GMT', request_second['body']['Rfc822Timestamp']) |
| 730 | + |
| 731 | + def test_invalid_timestamp_precision_raises_error(self): |
| 732 | + with self.assertRaises(ValueError) as context: |
| 733 | + serialize.create_serializer( |
| 734 | + self.service_model.metadata['protocol'], |
| 735 | + timestamp_precision='invalid', |
| 736 | + ) |
| 737 | + self.assertIn("Invalid timestamp precision", str(context.exception)) |
0 commit comments