@@ -764,6 +764,45 @@ def test_opt_out_and_invalid_opt_in_are_strict(self):
764764 self .assertTrue (self .base .control_write_gate .snapshot ()["closed" ])
765765
766766
767+ class TestControlDebugExport (unittest .TestCase ):
768+ """Live controller objects must never enter shared debug/state serialization."""
769+
770+ def test_actual_debug_export_excludes_live_control_objects (self ):
771+ """Keep debug downloads usable without traversing locks or credentials."""
772+ from datetime import datetime , timezone
773+ from tempfile import TemporaryDirectory
774+
775+ import yaml
776+ from userinterface import UserInterface
777+ from utils import is_debug_excluded_key
778+
779+ with TemporaryDirectory () as root :
780+ base = SimpleNamespace (
781+ now_utc = datetime .now (timezone .utc ),
782+ config_root = root ,
783+ config_root_p = root ,
784+ inverters = [],
785+ CONFIG_ITEMS = [],
786+ components = None ,
787+ args = {},
788+ minutes_now = 42 ,
789+ log = lambda * _args : None ,
790+ )
791+ gate = make_gate ()
792+ service = ControlHandoffService (base , "synthetic-instance" , "synthetic-boot" , "https://example.invalid" , "synthetic-private-service-credential" )
793+ service .gate = gate
794+ base .control_write_gate = gate
795+ base .control_handoff_service = service
796+ text = UserInterface .create_debug_yaml (base , write_file = False )
797+ debug = yaml .safe_load (text )
798+ self .assertEqual (debug ["minutes_now" ], 42 )
799+ for name in ("control_write_gate" , "control_handoff_service" ):
800+ self .assertTrue (is_debug_excluded_key (name ))
801+ self .assertNotIn (name , debug )
802+ self .assertNotIn ("synthetic-private-service-credential" , text )
803+ self .assertNotIn ("python/object" , text )
804+
805+
767806def run_control_handoff_tests (_predbat = None ):
768807 """Run through Predbat's normal unit-test registry as well as unittest."""
769808 suite = unittest .defaultTestLoader .loadTestsFromModule (__import__ (__name__ , fromlist = ["_" ]))
0 commit comments