|
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