userver: en/testsuite/protobuf/matching.py Source File
Loading...
Searching...
No Matches
matching.py
1import typing
2
3import google.protobuf.message
4
5from testsuite.matching import PartialDict, recursive_partial_dict
6from testsuite.protobuf.utils import message_to_dict
7
8
10 def __init__(self, repr: dict, impl: typing.Any):
11 self._repr = repr
12 self._impl = impl
13
14 def __repr__(self):
15 return f'<type(self).__name__ {self._repr!r}>'
16
17 def __eq__(self, other):
18 if isinstance(other, google.protobuf.message.Message):
19 return message_to_dict(other) == self._impl
20 if type(self) == type(other):
21 return self._impl == other._impl
22 return False
23
24
26 """Strict protobuf matcher.
27
28 Compares a protobuf message against an expected dict by converting the
29 message via :py:func:`testsuite.protobuf.utils.message_to_dict` and
30 requiring the resulting dict to equal the expected one exactly.
31
32 Every field present in the message must appear in the expected dict
33 (and vice versa). For partial matching where extra protobuf fields
34 should be ignored, use :py:class:`PartialProtobufDict` or
35 :py:func:`RecursivePartialProtobufDict` instead.
36
37 Example:
38
39 .. code-block:: python
40
41 assert msg == matching.ProtobufDict({
42 'first_name': 'John',
43 'last_name': 'Doe',
44 'status': 'STATUS_ACTIVE',
45 })
46 """
47
48 __testsuite_types__ = (google.protobuf.message.Message,)
49
50 def __init__(self, d: dict):
51 super().__init__(repr=d, impl=d)
52
53
55 """Partial protobuf matcher.
56
57 Compares a protobuf message against an expected dict by converting the
58 message via :py:func:`testsuite.protobuf.utils.message_to_dict` and
59 delegating to :py:class:`testsuite.matching.PartialDict`. Only the
60 keys listed in the expected dict are checked; any additional fields
61 on the protobuf message are ignored.
62
63 The match is only partial at the top level: nested dicts in the
64 expected pattern still have to equal the corresponding nested
65 message dicts exactly. Use :py:class:`RecursivePartialProtobufDict`
66 when nested messages should also be matched partially.
67
68 Example:
69
70 .. code-block:: python
71
72 # Passes regardless of other fields set on msg
73 assert msg == matching.PartialProtobufDict({'first_name': 'John'})
74 """
75
76 def __init__(self, d):
77 super().__init__(repr=d, impl=PartialDict(d))
78
79
81 """Recursive partial protobuf matcher.
82
83 Compares a protobuf message against an expected dict by converting the
84 message via :py:func:`testsuite.protobuf.utils.message_to_dict` and
85 delegating to :py:func:`testsuite.matching.recursive_partial_dict`.
86
87 Unlike :py:class:`PartialProtobufDict`, which only ignores extra fields
88 at the top level, this matcher applies partial matching recursively to
89 every nested message: only the keys listed in the expected dict (at any
90 depth) are checked, and any additional fields on the protobuf message
91 or its nested submessages are ignored.
92
93 Example:
94
95 .. code-block:: python
96
97 # Passes regardless of other fields set on msg or on
98 # msg.nested_field, as long as the listed fields match.
99 assert msg == matching.RecursivePartialProtobufDict({
100 'first_name': 'John',
101 'nested_field': {'inner_field': 1},
102 })
103 """
104
105 def __init__(self, d):
106 super().__init__(repr=d, impl=recursive_partial_dict(d))