Add isolated read-only official widget integration and searchable catalog

This commit is contained in:
Pigway
2026-10-02 16:20:54 +08:00
parent bfef1ad28d
commit 59fbdf7c1a
13 changed files with 1144 additions and 7 deletions
+16
View File
@@ -766,5 +766,21 @@
"502",
"503"
]
},
"/api/v1/devices/{device_id}/widgets": {
"get": [
"200",
"400",
"401",
"403",
"404",
"409",
"413",
"422",
"429",
"500",
"502",
"503"
]
}
}
+69
View File
@@ -0,0 +1,69 @@
from unittest.mock import Mock, patch
import pytest
from pigway_frame_flow.drivers.kokonna.widgets import WidgetReader
from pigway_frame_flow.errors import FlowError
def test_snapshot_does_not_contact_official_service():
with patch("requests.post") as post:
result = WidgetReader().read()
assert result.source == "snapshot"
assert len(result.catalog) == 75
assert not result.configured and not result.current_verified and not result.writable
post.assert_not_called()
def test_live_current_and_cache():
response = Mock(status_code=200)
response.json.return_value = {
"result": {
"rows": [
{"id": 29, "name": "古诗词", "tags": [], "args": {"城市": "private"}}
],
"frameCoverRobot": {
"frameCover": {"id": 29},
"args": {"private": "secret"},
},
}
}
with patch("requests.post", return_value=response) as post:
reader = WidgetReader("official-secret", 123)
result = reader.read()
assert reader.read() is result
assert result.current_verified and result.current.id == 29
assert "private" not in result.model_dump_json()
assert "official-secret" not in result.model_dump_json()
assert post.call_count == 1
assert post.call_args.kwargs["json"]["method"] == "appFrame.getFrameCovers"
assert post.call_args.kwargs["allow_redirects"] is False
@pytest.mark.parametrize(
"payload", [{"error": {"message": "official-secret"}}, {"result": {}}]
)
def test_vendor_errors_are_not_exposed(payload):
response = Mock(status_code=200)
response.json.return_value = payload
with (
patch("requests.post", return_value=response),
pytest.raises(FlowError) as error,
):
WidgetReader("official-secret", 123).read()
assert "official-secret" not in str(error.value)
def test_missing_robot_id_never_uses_session():
with patch("requests.post") as post:
assert not WidgetReader("secret").read().configured
post.assert_not_called()
def test_widget_api_mock_and_auth(client):
response = client.get("/api/v1/devices/living-room/widgets")
assert response.status_code == 200
assert response.json()["source"] == "mock"
assert len(response.json()["catalog"]) == 75
client.headers.clear()
assert client.get("/api/v1/devices/living-room/widgets").status_code == 401