Merge pull request #212 from dragon-and-panda/cursor/flaky-test-stability-6e1d

fix(surveys): stable ordering for unprocessed survey answers
This commit is contained in:
nic 2026-05-14 12:14:05 -05:00 committed by GitHub
commit 841287342f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 26 additions and 2 deletions

View file

@ -226,7 +226,7 @@ class SurveyDB:
SELECT id, session_id, question_text, field_kind, selector, answer_text, field_json, raw_json, created_at
FROM survey_answers
WHERE processed=0
ORDER BY created_at ASC
ORDER BY created_at ASC, id ASC
LIMIT ?
""",
(limit,),
@ -259,7 +259,7 @@ class SurveyDB:
FROM survey_answers a
JOIN survey_sessions s ON s.id = a.session_id
WHERE a.processed=0
ORDER BY a.created_at ASC
ORDER BY a.created_at ASC, a.id ASC
LIMIT ?
""",
(limit,),

View file

@ -1,4 +1,5 @@
import unittest
from unittest.mock import patch
from python.surveys.db import SurveyDB
from python.surveys.parser import parse_survey_page
@ -55,6 +56,29 @@ class TestSurveyDB(unittest.TestCase):
finally:
db.close()
def test_unprocessed_order_stable_when_created_at_ties(self):
"""Same-second inserts must not rely on undefined SQLite ordering."""
fixed_ts = 1_700_000_000
with patch("python.surveys.db._now_ts", return_value=fixed_ts):
db = SurveyDB(":memory:")
try:
persona = Persona(id="p1", name="Test", description="x", constraints={})
db.upsert_persona(persona)
profile = UserProfile(id="default", persona_id="p1", data={})
db.upsert_profile(profile)
db.create_session("s1", url="file://demo", persona_id="p1", profile_id="default")
field = SurveyField(selector="1a", kind=FieldKind.TEXT, label="Q")
# Insert lexicographically later id first; tie-break must still order by id ASC.
db.insert_answer("b2", "s1", "Q", field, "second")
db.insert_answer("a1", "s1", "Q", field, "first")
ids_plain = [r["id"] for r in db.fetch_unprocessed_answers()]
ids_events = [r["id"] for r in db.fetch_unprocessed_answer_events()]
self.assertEqual(ids_plain, ["a1", "b2"])
self.assertEqual(ids_events, ["a1", "b2"])
finally:
db.close()
class TestDeepMerge(unittest.TestCase):
def test_deep_merge(self):