1212import pytest
1313
1414from sentience .cloud_tracing import CloudTraceSink
15- from sentience .models import ScreenshotMetadata
1615from sentience .tracer_factory import create_tracer
1716from sentience .tracing import JsonlTraceSink , Tracer
1817
@@ -241,10 +240,26 @@ def test_cloud_trace_sink_uploads_screenshots_after_trace(self):
241240
242241 # Create test screenshot
243242 test_image_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
244- test_data_url = f"data:image/png;base64,{ test_image_base64 } "
245243
246244 sink = CloudTraceSink (upload_url , run_id = run_id , api_key = api_key )
247- sink .store_screenshot (sequence = 1 , screenshot_data = test_data_url , format = "png" )
245+
246+ # Emit trace event with screenshot embedded
247+ sink .emit (
248+ {
249+ "v" : 1 ,
250+ "type" : "snapshot" ,
251+ "ts" : "2026-01-01T00:00:00.000Z" ,
252+ "run_id" : run_id ,
253+ "seq" : 1 ,
254+ "step_id" : "step-1" ,
255+ "data" : {
256+ "url" : "https://example.com" ,
257+ "element_count" : 10 ,
258+ "screenshot_base64" : test_image_base64 ,
259+ "screenshot_format" : "png" ,
260+ },
261+ }
262+ )
248263
249264 # Mock all HTTP calls
250265 mock_upload_urls = {
@@ -289,8 +304,7 @@ def post_side_effect(*args, **kwargs):
289304 mock_put .side_effect = put_side_effect
290305 mock_post .side_effect = post_side_effect
291306
292- # Emit trace event and close
293- sink .emit ({"v" : 1 , "type" : "run_start" , "seq" : 1 })
307+ # Close triggers upload (which extracts screenshots and uploads them)
294308 sink .close ()
295309
296310 # Verify trace was uploaded
@@ -304,13 +318,38 @@ def post_side_effect(*args, **kwargs):
304318 put_urls = [call [0 ][0 ] for call in mock_put .call_args_list ]
305319 assert any ("screenshots" in url for url in put_urls )
306320
321+ # Verify uploaded trace data does NOT contain screenshot_base64
322+ trace_upload_call = None
323+ for call in mock_put .call_args_list :
324+ headers = call [1 ].get ("headers" , {})
325+ if headers .get ("Content-Type" ) == "application/x-gzip" :
326+ trace_upload_call = call
327+ break
328+
329+ assert trace_upload_call is not None , "Trace upload should have been called"
330+
331+ # Decompress and verify screenshot_base64 is removed
332+ compressed_data = trace_upload_call [1 ]["data" ]
333+ decompressed_data = gzip .decompress (compressed_data )
334+ trace_content = decompressed_data .decode ("utf-8" )
335+ events = [json .loads (line ) for line in trace_content .strip ().split ("\n " ) if line .strip ()]
336+
337+ snapshot_events = [e for e in events if e .get ("type" ) == "snapshot" ]
338+ assert len (snapshot_events ) > 0 , "Should have snapshot event"
339+
340+ for event in snapshot_events :
341+ data = event .get ("data" , {})
342+ assert "screenshot_base64" not in data , "screenshot_base64 should be removed from uploaded trace"
343+ assert "screenshot_format" not in data , "screenshot_format should be removed from uploaded trace"
344+
307345 # Cleanup
308346 cache_dir = Path .home () / ".sentience" / "traces" / "pending"
309- screenshot_dir = cache_dir / f"{ run_id } _screenshots"
310- if screenshot_dir .exists ():
311- for f in screenshot_dir .glob ("step_*" ):
312- f .unlink ()
313- screenshot_dir .rmdir ()
347+ trace_path = cache_dir / f"{ run_id } .jsonl"
348+ cleaned_trace_path = cache_dir / f"{ run_id } .cleaned.jsonl"
349+ if trace_path .exists ():
350+ os .remove (trace_path )
351+ if cleaned_trace_path .exists ():
352+ os .remove (cleaned_trace_path )
314353
315354
316355class TestTracerFactory :
0 commit comments