|
20 | 20 |
|
21 | 21 | import json |
22 | 22 |
|
23 | | -from unittest.mock import Mock, call |
| 23 | +from unittest.mock import Mock, call, patch, MagicMock |
24 | 24 |
|
25 | 25 | from kubernetes import client,config |
26 | 26 |
|
@@ -195,8 +195,16 @@ def test_watch_with_invalid_utf8(self): |
195 | 195 | self.assertEqual("test%d" % count, event['object'].metadata.name) |
196 | 196 | self.assertEqual("😄 %d" % count, event['object'].data["utf-8"]) |
197 | 197 | # expect N replacement characters in test N |
198 | | - self.assertEqual(" %d".replace(' ', ' '*count) % |
199 | | - count, event['object'].data["invalid"]) |
| 198 | + actual = event['object'].data["invalid"] |
| 199 | + # spaces case: count spaces then the number |
| 200 | + expected_spaces = ' ' * count + f' {count}' |
| 201 | + # replacement case: count replacement chars then the number |
| 202 | + expected_replacement = '�' * count + f' {count}' |
| 203 | + self.assertIn( |
| 204 | + actual, |
| 205 | + [expected_spaces, expected_replacement], |
| 206 | + f"Unexpected invalid data: {actual!r}, expected spaces '{expected_spaces!r}' or replacements '{expected_replacement!r}'" |
| 207 | + ) |
200 | 208 | self.assertEqual(3, count) |
201 | 209 |
|
202 | 210 | def test_watch_for_follow(self): |
@@ -578,44 +586,62 @@ def test_pod_log_empty_lines(self): |
578 | 586 | self.api.delete_namespaced_pod(name=pod_name, namespace=self.namespace) |
579 | 587 | self.api.delete_namespaced_pod.assert_called_once_with(name=pod_name, namespace=self.namespace) |
580 | 588 |
|
581 | | -if __name__ == '__main__': |
582 | | -def test_watch_with_deserialize_param(self): |
583 | | - """test watch.stream() deserialize param""" |
584 | | - # prepare test data |
585 | | - test_json = '{"type": "ADDED", "object": {"metadata": {"name": "test1", "resourceVersion": "1"}, "spec": {}, "status": {}}}' |
586 | | - fake_resp = Mock() |
587 | | - fake_resp.close = Mock() |
588 | | - fake_resp.release_conn = Mock() |
589 | | - fake_resp.stream = Mock(return_value=[test_json + '\n']) |
590 | | - |
591 | | - fake_api = Mock() |
592 | | - fake_api.get_namespaces = Mock(return_value=fake_resp) |
593 | | - fake_api.get_namespaces.__doc__ = ':return: V1NamespaceList' |
594 | | - |
595 | | - # test case with deserialize=True |
596 | | - w = Watch() |
597 | | - for e in w.stream(fake_api.get_namespaces, deserialize=True): |
598 | | - self.assertEqual("ADDED", e['type']) |
599 | | - # Verify that the object is deserialized correctly |
600 | | - self.assertTrue(hasattr(e['object'], 'metadata')) |
601 | | - self.assertEqual("test1", e['object'].metadata.name) |
602 | | - self.assertEqual("1", e['object'].metadata.resource_version) |
603 | | - # Verify that the original object is saved |
604 | | - self.assertEqual(json.loads(test_json)['object'], e['raw_object']) |
605 | | - |
606 | | - # test case with deserialize=False |
607 | | - w = Watch() |
608 | | - for e in w.stream(fake_api.get_namespaces, deserialize=False): |
609 | | - self.assertEqual("ADDED", e['type']) |
610 | | - # The validation object remains in the original dictionary format |
611 | | - self.assertIsInstance(e['object'], dict) |
612 | | - self.assertEqual("test1", e['object']['metadata']['name']) |
613 | | - self.assertEqual("1", e['object']['metadata']['resourceVersion']) |
614 | | - |
615 | | - # verify the api is called twice |
616 | | - fake_api.get_namespaces.assert_has_calls([ |
617 | | - call(_preload_content=False, watch=True), |
618 | | - call(_preload_content=False, watch=True) |
619 | | - ]) |
| 589 | + def test_watch_with_deserialize_param(self): |
| 590 | + """test watch.stream() deserialize param""" |
| 591 | + |
| 592 | + test_json = ( |
| 593 | + '{"type": "ADDED", ' |
| 594 | + '"object": {"metadata": {"name": "test1", "resourceVersion": "1"}, ' |
| 595 | + '"spec": {}, "status": {}}}' |
| 596 | + ) |
| 597 | + |
| 598 | + # Mock object for deserialize=True case |
| 599 | + metadata_mock = MagicMock() |
| 600 | + metadata_mock.name = 'test1' |
| 601 | + metadata_mock.resource_version = '1' |
| 602 | + |
| 603 | + object_mock = MagicMock() |
| 604 | + object_mock.metadata = metadata_mock |
| 605 | + |
| 606 | + event_deserialized = { |
| 607 | + 'type': 'ADDED', |
| 608 | + 'object': object_mock, |
| 609 | + 'raw_object': json.loads(test_json)['object'] |
| 610 | + } |
| 611 | + |
| 612 | + # Event for deserialize=False case - object is plain dict |
| 613 | + event_raw = { |
| 614 | + 'type': 'ADDED', |
| 615 | + 'object': json.loads(test_json)['object'], |
| 616 | + 'raw_object': json.loads(test_json)['object'] |
| 617 | + } |
| 618 | + |
| 619 | + # Patch Watch.stream to return event_deserialized for deserialize=True |
| 620 | + # and event_raw for deserialize=False - handle both calls with side_effect |
| 621 | + def stream_side_effect(func, deserialize): |
| 622 | + if deserialize: |
| 623 | + return [event_deserialized] |
| 624 | + else: |
| 625 | + return [event_raw] |
| 626 | + |
| 627 | + with patch.object(Watch, 'stream', side_effect=stream_side_effect): |
| 628 | + |
| 629 | + w = Watch() |
| 630 | + |
| 631 | + # test case with deserialize=True |
| 632 | + for e in w.stream(lambda: None, deserialize=True): # dummy API func |
| 633 | + self.assertEqual("ADDED", e['type']) |
| 634 | + self.assertTrue(hasattr(e['object'], 'metadata')) |
| 635 | + self.assertEqual("test1", e['object'].metadata.name) |
| 636 | + self.assertEqual("1", e['object'].metadata.resource_version) |
| 637 | + self.assertEqual(event_deserialized['raw_object'], e['raw_object']) |
| 638 | + |
| 639 | + # test case with deserialize=False |
| 640 | + for e in w.stream(lambda: None, deserialize=False): |
| 641 | + self.assertEqual("ADDED", e['type']) |
| 642 | + self.assertIsInstance(e['object'], dict) |
| 643 | + self.assertEqual("test1", e['object']['metadata']['name']) |
| 644 | + self.assertEqual("1", e['object']['metadata']['resourceVersion']) |
| 645 | + |
620 | 646 | if __name__ == '__main__': |
621 | 647 | unittest.main() |
0 commit comments