feat(device-plugins): add profiled K1 lifecycle and canonical data plane
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.web.app import INVALID_REQUEST_DETAIL, app
|
||||
from k1link.web.xgrids_k1_facade import XGRIDS_K1_PLUGIN_ID
|
||||
|
||||
|
||||
async def _post_json(path: str, payload: dict[str, Any]) -> tuple[int, str]:
|
||||
body = json.dumps(payload).encode()
|
||||
request_sent = False
|
||||
response_messages: list[dict[str, Any]] = []
|
||||
|
||||
async def receive() -> dict[str, Any]:
|
||||
nonlocal request_sent
|
||||
if request_sent:
|
||||
return {"type": "http.disconnect"}
|
||||
request_sent = True
|
||||
return {"type": "http.request", "body": body, "more_body": False}
|
||||
|
||||
async def send(message: dict[str, Any]) -> None:
|
||||
response_messages.append(message)
|
||||
|
||||
scope: dict[str, Any] = {
|
||||
"type": "http",
|
||||
"asgi": {"version": "3.0", "spec_version": "2.3"},
|
||||
"http_version": "1.1",
|
||||
"method": "POST",
|
||||
"scheme": "http",
|
||||
"path": path,
|
||||
"raw_path": path.encode(),
|
||||
"query_string": b"",
|
||||
"root_path": "",
|
||||
"headers": [
|
||||
(b"content-type", b"application/json"),
|
||||
(b"content-length", str(len(body)).encode()),
|
||||
],
|
||||
"client": ("127.0.0.1", 41000),
|
||||
"server": ("127.0.0.1", 8765),
|
||||
}
|
||||
await app(scope, receive, send)
|
||||
|
||||
start = next(
|
||||
message for message in response_messages if message["type"] == "http.response.start"
|
||||
)
|
||||
response_body = b"".join(
|
||||
message.get("body", b"")
|
||||
for message in response_messages
|
||||
if message["type"] == "http.response.body"
|
||||
)
|
||||
return int(start["status"]), response_body.decode()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("path", "wrap_input"),
|
||||
[
|
||||
(
|
||||
f"/api/v1/device-plugins/{XGRIDS_K1_PLUGIN_ID}/actions/network.provision",
|
||||
True,
|
||||
),
|
||||
("/api/connect", False),
|
||||
],
|
||||
)
|
||||
def test_validation_errors_do_not_echo_sensitive_request_values(
|
||||
path: str,
|
||||
wrap_input: bool,
|
||||
) -> None:
|
||||
sensitive_value = "x" * 300
|
||||
action_input = {
|
||||
"device_id": "synthetic-device",
|
||||
"ssid": "synthetic-network",
|
||||
"password": sensitive_value,
|
||||
"compatibility_attestation": {
|
||||
"firmware_version": "3.0.2",
|
||||
"topology": "direct-lan",
|
||||
"operator_confirmed": True,
|
||||
},
|
||||
}
|
||||
payload = {"input": action_input} if wrap_input else action_input
|
||||
|
||||
status_code, response_text = asyncio.run(_post_json(path, payload))
|
||||
|
||||
assert status_code == 422
|
||||
assert json.loads(response_text) == {"detail": INVALID_REQUEST_DETAIL}
|
||||
assert sensitive_value not in response_text
|
||||
assert sensitive_value[:32] not in response_text
|
||||
assert "input_value" not in response_text
|
||||
Reference in New Issue
Block a user