@@ -424,6 +424,78 @@ async def test_max_alleles_filter_through_start(
424424 assert not client ._proc .is_alive ()
425425 assert client ._proc .exitcode == 0
426426
427+ @pytest .mark .parametrize ("no_header_samples" , [True , False ])
428+ async def test_bgen_no_header_samples_stable_across_opens (
429+ self , fx_small_vcz , tmp_path , no_header_samples
430+ ):
431+ """``--no-header-samples`` flows into every per-connection
432+ ``BgenEncoder`` consistently.
433+
434+ Each ``open_stream`` spawns a fresh server-side thread that
435+ constructs a new encoder via ``spec.encoder_factory(reader, opts)``.
436+ Three sequential open/read/close cycles must all return the same
437+ bytes — and those bytes must match an in-process reference
438+ ``BgenEncoder`` constructed with the matching
439+ ``embed_header_samples`` flag.
440+ """
441+ opts = dataclasses .replace (
442+ vcztools .ViewBgenOptions (), no_header_samples = no_header_samples
443+ )
444+ ref_reader = opts .make_reader (str (fx_small_vcz .path ))
445+ with vcztools .BgenEncoder (
446+ ref_reader , embed_header_samples = not no_header_samples
447+ ) as ref :
448+ expected_size = int (ref .total_size )
449+ expected = ref .read (0 , expected_size )
450+
451+ socket_path = tmp_path / "encoder.sock"
452+ async with await encoder_client .EncoderClient .start (
453+ str (fx_small_vcz .path ), socket_path , formats .BGEN_SPEC , opts = opts
454+ ) as client :
455+ assert client .stream_size == expected_size
456+ for cycle in range (3 ):
457+ conn = await client .open_stream ()
458+ try :
459+ data = await conn .read (0 , client .stream_size )
460+ finally :
461+ await conn .aclose ()
462+ assert data == expected , f"cycle { cycle } differed from reference"
463+
464+ @pytest .mark .parametrize (
465+ ("no_sample_file" , "no_bgi" ),
466+ [(True , False ), (False , True ), (True , True ), (False , False )],
467+ )
468+ async def test_bgen_sidecar_toggles_stable_across_starts (
469+ self , fx_small_vcz , tmp_path , no_sample_file , no_bgi
470+ ):
471+ """The handshake's ``static_files`` honours ``--no-sample-file``
472+ / ``--no-bgi`` and yields the same suffix set on each
473+ ``EncoderClient.start`` against the same options."""
474+ opts = dataclasses .replace (
475+ vcztools .ViewBgenOptions (),
476+ no_sample_file = no_sample_file ,
477+ no_bgi = no_bgi ,
478+ )
479+ expected_suffixes = formats .BGEN_SPEC .static_suffixes (opts )
480+ first : dict [str , bytes ] | None = None
481+ for cycle in range (3 ):
482+ socket_path = tmp_path / f"encoder-{ cycle } .sock"
483+ async with await encoder_client .EncoderClient .start (
484+ str (fx_small_vcz .path ), socket_path , formats .BGEN_SPEC , opts = opts
485+ ) as client :
486+ assert tuple (client .static_files ) == expected_suffixes
487+ # ``.bgen.bgi`` SQLite payloads have non-deterministic
488+ # header bytes across independent writes; compare sizes
489+ # only for that entry and the full body for ``.sample``.
490+ if first is None :
491+ first = dict (client .static_files )
492+ continue
493+ for suffix , body in client .static_files .items ():
494+ if suffix == ".bgen.bgi" :
495+ assert len (body ) == len (first [suffix ])
496+ else :
497+ assert body == first [suffix ]
498+
427499 @pytest .mark .parametrize (
428500 "spec" , [formats .PLINK_SPEC , formats .BGEN_SPEC ], ids = ["plink" , "bgen" ]
429501 )
0 commit comments