fix(k1): restore canonical local connection lifecycle

This commit is contained in:
DCCONSTRUCTIONS
2026-08-06 13:52:46 +03:00
parent 52da9b75b7
commit aff331082f
19 changed files with 2169 additions and 274 deletions
+284 -4
View File
@@ -200,14 +200,18 @@ def service_with_fake_runtime(
return service, runtime
def _wifi_status_read(ipv4: str | None) -> dict[str, Any]:
def _wifi_status_read(
ipv4: str | None,
*,
device_id: str = "test-ble-transport",
) -> dict[str, Any]:
return {
"schema_version": 1,
"profile_id": "xgrids-k1-fw3-wifi-v1",
"observed_at_utc": "2026-07-20T12:00:00Z",
"adapter": "CoreBluetooth",
"bleak_version": "test",
"device_macos_uuid": "test-ble-transport",
"device_macos_uuid": device_id,
"device_name": "XGR-K1",
"service_uuid": "00007f00-0000-1000-8000-00805f9b34fb",
"status_characteristic_uuid": "00007f02-0000-1000-8000-00805f9b34fb",
@@ -2867,6 +2871,7 @@ def test_quick_connect_activates_the_device_ap_then_associates_the_host(
"ready_observed": True,
"write_performed": True,
"write_mode": "with_response",
"observations": [{"status": {"mode": "WIFI_AP"}}],
}
finally:
ble_session_open = False
@@ -3090,6 +3095,7 @@ def test_quick_connect_does_not_start_host_wifi_without_ap_ready_flag(
"ready_observed": False,
"write_performed": True,
"write_mode": "with_response",
"observations": [],
}
def forbidden_association(*_: object, **__: object) -> dict[str, Any]:
@@ -3121,6 +3127,9 @@ def test_quick_connect_does_not_start_host_wifi_without_ap_ready_flag(
assert len(quick_sessions) == 1
assert (quick_sessions[0] / "ap-activation.redacted.json").exists()
assert not (quick_sessions[0] / "manifest.redacted.json").exists()
reconciliation = service.state()["network_write_reconciliation"]
assert reconciliation["transport_ref"] == "k1-a"
assert reconciliation["status"] == "device-state-unknown-after-write"
def test_failed_connection_change_revokes_the_previous_route(
@@ -3154,6 +3163,7 @@ def test_failed_connection_change_revokes_the_previous_route(
"ready_observed": True,
"write_performed": True,
"write_mode": "with_response",
"observations": [{"status": {"mode": "WIFI_AP"}}],
}
def failed_association(*_: object, **__: object) -> dict[str, Any]:
@@ -3195,6 +3205,75 @@ def test_failed_connection_change_revokes_the_previous_route(
assert failure_evidence["scan_elapsed_ms"] == 15014
def test_quick_connect_helper_build_failure_after_ap_write_preserves_side_effect_facts(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
_set_scanned_devices(service, [{"device_id": "k1-a", "name": "XGR-TEST-A"}])
monkeypatch.setattr(
facade_module,
"ensure_wifi_profile_from_credential_source",
lambda *_args, **_kwargs: {
"schema_version": 1,
"adapter": "macOS Keychain",
"available": True,
"profile_enrolled": False,
"credential_source": "exact-firmware-profile",
},
)
@asynccontextmanager
async def ready_activation(*_: object, **__: object) -> AsyncIterator[dict[str, Any]]:
yield {
"profile_id": "xgrids-k1-fw3-quick-connect-ap-v1",
"started_at_utc": "2026-08-06T10:00:00Z",
"completed_at_utc": "2026-08-06T10:00:01Z",
"outcome": "ap_ready_observed",
"ready_observed": True,
"write_performed": True,
"write_mode": "with_response",
"observations": [{"status": {"mode": "WIFI_AP"}}],
}
def failed_post_write_build(*_: object, **__: object) -> dict[str, Any]:
raise facade_module.HostWifiProfileError(
"host-wifi-helper-build-failed",
helper_stage="compile",
helper_elapsed_ms=21,
)
monkeypatch.setattr(facade_module, "device_ap_activation_session", ready_activation)
monkeypatch.setattr(
facade_module,
"associate_with_wifi_profile_once",
failed_post_write_build,
)
with pytest.raises(
facade_module.HostWifiProfileError,
match="host-wifi-helper-build-failed",
):
asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
connection_mode="quick-connect",
compatibility_attestation=QUICK_CONNECT_ATTESTATION,
)
)
)
state = service.state()
operation = next(item for item in state["operations"] if item["action"] == "network.provision")
assert operation["stage_code"] == "host-wifi-association-failed"
assert operation["error"]["code"] == "host-wifi-helper-build-failed"
assert operation["error"]["side_effect_status"] == "confirmed"
assert operation["error"]["safe_to_retry"] is False
assert operation["error"]["helper_stage"] == "compile"
assert state["network_write_reconciliation"] is None
def test_network_provisioning_rejects_an_ipv4_owned_by_the_local_host(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
@@ -3236,7 +3315,8 @@ def test_network_provisioning_rejects_an_ipv4_owned_by_the_local_host(
operation = next(item for item in state["operations"] if item["action"] == "network.provision")
assert operation["status"] == "failed"
assert operation["error"]["safe_to_retry"] is False
assert operation["error"]["side_effect_status"] == "unknown"
assert operation["error"]["side_effect_status"] == "confirmed"
assert state["network_write_reconciliation"] is None
failure_log = next(
record
for record in caplog.records
@@ -3246,12 +3326,212 @@ def test_network_provisioning_rejects_an_ipv4_owned_by_the_local_host(
assert failure_log.connection_mode == "bridge"
assert failure_log.error_code == "RuntimeError"
assert failure_log.safe_to_retry is False
assert failure_log.side_effect_status == "unknown"
assert failure_log.side_effect_status == "confirmed"
assert failure_log.network_change_attempted is True
assert failure_log.device_write_attempted is True
assert failure_log.device_write_confirmed is True
assert PRIMARY_TEST_CREDENTIAL not in caplog.text
assert "lab-network" not in caplog.text
def test_ambiguous_ble_write_blocks_new_network_mutation_until_reconciled(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
_set_scanned_devices(service, [{"device_id": "k1-a"}])
calls = 0
async def ambiguous_write(*_: object, **__: object) -> dict[str, Any]:
nonlocal calls
calls += 1
exc = RuntimeError("synthetic transport failure")
exc.operation_stage = "gatt-write" # type: ignore[attr-defined]
exc.device_write_attempted = True # type: ignore[attr-defined]
exc.device_write_confirmed = False # type: ignore[attr-defined]
exc.att_error_code = 4 # type: ignore[attr-defined]
exc.att_error_name = "INVALID_PDU" # type: ignore[attr-defined]
raise exc
monkeypatch.setattr(facade_module, "provision_wifi_once", ambiguous_write)
with pytest.raises(RuntimeError, match="synthetic transport failure"):
asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
ssid="lab-network",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
compatibility_attestation=ATTESTATION,
idempotency_key="ambiguous-write-1",
)
)
)
state = service.state()
operation = next(item for item in state["operations"] if item["action"] == "network.provision")
assert operation["error"] == {
"category": "device",
"code": "RuntimeError",
"retryable": False,
"safe_to_retry": False,
"side_effect_status": "unknown",
"operation_stage": "gatt-write",
"device_write_attempted": True,
"device_write_confirmed": False,
"ble_att_error_code": 4,
"ble_att_error_name": "INVALID_PDU",
}
assert state["network_write_reconciliation"] == {
"status": "device-state-unknown-after-write",
"operation_id": operation["operation_id"],
"transport_ref": "k1-a",
"connection_mode": "bridge",
"operation_stage": "gatt-write",
"reason_code": "RuntimeError",
"device_write_confirmed": False,
"required_action": "explicit-read-only-ble-status-observation",
"scope": "process-runtime",
"observed_at": state["network_write_reconciliation"]["observed_at"],
}
with pytest.raises(
facade_module.NetworkWriteReconciliationRequired,
match="новая запись заблокирована",
):
asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
ssid="another-network",
password=SecretStr(SECONDARY_TEST_CREDENTIAL),
compatibility_attestation=ATTESTATION,
idempotency_key="ambiguous-write-2",
)
)
)
assert calls == 1
def test_unchanged_status_does_not_confirm_without_response_write(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
caplog: pytest.LogCaptureFixture,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
_set_scanned_devices(service, [{"device_id": "k1-a"}])
unchanged_status = {"ipv4": None, "mode": "WIFI_CLIENT"}
async def unchanged_write(*_: object, **__: object) -> dict[str, Any]:
return {
"started_at_utc": "2026-08-06T10:00:00Z",
"completed_at_utc": "2026-08-06T10:00:01Z",
"profile_id": "xgrids-k1-fw3-wifi-v1",
"outcome": "no_status_change_before_timeout",
"write_mode": "without_response",
"baseline_status": unchanged_status,
"observations": [{"status": unchanged_status}],
}
monkeypatch.setattr(facade_module, "provision_wifi_once", unchanged_write)
with (
caplog.at_level(logging.ERROR, logger=facade_module.__name__),
pytest.raises(RuntimeError, match="не сообщило адрес"),
):
asyncio.run(
service.connect(
ConnectRequest(
device_id="k1-a",
ssid="lab-network",
password=SecretStr(PRIMARY_TEST_CREDENTIAL),
compatibility_attestation=ATTESTATION,
)
)
)
record = next(
item
for item in caplog.records
if getattr(item, "event_code", None) == "k1_network_provision_failed"
)
assert record.device_write_attempted is True
assert record.device_write_confirmed is False
assert record.side_effect_status == "unknown"
assert service.state()["network_write_reconciliation"] is None
def test_read_only_ble_status_clears_network_write_reconciliation_fence(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
_set_scanned_k1(service)
service._network_write_reconciliation = { # noqa: SLF001
"status": "device-state-unknown-after-write",
"operation_id": "ambiguous-operation",
"transport_ref": "test-ble-transport",
"connection_mode": "bridge",
"operation_stage": "gatt-write",
"reason_code": "BleakGATTProtocolError",
"device_write_confirmed": False,
"required_action": "explicit-read-only-ble-status-observation",
"scope": "process-runtime",
"observed_at": "2026-08-06T10:00:00Z",
}
async def read_current_status(*_: object, **__: object) -> dict[str, Any]:
return _wifi_status_read(None)
monkeypatch.setattr(facade_module, "read_wifi_status_once", read_current_status)
with pytest.raises(RuntimeError, match="не сообщил актуальный DHCP-адрес"):
service.verify_connection(
ConnectionVerifyRequest(
device_id="test-ble-transport",
compatibility_attestation=ATTESTATION,
)
)
assert service.state()["network_write_reconciliation"] is None
def test_read_only_ble_status_for_another_transport_keeps_reconciliation_fence(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
service, _ = service_with_fake_runtime(tmp_path)
_set_scanned_k1(service, device_id="k1-b")
fence = {
"status": "device-state-unknown-after-write",
"operation_id": "ambiguous-operation",
"transport_ref": "k1-a",
"connection_mode": "bridge",
"operation_stage": "gatt-write",
"reason_code": "BleakGATTProtocolError",
"device_write_confirmed": False,
"required_action": "explicit-read-only-ble-status-observation",
"scope": "process-runtime",
"observed_at": "2026-08-06T10:00:00Z",
}
service._network_write_reconciliation = dict(fence) # noqa: SLF001
async def read_other_status(*_: object, **__: object) -> dict[str, Any]:
return _wifi_status_read(None, device_id="k1-b")
monkeypatch.setattr(facade_module, "read_wifi_status_once", read_other_status)
with pytest.raises(RuntimeError, match="не сообщил актуальный DHCP-адрес"):
service.verify_connection(
ConnectionVerifyRequest(
device_id="k1-b",
compatibility_attestation=ATTESTATION,
)
)
assert service.state()["network_write_reconciliation"] == fence
def test_provisioning_cannot_switch_device_during_active_acquisition(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,