@@ -145,9 +145,11 @@ def drop_test_events(event):
145145 self .assertEqual (enqueued_msg ["event" ], "keep_me" )
146146
147147 def test_before_send_callback_handles_exceptions (self ):
148- """Test that exceptions in before_send don't crash the client ."""
148+ """Test that exceptions in before_send drop the event without crashing ."""
149149
150150 def buggy_before_send (event ):
151+ event ["properties" ]["partially_mutated" ] = True
152+ event ["uuid" ] = "invalid"
151153 raise ValueError ("Oops!" )
152154
153155 with mock .patch ("posthog.client.batch_post" ) as mock_post :
@@ -157,16 +159,49 @@ def buggy_before_send(event):
157159 before_send = buggy_before_send ,
158160 sync_mode = True ,
159161 )
160- msg_uuid = client .capture ("robust_event" , distinct_id = "user1" )
162+ with (
163+ mock .patch .object (
164+ client ,
165+ "_normalize_event_uuid" ,
166+ wraps = client ._normalize_event_uuid ,
167+ ) as normalize_uuid ,
168+ self .assertLogs ("posthog" , level = "ERROR" ) as logs ,
169+ ):
170+ msg_uuid = client .capture ("robust_event" , distinct_id = "user1" )
161171
162- # Event should still be sent despite the exception
163- self .assertIsNotNone (msg_uuid )
172+ self .assertIsNone (msg_uuid )
173+ mock_post .assert_not_called ()
174+ normalize_uuid .assert_called_once ()
175+ self .assertIn ("Error in before_send callback: Oops!" , logs .output [0 ])
164176
165- # Check the enqueued message
166- mock_post .assert_called_once ()
167- batch_data = mock_post .call_args [1 ]["batch" ]
168- enqueued_msg = batch_data [0 ]
169- self .assertEqual (enqueued_msg ["event" ], "robust_event" )
177+ def test_before_send_callback_exception_does_not_enqueue_mutated_event (self ):
178+ def buggy_before_send (event ):
179+ event ["properties" ]["partially_mutated" ] = True
180+ raise ValueError ("Oops!" )
181+
182+ client = Client (
183+ FAKE_TEST_API_KEY ,
184+ on_error = self .set_fail ,
185+ before_send = buggy_before_send ,
186+ flush_at = 1 ,
187+ )
188+ try :
189+ with (
190+ mock .patch ("posthog.consumer.batch_post" ) as mock_post ,
191+ mock .patch .object (
192+ client ._analytics_lane ,
193+ "enqueue" ,
194+ wraps = client ._analytics_lane .enqueue ,
195+ ) as enqueue ,
196+ self .assertLogs ("posthog" , level = "ERROR" ),
197+ ):
198+ msg_uuid = client .capture ("robust_event" , distinct_id = "user1" )
199+
200+ self .assertIsNone (msg_uuid )
201+ enqueue .assert_not_called ()
202+ mock_post .assert_not_called ()
203+ finally :
204+ client .shutdown ()
170205
171206 def test_before_send_callback_output_is_recleaned (self ):
172207 marker = object ()
@@ -186,7 +221,7 @@ def add_unsupported_value(event):
186221 sent_event = mock_post .call_args .kwargs ["batch" ][0 ]
187222 self .assertIsNone (sent_event ["properties" ]["marker" ])
188223
189- def test_before_send_callback_non_dict_output_uses_original_event (self ):
224+ def test_before_send_callback_non_dict_output_drops_event (self ):
190225 with (
191226 mock .patch ("posthog.client.batch_post" ) as mock_post ,
192227 mock .patch ("posthog.client.Client.log.exception" ) as mock_log ,
@@ -196,10 +231,9 @@ def test_before_send_callback_non_dict_output_uses_original_event(self):
196231 before_send = lambda _event : "invalid" ,
197232 sync_mode = True ,
198233 )
199- self .assertIsNotNone (client .capture ("original" , distinct_id = "user1" ))
234+ self .assertIsNone (client .capture ("original" , distinct_id = "user1" ))
200235
201- sent_event = mock_post .call_args .kwargs ["batch" ][0 ]
202- self .assertEqual (sent_event ["event" ], "original" )
236+ mock_post .assert_not_called ()
203237 self .assertIn (
204238 "before_send must return a dict or None" , mock_log .call_args .args [0 ]
205239 )
0 commit comments