fix(api): align zone content kind validation with model

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-08 10:33:42 +12:00
parent aab62efd4f
commit cb9758b97b
2 changed files with 18 additions and 21 deletions

View File

@@ -21,8 +21,8 @@ def _maybe_migrate_tab_json_to_zone():
class Zone(Model): class Zone(Model):
"""Preset layout row (stored in ``db/zone.json``); legacy storage was ``tab.json`` / Tab. """Preset layout row (stored in ``db/zone.json``); legacy storage was ``tab.json`` / Tab.
Optional ``content_kind`` on a row: ``\"presets\"`` (preset tiles only) or ``\"sequences\"`` Optional legacy ``content_kind`` (``\"presets\"`` / ``\"sequences\"``) is kept for older data;
(sequence tiles only). Legacy rows without ``content_kind`` are inferred on load. zones may hold both preset tiles and ``sequence_ids``.
""" """
def __init__(self): def __init__(self):
@@ -95,13 +95,8 @@ class Zone(Model):
return "presets" return "presets"
def _enforce_content_kind_invariants(self, doc): def _enforce_content_kind_invariants(self, doc):
"""Presets-only zones hold no sequences; sequences-only hold no preset tiles.""" """No-op: presets and sequences may coexist on one zone."""
kind = self._normalized_content_kind(doc) _ = doc
if kind == "presets":
doc["sequence_ids"] = []
elif kind == "sequences":
doc["presets"] = []
doc["presets_flat"] = []
def create(self, name="", names=None, presets=None, group_ids=None, content_kind=None): def create(self, name="", names=None, presets=None, group_ids=None, content_kind=None):
next_id = self.get_next_id() next_id = self.get_next_id()
@@ -135,13 +130,7 @@ class Zone(Model):
if id_str not in self: if id_str not in self:
return False return False
patch = dict(data) if isinstance(data, dict) else {} patch = dict(data) if isinstance(data, dict) else {}
doc = self[id_str]
locked_kind = self._normalized_content_kind(doc) or self._infer_content_kind(doc)
if "content_kind" in patch:
patch["content_kind"] = locked_kind
self[id_str].update(patch) self[id_str].update(patch)
if "content_kind" in patch:
self._enforce_content_kind_invariants(self[id_str])
self.save() self.save()
return True return True

View File

@@ -1,4 +1,4 @@
"""Zone content_kind is fixed after create.""" """Zones may hold both presets and sequences."""
import json import json
import os import os
@@ -12,7 +12,7 @@ sys.path.insert(0, str(PROJECT_ROOT / "src"))
from models.zone import Zone # noqa: E402 from models.zone import Zone # noqa: E402
def test_update_cannot_change_content_kind(): def test_zone_presets_and_sequences_can_coexist():
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
path = os.path.join(tmp, "zone.json") path = os.path.join(tmp, "zone.json")
with open(path, "w", encoding="utf-8") as f: with open(path, "w", encoding="utf-8") as f:
@@ -20,8 +20,16 @@ def test_update_cannot_change_content_kind():
z = Zone() z = Zone()
z.file = path z.file = path
z.clear() z.clear()
zid = z.create("preset zone", group_ids=[], content_kind="presets") zid = z.create("mixed zone", group_ids=[], content_kind="presets")
z.update(zid, {"content_kind": "sequences", "name": "preset zone"}) z.update(
zid,
{
"presets": [["p1", "p2"]],
"sequence_ids": ["seq1"],
},
)
doc = z.read(zid) doc = z.read(zid)
assert doc["content_kind"] == "presets" assert doc.get("sequence_ids") == ["seq1"]
assert doc.get("sequence_ids") == [] preset_ids = Zone._preset_ids_in_doc(doc)
assert "p1" in preset_ids
assert "p2" in preset_ids