初始化奇妙小屏幕控制器项目
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
import base64
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.demo_library import DEMO_ANIMATION_ID, DEMO_STATIC_ID
|
||||
from app.library_order import LibraryOrderValidationError
|
||||
from app.main import create_app
|
||||
|
||||
|
||||
def scene(color=(0, 0, 0)) -> dict:
|
||||
return {
|
||||
"version": 1,
|
||||
"width": 64,
|
||||
"height": 64,
|
||||
"pixelRgb": base64.b64encode(bytes(color) * (64 * 64)).decode("ascii"),
|
||||
"elements": [],
|
||||
}
|
||||
|
||||
|
||||
def create_template(client: TestClient, name: str) -> dict:
|
||||
response = client.post("/api/templates", json={"name": name, "scene": scene()})
|
||||
assert response.status_code == 201
|
||||
return response.json()
|
||||
|
||||
|
||||
def create_animation(client: TestClient, name: str) -> dict:
|
||||
response = client.post("/api/animations", json={"name": name})
|
||||
assert response.status_code == 201
|
||||
return response.json()
|
||||
|
||||
|
||||
def put_order(client: TestClient, items: list[dict], revision: str):
|
||||
return client.put(
|
||||
"/api/library/order",
|
||||
headers={"If-Match": f'"{revision}"'},
|
||||
json={"items": items},
|
||||
)
|
||||
|
||||
|
||||
def test_library_order_defaults_mixes_types_and_persists_only_after_update(tmp_path):
|
||||
client = TestClient(create_app(project_root=tmp_path, driver_kind="mock"))
|
||||
first_template = create_template(client, "静态一")
|
||||
second_template = create_template(client, "静态二")
|
||||
first_animation = create_animation(client, "动图一")
|
||||
second_animation = create_animation(client, "动图二")
|
||||
|
||||
order_path = tmp_path / "data" / "library" / "order.json"
|
||||
default = client.get("/api/library/order")
|
||||
assert default.status_code == 200
|
||||
assert default.json()["items"] == [
|
||||
{"type": "template", "id": second_template["id"]},
|
||||
{"type": "template", "id": first_template["id"]},
|
||||
{"type": "animation", "id": second_animation["id"]},
|
||||
{"type": "animation", "id": first_animation["id"]},
|
||||
]
|
||||
assert not order_path.exists()
|
||||
|
||||
mixed = [
|
||||
{"type": "animation", "id": first_animation["id"]},
|
||||
{"type": "template", "id": first_template["id"]},
|
||||
{"type": "animation", "id": second_animation["id"]},
|
||||
{"type": "template", "id": second_template["id"]},
|
||||
]
|
||||
assert client.put("/api/library/order", json={"items": mixed}).status_code == 428
|
||||
updated = put_order(client, mixed, default.json()["revision"])
|
||||
assert updated.status_code == 200
|
||||
assert updated.json()["items"] == mixed
|
||||
persisted = json.loads(order_path.read_text(encoding="utf-8"))
|
||||
assert persisted == {"schema_version": 1, "items": mixed}
|
||||
|
||||
restarted = TestClient(create_app(project_root=tmp_path, driver_kind="mock"))
|
||||
assert restarted.get("/api/library/order").json() == updated.json()
|
||||
stale = put_order(restarted, list(reversed(mixed)), default.json()["revision"])
|
||||
assert stale.status_code == 409
|
||||
|
||||
|
||||
def test_library_order_rejects_invalid_demo_duplicate_and_changed_sets(tmp_path):
|
||||
client = TestClient(create_app(project_root=tmp_path, driver_kind="mock"))
|
||||
template = create_template(client, "静态")
|
||||
animation = create_animation(client, "动图")
|
||||
current = client.get("/api/library/order").json()
|
||||
|
||||
demo = put_order(
|
||||
client,
|
||||
[{"type": "template", "id": DEMO_STATIC_ID}, {"type": "animation", "id": DEMO_ANIMATION_ID}],
|
||||
current["revision"],
|
||||
)
|
||||
assert demo.status_code == 422
|
||||
duplicate = put_order(
|
||||
client,
|
||||
[
|
||||
{"type": "template", "id": template["id"]},
|
||||
{"type": "template", "id": template["id"]},
|
||||
{"type": "animation", "id": animation["id"]},
|
||||
],
|
||||
current["revision"],
|
||||
)
|
||||
assert duplicate.status_code == 422
|
||||
missing = put_order(
|
||||
client,
|
||||
[{"type": "template", "id": template["id"]}],
|
||||
current["revision"],
|
||||
)
|
||||
assert missing.status_code == 409
|
||||
extra_field = client.put(
|
||||
"/api/library/order",
|
||||
headers={"If-Match": f'"{current["revision"]}"'},
|
||||
json={"items": [{"type": "template", "id": template["id"], "name": "bad"}]},
|
||||
)
|
||||
assert extra_field.status_code == 422
|
||||
|
||||
|
||||
def test_library_order_appends_new_items_and_filters_deleted_references(tmp_path):
|
||||
client = TestClient(create_app(project_root=tmp_path, driver_kind="mock"))
|
||||
first = create_template(client, "静态一")
|
||||
animation = create_animation(client, "动图")
|
||||
initial = client.get("/api/library/order").json()
|
||||
ordered = [
|
||||
{"type": "animation", "id": animation["id"]},
|
||||
{"type": "template", "id": first["id"]},
|
||||
]
|
||||
assert put_order(client, ordered, initial["revision"]).status_code == 200
|
||||
|
||||
appended = create_template(client, "静态二")
|
||||
after_create = client.get("/api/library/order").json()
|
||||
assert after_create["items"] == [
|
||||
*ordered,
|
||||
{"type": "template", "id": appended["id"]},
|
||||
]
|
||||
|
||||
removed = client.delete(
|
||||
f"/api/templates/{first['id']}",
|
||||
headers={"If-Match": f'"{first["revision"]}"'},
|
||||
)
|
||||
assert removed.status_code == 200
|
||||
after_delete = client.get("/api/library/order").json()
|
||||
assert after_delete["items"] == [
|
||||
{"type": "animation", "id": animation["id"]},
|
||||
{"type": "template", "id": appended["id"]},
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"content",
|
||||
[
|
||||
b"not json",
|
||||
json.dumps({"schema_version": 2, "items": []}).encode(),
|
||||
json.dumps({"schema_version": 1, "items": [], "extra": True}).encode(),
|
||||
json.dumps({
|
||||
"schema_version": 1,
|
||||
"items": [
|
||||
{"type": "template", "id": "11111111-1111-4111-8111-111111111111"},
|
||||
{"type": "template", "id": "11111111-1111-4111-8111-111111111111"},
|
||||
],
|
||||
}).encode(),
|
||||
json.dumps({
|
||||
"schema_version": 1,
|
||||
"items": [{"type": "template", "id": DEMO_STATIC_ID}],
|
||||
}).encode(),
|
||||
],
|
||||
)
|
||||
def test_invalid_library_order_blocks_startup_and_preserves_original(tmp_path, content):
|
||||
path = tmp_path / "data" / "library" / "order.json"
|
||||
path.parent.mkdir(parents=True)
|
||||
path.write_bytes(content)
|
||||
|
||||
with pytest.raises(LibraryOrderValidationError):
|
||||
create_app(project_root=tmp_path, driver_kind="mock")
|
||||
|
||||
assert path.read_bytes() == content
|
||||
Reference in New Issue
Block a user