Harden RF handoff and stream OTA decode
This commit is contained in:
@@ -26,10 +26,15 @@ from tools.tx_agent_watcher import TxWatcherState, process_history_cycle
|
||||
from tools.tx_handoff_channel import (
|
||||
CHANNEL_COMPLETE_OBSERVED,
|
||||
CHANNEL_READY_OBSERVED,
|
||||
TX_ACK_SCHEMA_VERSION,
|
||||
TX_AUTHORIZED,
|
||||
TX_COMPLETE,
|
||||
TX_STARTED,
|
||||
TX_RESULT_PATHS,
|
||||
GitAcknowledgementChannel,
|
||||
TxHandoffAcknowledgement,
|
||||
acknowledgement_from_mapping,
|
||||
build_tx_execution_event,
|
||||
channel_test_acknowledgements,
|
||||
)
|
||||
|
||||
@@ -91,7 +96,7 @@ def channel_ready(**changes: object) -> RxHandoffResult:
|
||||
capture_pid=1234,
|
||||
published_at=NOW.isoformat(),
|
||||
capture_started_at=NOW.isoformat(),
|
||||
expected_capture_end=(NOW + timedelta(seconds=120)).isoformat(),
|
||||
expected_capture_end=(NOW + timedelta(seconds=60)).isoformat(),
|
||||
capture_path="MODEL_FREE_FAKE_CAPTURE",
|
||||
common_clock_verified=False,
|
||||
source_head=SOURCE_HEAD,
|
||||
@@ -175,6 +180,95 @@ def test_acknowledgement_schema_binds_digest_and_source_commit() -> None:
|
||||
acknowledgement_from_mapping({**raw, "rf_tx_authorized": True})
|
||||
|
||||
|
||||
def test_tx_events_preserve_authorization_binding_and_timestamps() -> None:
|
||||
authorization = TxHandoffAcknowledgement(
|
||||
schema_version=TX_ACK_SCHEMA_VERSION,
|
||||
task_id="RF001A_OTA_CAPTURE_004",
|
||||
state=TX_AUTHORIZED,
|
||||
source_rx_commit="1" * 40,
|
||||
source_rx_digest="2" * 64,
|
||||
source_head=SOURCE_HEAD,
|
||||
observed_at=NOW.isoformat(),
|
||||
main_watcher_pid=4321,
|
||||
rf_tx_authorized=True,
|
||||
rf_tx_started=False,
|
||||
rf_tx=0,
|
||||
tx_task_id="RF001A_OTA_TX_004",
|
||||
)
|
||||
started_at = (NOW + timedelta(seconds=1)).isoformat()
|
||||
started = build_tx_execution_event(
|
||||
authorization,
|
||||
state=TX_STARTED,
|
||||
observed_at=started_at,
|
||||
tx_started_at=started_at,
|
||||
)
|
||||
finished_at = (NOW + timedelta(seconds=2.2)).isoformat()
|
||||
complete = build_tx_execution_event(
|
||||
authorization,
|
||||
state=TX_COMPLETE,
|
||||
observed_at=finished_at,
|
||||
tx_started_at=started_at,
|
||||
tx_finished_at=finished_at,
|
||||
process_exit_code=0,
|
||||
underruns=0,
|
||||
errors=0,
|
||||
)
|
||||
assert started.source_rx_commit == authorization.source_rx_commit
|
||||
assert started.source_rx_digest == authorization.source_rx_digest
|
||||
assert started.rf_tx_started and started.rf_tx == 1
|
||||
assert complete.tx_finished_at == finished_at
|
||||
assert complete.tx_process_exit_code == 0
|
||||
|
||||
|
||||
def test_execution_event_publication_requires_authorization_and_start(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
_remote, work, _tx_root = repository_fixture(tmp_path)
|
||||
channel = GitAcknowledgementChannel(work)
|
||||
authorization = TxHandoffAcknowledgement(
|
||||
schema_version=TX_ACK_SCHEMA_VERSION,
|
||||
task_id="RF001A_OTA_CAPTURE_004",
|
||||
state=TX_AUTHORIZED,
|
||||
source_rx_commit="1" * 40,
|
||||
source_rx_digest="2" * 64,
|
||||
source_head=SOURCE_HEAD,
|
||||
observed_at=NOW.isoformat(),
|
||||
main_watcher_pid=4321,
|
||||
rf_tx_authorized=True,
|
||||
rf_tx_started=False,
|
||||
rf_tx=0,
|
||||
tx_task_id="RF001A_OTA_TX_004",
|
||||
)
|
||||
started_at = (NOW + timedelta(seconds=1)).isoformat()
|
||||
finished_at = (NOW + timedelta(seconds=2.2)).isoformat()
|
||||
started = build_tx_execution_event(
|
||||
authorization,
|
||||
state=TX_STARTED,
|
||||
observed_at=started_at,
|
||||
tx_started_at=started_at,
|
||||
)
|
||||
complete = build_tx_execution_event(
|
||||
authorization,
|
||||
state=TX_COMPLETE,
|
||||
observed_at=finished_at,
|
||||
tx_started_at=started_at,
|
||||
tx_finished_at=finished_at,
|
||||
process_exit_code=0,
|
||||
underruns=0,
|
||||
errors=0,
|
||||
)
|
||||
|
||||
with pytest.raises(ResultChannelError, match="TX_AUTHORIZED"):
|
||||
channel.publish_execution_event(started)
|
||||
channel.publish(authorization)
|
||||
with pytest.raises(ResultChannelError, match="TX_STARTED"):
|
||||
channel.publish_execution_event(complete)
|
||||
started_outcome = channel.publish_execution_event(started)
|
||||
complete_outcome = channel.publish_execution_event(complete)
|
||||
assert started_outcome.status == "PUBLISHED"
|
||||
assert complete_outcome.status == "PUBLISHED"
|
||||
|
||||
|
||||
def test_main_publish_uses_exact_allowlist_and_suppresses_duplicate(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user