v3.18.2: review fixes - sticky event-JSON error, store keep-on-load, write queue

The four findings from the review of the 3.15-3.18 batch:

- The event step's `_eventFieldsError` flag is cleared at the start of each
  collect: fixing malformed JSON used to leave the save refused until the
  editor was closed and reopened.  Regression test covers invalid -> fixed.
- The script store keeps an entry that no longer validates on load (a
  tightened engine rule or a hand edit): it is served, fails at run start with
  the validator's message and can be fixed in the editor, instead of being
  dropped from memory and erased by the next save.  Mutation paths still
  refuse it.  (The presets store already had this behaviour.)
- Writebacks are serialized per script (`_testWriteChains`): two quick saves
  of an unsaved draft no longer create two server-side scripts - the window
  could be minutes while a RAM operation holds the card lock.  Regression
  test asserts one create + one update.
- Deleting a script cancels its pending debounced save and marks it deleted,
  so a queued writeback never resurrects it; a create already in flight is
  undone with a delete.  Regression test covers the pending-save case.
- The event vectors moved to a shared fixture
  (frontend/tests/event_vectors.json) asserted by BOTH suites - a one-sided
  builder change now fails the other suite instead of drifting silently.

Tests: 816 frontend + 716 Python; sw cache simple-v383.
This commit is contained in:
2026-10-04 18:27:04 +03:00
parent 1aa1b44063
commit 152878062f
10 changed files with 212 additions and 88 deletions
+26
View File
@@ -113,6 +113,32 @@ class StorePersistenceTests(unittest.TestCase):
def tearDown(self):
self.tmp.cleanup()
def test_an_entry_that_fails_the_validator_is_kept_and_survives_a_save(self):
# a tightened engine rule (or a hand edit) must not silently destroy a
# stored script on load - it stays, fails at run/update time, and the
# next save keeps it on disk
store = make_store(self.tmp.name, validator=lambda raw: raw)
store.add(script('one'))
def strict(raw):
if raw['name'] == 'one':
raise ValueError('rule tightened in a later version')
return raw
reopened = make_store(self.tmp.name, validator=strict)
self.assertEqual([s['name'] for s in reopened.list()], ['one'])
reopened.add(script('two'))
again = make_store(self.tmp.name, validator=strict)
self.assertEqual(sorted(s['name'] for s in again.list()), ['one', 'two'])
# the kept entry is refused by the mutation paths while it stays invalid
# (here the validator rejects the name 'one'), and renaming it - the
# editor's fix - succeeds
one = next(s for s in reopened.list() if s['name'] == 'one')
with self.assertRaises(test_scripts.TestScriptError):
reopened.update(one['id'], {'name': 'one'})
fixed = reopened.update(one['id'], {'name': 'renamed'})
self.assertEqual(fixed['name'], 'renamed')
def test_round_trip_keeps_ids_and_timestamps(self):
store = make_store(self.tmp.name)
s = store.add(script('one'))