`PUT /dashboards/{name}` had no backend test: three cover what it promises —
a first draft for a name nobody has used (200, not the 404 that came off in
4a2337f), an update that keeps everything the edit did not name, and a save
based on a version someone moved past.
The Playwright half is the same property through the editor. Pages and
sections are gone since 7ff29ca, so what the editor never draws — a widget's
md/sm placements and the dashboard-wide settings — is what a save has to
carry, and dragging one widget is what the spec makes it carry it through.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1moruzue2kTJd3uVisgNk
89 lines
3.0 KiB
Python
89 lines
3.0 KiB
Python
"""Dashboards over HTTP: saving one, which is also how the first one is made."""
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from fluksio.core.config import settings
|
|
|
|
PREFIX = f"{settings.API_V1_STR}/dashboards"
|
|
|
|
|
|
def a_dashboard(name: str) -> dict:
|
|
return {
|
|
"name": name,
|
|
"title": "Hall",
|
|
"icon": "gauge",
|
|
"widgets": [
|
|
{
|
|
"id": "temperature",
|
|
"type": "stat",
|
|
"title": "Temperature",
|
|
"layout": {"lg": {"x": 0, "y": 0, "w": 3, "h": 2}},
|
|
"config": {"message": "house.temperature", "dtype": "float"},
|
|
}
|
|
],
|
|
"settings": {"theme": {"value": "dark", "message": "", "dtype": "str"}},
|
|
"version": 0,
|
|
}
|
|
|
|
|
|
def test_a_save_creates_a_dashboard_that_does_not_exist_yet(
|
|
client: TestClient, superuser_token_headers: dict[str, str]
|
|
) -> None:
|
|
"""A first draft, not a 404: creating one *is* saving it at version 0."""
|
|
body = a_dashboard("put_creates")
|
|
|
|
response = client.put(
|
|
f"{PREFIX}/put_creates", headers=superuser_token_headers, json=body
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
saved = response.json()
|
|
assert saved["version"] == 1 and saved["has_draft"] is True
|
|
# A draft alone: nothing was published, so no panel can be shown it.
|
|
assert (
|
|
client.get(f"{PREFIX}/put_creates", headers=superuser_token_headers).status_code
|
|
== 404
|
|
)
|
|
draft = client.get(
|
|
f"{PREFIX}/put_creates?draft=true", headers=superuser_token_headers
|
|
)
|
|
assert draft.json()["widgets"] == body["widgets"]
|
|
|
|
|
|
def test_a_save_of_an_existing_dashboard_keeps_what_it_did_not_change(
|
|
client: TestClient, superuser_token_headers: dict[str, str]
|
|
) -> None:
|
|
seeded = a_dashboard("put_updates")
|
|
first = client.put(
|
|
f"{PREFIX}/put_updates", headers=superuser_token_headers, json=seeded
|
|
).json()
|
|
|
|
renamed = {**first, "widgets": [{**first["widgets"][0], "title": "Outside"}]}
|
|
second = client.put(
|
|
f"{PREFIX}/put_updates", headers=superuser_token_headers, json=renamed
|
|
)
|
|
|
|
assert second.status_code == 200, second.text
|
|
stored = second.json()
|
|
assert stored["version"] == first["version"] + 1
|
|
assert stored["widgets"][0]["title"] == "Outside"
|
|
# Everything the edit did not name is still what was first written.
|
|
assert stored["icon"] == seeded["icon"]
|
|
assert stored["settings"] == seeded["settings"]
|
|
assert stored["widgets"][0]["layout"] == seeded["widgets"][0]["layout"]
|
|
assert stored["widgets"][0]["config"] == seeded["widgets"][0]["config"]
|
|
|
|
|
|
def test_a_save_based_on_a_version_someone_moved_past_is_refused(
|
|
client: TestClient, superuser_token_headers: dict[str, str]
|
|
) -> None:
|
|
body = a_dashboard("put_conflicts")
|
|
client.put(f"{PREFIX}/put_conflicts", headers=superuser_token_headers, json=body)
|
|
|
|
stale = client.put(
|
|
f"{PREFIX}/put_conflicts", headers=superuser_token_headers, json=body
|
|
)
|
|
|
|
assert stale.status_code == 409
|
|
assert stale.json()["detail"]["current_version"] == 1
|