Files
matrix-screen-controller/核桃派软件源代码/tests/test_font_favorites.py
T

82 lines
4.1 KiB
Python

from concurrent.futures import ThreadPoolExecutor
import json
import pytest
from fastapi.testclient import TestClient
from app.fonts.store import FontCatalog, FontValidationError
from app.main import create_app
def test_api_validation_and_persistence(tmp_path):
app = create_app(project_root=tmp_path, driver_kind="mock")
client = TestClient(app)
items = client.get("/api/fonts").json()["items"]
assert client.get("/api/fonts").json()["favorite_ids"] == []
ids = [item["id"] for item in items[:3]]
for identifier in ids:
assert client.put("/api/fonts/favorite", json={"id": identifier, "favorite": True}).status_code == 200
path = app.state.font_catalog.root / "favorites.json"
before = path.read_bytes()
client.put("/api/fonts/favorite", json={"id": ids[0], "favorite": True})
assert path.read_bytes() == before
assert FontCatalog(path.parent.parent).document()["favorite_ids"] == ids
for payload in ({"id": "default", "favorite": "true"}, {"id": "default", "favorite": 1}, {"id": "", "favorite": True}, {"id": "default", "favorite": True, "extra": 1}):
assert client.put("/api/fonts/favorite", json=payload).status_code == 422
assert client.put("/api/fonts/favorite", json={"id": "missing", "favorite": True}).status_code == 404
assert client.put("/api/fonts/favorite", json={"id": "missing", "favorite": False}).status_code == 200
client.put("/api/fonts/favorite", json={"id": ids[0], "favorite": False})
assert client.get("/api/fonts").json()["favorite_ids"] == ids[1:]
def test_concurrent_merge_and_write_failure(tmp_path, monkeypatch):
catalog = FontCatalog(tmp_path)
ids = [item["id"] for item in catalog.document()["items"][:3]]
with ThreadPoolExecutor(max_workers=3) as pool:
list(pool.map(lambda identifier: catalog.set_favorite(identifier, True), ids))
assert set(catalog.document()["favorite_ids"]) == set(ids)
before = (catalog.root / "favorites.json").read_bytes()
def fail(*args):
raise OSError("injected write failure")
monkeypatch.setattr("app.fonts.store.atomic_write_bytes", fail)
with pytest.raises(OSError):
catalog.set_favorite(ids[0], False)
assert set(catalog.document()["favorite_ids"]) == set(ids)
assert (catalog.root / "favorites.json").read_bytes() == before
@pytest.mark.parametrize("content", [b"broken", b'{"schema_version":2,"favorite_ids":[]}', b'{"schema_version":true,"favorite_ids":[]}', b'{"schema_version":1,"favorite_ids":["x","x"]}', b'{"schema_version":1,"favorite_ids":[],"extra":1}'])
def test_invalid_file_preserved(tmp_path, content):
(tmp_path / "fonts").mkdir()
path = tmp_path / "fonts/favorites.json"
path.write_bytes(content)
with pytest.raises(FontValidationError):
FontCatalog(tmp_path)
assert path.read_bytes() == content
def test_stale_ids_preserved_and_removable(tmp_path):
catalog = FontCatalog(tmp_path)
path = catalog.root / "favorites.json"
path.write_text(json.dumps({"schema_version": 1, "favorite_ids": ["stale"]}), encoding="utf-8")
catalog = FontCatalog(tmp_path)
assert catalog.document()["favorite_ids"] == ["stale"]
assert not any(item["id"] == "stale" for item in catalog.document()["items"])
assert catalog.set_favorite("stale", False) == []
def test_same_family_styles_are_independent(tmp_path):
from test_fonts import build_test_font
app = create_app(project_root=tmp_path, driver_kind="mock")
client = TestClient(app)
ids = []
for style in ["Bold", "Italic"]:
response = client.post("/api/fonts/import?filename=style.ttf", content=build_test_font("Same Family", style), headers={"Content-Type": "application/octet-stream"})
assert response.status_code == 201
ids.append(response.json()["fonts"][0]["id"])
client.put("/api/fonts/favorite", json={"id": ids[0], "favorite": True})
assert client.get("/api/fonts").json()["favorite_ids"] == [ids[0]]
client.put("/api/fonts/favorite", json={"id": ids[1], "favorite": True})
client.put("/api/fonts/favorite", json={"id": ids[0], "favorite": False})
assert client.get("/api/fonts").json()["favorite_ids"] == [ids[1]]