Coverage for app / backend / src / tests / test_sanitized_bytes.py: 100%
116 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-02-03 06:18 +0000
« prev ^ index » next coverage.py v7.13.2, created at 2026-02-03 06:18 +0000
1from google.protobuf import empty_pb2
3from couchers.interceptors import _sanitized_bytes
4from couchers.proto import api_pb2, auth_pb2, conversations_pb2
7class TestSanitizedBytes:
8 """Test suite for _sanitized_bytes function."""
10 def test_none_input(self):
11 """Test that None input returns None."""
12 result = _sanitized_bytes(None)
13 assert result is None
15 def test_empty_message(self):
16 """Test that an empty message is serialized correctly."""
17 proto = empty_pb2.Empty()
18 result = _sanitized_bytes(proto)
20 assert isinstance(result, bytes)
21 # Verify we can deserialize it back
22 deserialized = empty_pb2.Empty.FromString(result)
23 assert deserialized == proto
25 def test_message_with_no_sensitive_fields(self):
26 """Test that messages without sensitive fields are not modified."""
27 # AuthRes has no sensitive fields
28 proto = auth_pb2.AuthRes(user_id=12345, jailed=False)
29 result = _sanitized_bytes(proto)
31 deserialized = auth_pb2.AuthRes.FromString(result)
32 assert deserialized.user_id == 12345
33 assert deserialized.jailed is False
35 def test_message_with_sensitive_field(self):
36 """Test that sensitive fields are cleared from messages."""
37 # AuthReq has a sensitive password field
38 proto = auth_pb2.AuthReq(user="testuser", password="supersecret123", remember_device=True)
39 result = _sanitized_bytes(proto)
41 deserialized = auth_pb2.AuthReq.FromString(result)
43 # Non-sensitive fields should remain
44 assert deserialized.user == "testuser"
45 assert deserialized.remember_device is True
47 # Sensitive field should be cleared
48 assert deserialized.password == ""
50 def test_message_with_nested_message_non_repeated(self):
51 """Test that nested messages are recursively sanitized (non-repeated case)."""
52 # SignupFlowReq contains a nested SignupAccount message
53 proto = auth_pb2.SignupFlowReq(
54 flow_token="token123",
55 account=auth_pb2.SignupAccount(username="nesteduser", password="nestedsecret", city="Boston"),
56 )
57 result = _sanitized_bytes(proto)
59 deserialized = auth_pb2.SignupFlowReq.FromString(result)
61 # Top-level fields should remain
62 assert deserialized.flow_token == "token123"
64 # Nested message non-sensitive fields should remain
65 assert deserialized.account.username == "nesteduser"
66 assert deserialized.account.city == "Boston"
68 # Nested message sensitive field should be cleared
69 assert deserialized.account.password == ""
71 def test_message_with_empty_nested_message(self):
72 """Test that empty nested message fields don't cause errors (continue branch)."""
73 # Create a message where nested message field is default/empty
74 # SignupFlowRes with auth_res not set (optional field)
75 proto = auth_pb2.SignupFlowRes(
76 flow_token="token456",
77 need_basic=True,
78 need_account=False,
79 # auth_res is not set
80 )
81 result = _sanitized_bytes(proto)
83 deserialized = auth_pb2.SignupFlowRes.FromString(result)
84 assert deserialized.flow_token == "token456"
85 assert deserialized.need_basic is True
86 assert deserialized.need_account is False
87 # auth_res should still be empty/default
88 assert not deserialized.HasField("auth_res")
90 def test_message_with_empty_repeated_field(self):
91 """
92 Test that empty repeated message fields trigger the continue branch.
94 This covers line 171-172 where submessage evaluates to False (empty list).
95 """
96 # Create a message with a repeated field but don't add any items
97 proto = conversations_pb2.GetGroupChatMessagesRes(
98 last_message_id=999,
99 no_more=True,
100 # messages field is empty (default empty repeated field)
101 )
102 result = _sanitized_bytes(proto)
104 deserialized = conversations_pb2.GetGroupChatMessagesRes.FromString(result)
105 assert deserialized.last_message_id == 999
106 assert deserialized.no_more is True
107 # Repeated field should be empty
108 assert len(deserialized.messages) == 0
110 def test_message_with_repeated_messages(self):
111 """Test that repeated message fields are recursively sanitized."""
112 # Create a message with repeated nested messages
113 # Using GetGroupChatMessagesRes which has repeated Message fields
114 proto = conversations_pb2.GetGroupChatMessagesRes(last_message_id=100, no_more=False)
116 # Add messages to the repeated field
117 msg1 = proto.messages.add()
118 msg1.message_id = 1
119 msg1.author_user_id = 123
120 msg1.text.text = "Hello"
122 msg2 = proto.messages.add()
123 msg2.message_id = 2
124 msg2.author_user_id = 456
125 msg2.text.text = "World"
127 result = _sanitized_bytes(proto)
129 deserialized = conversations_pb2.GetGroupChatMessagesRes.FromString(result)
131 # Check that repeated messages are preserved
132 assert len(deserialized.messages) == 2
133 assert deserialized.messages[0].message_id == 1
134 assert deserialized.messages[0].text.text == "Hello"
135 assert deserialized.messages[0].author_user_id == 123
136 assert deserialized.messages[1].message_id == 2
137 assert deserialized.messages[1].text.text == "World"
138 assert deserialized.messages[1].author_user_id == 456
140 def test_deeply_nested_messages(self):
141 """Test that deeply nested messages are recursively sanitized."""
142 # SignupFlowRes contains AuthRes which is nested
143 proto = auth_pb2.SignupFlowRes(
144 flow_token="deep_token",
145 auth_res=auth_pb2.AuthRes(user_id=789, jailed=False),
146 need_basic=False,
147 need_account=True,
148 )
149 result = _sanitized_bytes(proto)
151 deserialized = auth_pb2.SignupFlowRes.FromString(result)
153 # All nested data should be preserved (no sensitive fields in this structure)
154 assert deserialized.flow_token == "deep_token"
155 assert deserialized.auth_res.user_id == 789
156 assert deserialized.auth_res.jailed is False
157 assert deserialized.need_basic is False
158 assert deserialized.need_account is True
160 def test_multiple_nested_messages_with_sensitive_fields(self):
161 """
162 Test sanitization when a message has multiple nested messages with sensitive fields.
164 This ensures the function properly handles multiple different nested message fields.
165 """
166 # Create a SignupFlowReq with multiple nested messages
167 proto = auth_pb2.SignupFlowReq(
168 flow_token="repeat_token",
169 basic=auth_pb2.SignupBasic(name="Test User", email="test@example.com"),
170 account=auth_pb2.SignupAccount(username="testuser", password="shouldberemoved"),
171 )
172 result = _sanitized_bytes(proto)
174 deserialized = auth_pb2.SignupFlowReq.FromString(result)
176 # Check basic nested message
177 assert deserialized.basic.name == "Test User"
178 assert deserialized.basic.email == "test@example.com"
180 # Check account nested message with sensitive field
181 assert deserialized.account.username == "testuser"
182 assert deserialized.account.password == ""
184 def test_message_preserves_original(self):
185 """Test that the original message is not modified (deepcopy is used)."""
186 original = auth_pb2.AuthReq(user="original_user", password="original_password")
188 # Store original values
189 original_user = original.user
190 original_password = original.password
192 # Call _sanitized_bytes
193 result = _sanitized_bytes(original)
195 # Original should not be modified
196 assert original.user == original_user
197 assert original.password == original_password
199 # But the result should have password cleared
200 deserialized = auth_pb2.AuthReq.FromString(result)
201 assert deserialized.user == original_user
202 assert deserialized.password == ""
204 def test_message_with_non_message_type_fields(self):
205 """Test messages with primitive fields (no message_type)."""
206 # Create a message with only primitive types
207 proto = auth_pb2.AuthRes(user_id=12345, jailed=True)
208 result = _sanitized_bytes(proto)
210 deserialized = auth_pb2.AuthRes.FromString(result)
211 assert deserialized.user_id == 12345
212 assert deserialized.jailed is True
214 def test_complex_nested_structure(self):
215 """Test a complex nested structure to ensure all branches are covered."""
216 # Create a complex nested message
217 proto = auth_pb2.SignupFlowReq(
218 flow_token="complex_token",
219 basic=auth_pb2.SignupBasic(name="Complex User", email="complex@example.com", invite_code="INVITE123"),
220 account=auth_pb2.SignupAccount(
221 username="complexuser",
222 password="complexsecret",
223 city="Seattle",
224 lat=47.6062,
225 lng=-122.3321,
226 birthdate="1985-05-15",
227 gender="other",
228 hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST,
229 accept_tos=True,
230 ),
231 feedback=auth_pb2.ContributorForm(ideas="Great platform!", contribute=auth_pb2.CONTRIBUTE_OPTION_YES),
232 email_token="email_verification_token",
233 )
235 result = _sanitized_bytes(proto)
237 deserialized = auth_pb2.SignupFlowReq.FromString(result)
239 # Verify all non-sensitive fields are preserved
240 assert deserialized.flow_token == "complex_token"
241 assert deserialized.basic.name == "Complex User"
242 assert deserialized.basic.email == "complex@example.com"
243 assert deserialized.basic.invite_code == "INVITE123"
244 assert deserialized.account.username == "complexuser"
245 assert deserialized.account.city == "Seattle"
246 assert deserialized.account.lat == 47.6062
247 assert deserialized.account.birthdate == "1985-05-15"
248 assert deserialized.feedback.ideas == "Great platform!"
249 assert deserialized.email_token == "email_verification_token"
251 # Verify sensitive field is cleared
252 assert deserialized.account.password == ""