from __future__ import annotations
import ast, importlib.util, json
from pathlib import Path
import pytest
ROOT=Path(__file__).resolve().parent
spec=importlib.util.spec_from_file_location('controller',ROOT/'launch_controller_v6.py'); assert spec and spec.loader
c=importlib.util.module_from_spec(spec);spec.loader.exec_module(c)
def test_controller_uses_only_read_only_bootstrap_api():
 text=(ROOT/'launch_controller_v6.py').read_text()
 assert 'load_gateway_'+'config' not in text
 assert 'load_gateway_preflight_inputs' in text
 assert "['get_me','get_chat_member(bot_admin)']" in text
def test_failed_launch_chain_is_bound():
 assert c.V5_CLEAN_RUN_SEAL_SHA=='ba8226e9e7cc036560070d43e2d90a4c7bd56345fc0dc2148ea1a69ba986b1f9'
 assert c.sha(c.PREDECESSOR/'run-20260817-strict-a-v5/RUN-SEAL-v5.json')==c.V5_CLEAN_RUN_SEAL_SHA
def test_order_prepares_before_deploy_and_runtime():
 tree=ast.parse((ROOT/'launch_controller_v6.py').read_text())
 fn=next(x for x in tree.body if isinstance(x,(ast.FunctionDef,ast.AsyncFunctionDef)) and x.name=='execute')
 calls=[x.func.id for x in ast.walk(fn) if isinstance(x,ast.Call) and isinstance(x.func,ast.Name)]
 assert calls.index('prepare') < calls.index('deploy') < calls.index('start_runtime')
def test_boundary_model_is_one_use_fail_closed():
 m=c.BoundaryModel()
 for name in c.BOUNDARIES:m.cross(name)
 assert m.sent==1
 with pytest.raises(c.Blocked):m.cross('invite_result')
def test_dry_transport_is_read_only_contract():
 source=(ROOT/'launch_controller_v6.py').read_text()
 assert "'network_executed':False" in source
 assert "'authority_tree_mutations':0" in source
