fix(k1): record redacted acceptance failures

This commit is contained in:
DCCONSTRUCTIONS
2026-07-18 16:11:29 +03:00
parent eddc09008e
commit a29b38a0d7
6 changed files with 187 additions and 18 deletions
@@ -1,5 +1,6 @@
from __future__ import annotations
import hashlib
from collections.abc import Collection, Sequence
import pytest
@@ -11,6 +12,7 @@ from k1link.device_plugins.xgrids_k1.protocol.application_acceptance import (
PhysicalAcceptancePermit,
)
from k1link.device_plugins.xgrids_k1.protocol.application_bootstrap import (
DEVICE_CONFIG_RESPONSE_TOPIC,
ApplicationControlAuthority,
ShadowApplicationBootstrapOrchestrator,
)
@@ -186,10 +188,71 @@ def test_start_acceptance_requires_full_bootstrap_and_consumes_one_short_permit(
assert permit.snapshot()["consumed"] is True
assert executor.snapshot()["bootstrap_complete"] is True
assert executor.snapshot()["command_complete"] is True
response_evidence = executor.snapshot()["response_evidence"]
assert isinstance(response_evidence, tuple)
assert len(response_evidence) == 10
assert response_evidence[0]["operation_key"] == "bootstrap:1:DeviceInfoRequest"
assert response_evidence[-1]["operation_key"] == "modeling:start"
assert all("payload" not in item for item in response_evidence)
assert APPLICATION_KEY not in str(executor.snapshot())
assert VENDOR_DEVICE_ID not in str(executor.snapshot())
with pytest.raises(ApplicationAcceptanceError, match="already attempted"):
executor.execute_modeling(command)
def test_bootstrap_correlation_failure_records_only_redacted_response_evidence() -> None:
class CorruptDeviceConfigTransport(SyntheticAcceptanceTransport):
def exchange_batch_once(
self,
envelopes: Sequence[OneShotPublishEnvelope],
*,
required_response_topics: Collection[str],
) -> dict[str, bytes]:
responses = super().exchange_batch_once(
envelopes,
required_response_topics=required_response_topics,
)
if DEVICE_CONFIG_RESPONSE_TOPIC in responses:
responses[DEVICE_CONFIG_RESPONSE_TOPIC] = _generic_response(
f"{VENDOR_DEVICE_ID}:wrong-session"
)
return responses
executor = PhysicalAcceptanceDialogueExecutor(
CorruptDeviceConfigTransport(),
PhysicalAcceptancePermit(_checklist(ModelingAction.START)),
)
orchestrator = ShadowApplicationBootstrapOrchestrator(
ApplicationControlAuthority(openapi_key=APPLICATION_KEY),
epoch_seconds=1_752_680_000,
timezone_name="Europe/Moscow",
)
with pytest.raises(
ApplicationAcceptanceError,
match=r"ordinal 4 \(DeviceConfigRequest\).*session correlation",
):
executor.run_bootstrap(orchestrator)
snapshot = executor.snapshot()
failure = snapshot["correlation_failure"]
assert failure == {
"phase": "bootstrap",
"operation_key": "bootstrap:4:DeviceConfigRequest",
"response_topic": DEVICE_CONFIG_RESPONSE_TOPIC,
"reason": "application response session correlation failed",
}
evidence = snapshot["response_evidence"]
assert isinstance(evidence, tuple)
assert evidence[-1]["payload_sha256"] == hashlib.sha256(
_generic_response(f"{VENDOR_DEVICE_ID}:wrong-session")
).hexdigest()
assert evidence[-1]["payload_bytes"] > 0
assert all("payload" not in item for item in evidence)
assert APPLICATION_KEY not in str(snapshot)
assert VENDOR_DEVICE_ID not in str(snapshot)
def test_start_cannot_skip_bootstrap_and_stop_uses_a_separate_action_permit() -> None:
transport = SyntheticAcceptanceTransport()
start = PhysicalAcceptanceDialogueExecutor(