@@ -51,6 +51,17 @@ class ProtocolFixture(BaseModel):
5151 substitutions : dict [str , str ] = Field (default_factory = dict )
5252
5353
54+ class ReplayResult (BaseModel ):
55+ name : str
56+ protocol_family : ProtocolFamily
57+ status_matches : bool
58+ visible_bytes_match : bool
59+ expected_status_code : int
60+ actual_status_code : int
61+ expected_visible_bytes : int
62+ actual_visible_bytes : int
63+
64+
5465def _substitution_for (secret : str ) -> str :
5566 digest = blake3 .blake3 (secret .encode ("utf-8" )).hexdigest ()
5667 return f"credential:blake3:{ digest } "
@@ -312,10 +323,41 @@ def record_debug_upstream(
312323 return written
313324
314325
326+ def replay_fixtures (base_url : str , fixture_paths : list [str | Path ]) -> list [ReplayResult ]:
327+ results : list [ReplayResult ] = []
328+ for path in fixture_paths :
329+ fixture = ProtocolFixture .model_validate_json (Path (path ).read_text ())
330+ exchange , visible_bytes , _substitutions = _http_exchange (
331+ base_url ,
332+ fixture .exchange .method ,
333+ fixture .exchange .path ,
334+ headers = dict (fixture .exchange .request_headers ),
335+ body = fixture .exchange .request_body ,
336+ )
337+ results .append (
338+ ReplayResult (
339+ name = fixture .name ,
340+ protocol_family = fixture .protocol_family ,
341+ status_matches = exchange .status_code == fixture .exchange .status_code ,
342+ visible_bytes_match = visible_bytes == fixture .expected_visible_bytes ,
343+ expected_status_code = fixture .exchange .status_code ,
344+ actual_status_code = exchange .status_code ,
345+ expected_visible_bytes = fixture .expected_visible_bytes ,
346+ actual_visible_bytes = visible_bytes ,
347+ )
348+ )
349+ return results
350+
351+
315352def main () -> int :
316353 parser = argparse .ArgumentParser (description = __doc__ )
317354 parser .add_argument ("--base-url" , required = True , help = "capsem-debug-upstream base URL" )
318355 parser .add_argument ("--out-dir" , required = True , type = Path , help = "fixture output directory" )
356+ parser .add_argument (
357+ "--replay" ,
358+ action = "store_true" ,
359+ help = "replay written fixtures after recording and include replay results" ,
360+ )
319361 parser .add_argument (
320362 "--scenario" ,
321363 action = "append" ,
@@ -328,7 +370,12 @@ def main() -> int:
328370 args .out_dir ,
329371 scenarios = set (args .scenarios ) if args .scenarios else None ,
330372 )
331- print (json .dumps ({"written" : [str (path ) for path in written ]}, indent = 2 ))
373+ output : dict [str , Any ] = {"written" : [str (path ) for path in written ]}
374+ if args .replay :
375+ output ["replay" ] = [
376+ result .model_dump () for result in replay_fixtures (args .base_url , written )
377+ ]
378+ print (json .dumps (output , indent = 2 ))
332379 return 0
333380
334381
0 commit comments