1- from typing import Literal
1+ import json
22import os
3+ from pathlib import Path
4+ from typing import Literal , cast
5+
36import pytest
47
58from docker .models .networks import Network
@@ -466,9 +469,9 @@ def _wait_and_stop_containers(self, *, is_empty_test_run: bool):
466469 else :
467470 self ._wait_interface (interfaces .library , 0 if is_empty_test_run else self .library_interface_timeout )
468471
469- # An empty selection has no test-generated data to flush. This also avoids waiting on
470- # Agent-backed writers in scenarios that intentionally do not start an Agent .
471- self .weblog_infra .stop (flush = not is_empty_test_run )
472+ # An empty selection has no test-generated data to flush. An Agentless scenario also
473+ # has no Agent-backed writer target, so its flush endpoint can only time out .
474+ self .weblog_infra .stop (flush = not is_empty_test_run and self . include_agent )
472475 interfaces .library .check_deserialization_errors ()
473476
474477 for container in self .buddies :
@@ -615,6 +618,7 @@ class FeatureFlaggingAgentlessEndToEndScenario(DdTraceEndToEndScenario):
615618 """FFE end-to-end scenario with UFC available before the weblog starts."""
616619
617620 _default_scenario_groups : tuple [ScenarioGroup , ...] = ()
621+ _mock_backend_status_filename = "mock_ffe_agentless_backend_status.json"
618622
619623 _mock_backend : MockFFEAgentlessBackendServer | None = None
620624 _last_mock_backend_status : MockFFEAgentlessBackendStatus | None = None
@@ -627,7 +631,6 @@ def __init__(
627631 weblog_env : dict [str , str | None ] | None = None ,
628632 ) -> None :
629633 environment : dict [str , str | None ] = {
630- "DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED" : "true" ,
631634 "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS" : "0.2" ,
632635 "DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS" : "2" ,
633636 "DD_REMOTE_CONFIGURATION_ENABLED" : "false" ,
@@ -647,12 +650,15 @@ def __init__(
647650
648651 def configure (self , config : pytest .Config ) -> None :
649652 try :
650- if not self .replay :
653+ if self .replay :
654+ self ._load_mock_backend_status ()
655+ else :
656+ self ._last_mock_backend_status = None
651657 self ._start_mock_backend ()
652658
653659 super ().configure (config )
654660 except BaseException :
655- self ._stop_mock_backend ()
661+ self ._stop_mock_backend (persist_status = False )
656662 raise
657663
658664 def _start_mock_backend (self ) -> None :
@@ -673,14 +679,30 @@ def mock_backend_status(self) -> MockFFEAgentlessBackendStatus | None:
673679 return self ._mock_backend .status ()
674680 return self ._last_mock_backend_status
675681
676- def _stop_mock_backend (self ) -> None :
682+ @property
683+ def _mock_backend_status_path (self ) -> Path :
684+ return Path (self .host_log_folder ) / self ._mock_backend_status_filename
685+
686+ def _load_mock_backend_status (self ) -> None :
687+ self ._last_mock_backend_status = cast (
688+ "MockFFEAgentlessBackendStatus" ,
689+ json .loads (self ._mock_backend_status_path .read_text (encoding = "utf-8" )),
690+ )
691+
692+ def _stop_mock_backend (self , * , persist_status : bool = True ) -> None :
677693 backend = self ._mock_backend
678694 if backend is None :
679695 return
680696
681697 self ._mock_backend = None
682698 try :
683- self ._last_mock_backend_status = backend .status ()
699+ if persist_status :
700+ self ._last_mock_backend_status = backend .status ()
701+ self ._mock_backend_status_path .parent .mkdir (parents = True , exist_ok = True )
702+ self ._mock_backend_status_path .write_text (
703+ json .dumps (self ._last_mock_backend_status , indent = 2 ) + "\n " ,
704+ encoding = "utf-8" ,
705+ )
684706 finally :
685707 backend .close ()
686708
0 commit comments