From 1fe4549b22a8206001fb33973db57078136ce871 Mon Sep 17 00:00:00 2001 From: ochenstarik-ui <267932263+ochenstarik-ui@users.noreply.github.com> Date: Thu, 3 Sep 2026 01:54:35 +0700 Subject: [PATCH] =?UTF-8?q?fix(tests):=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BA=D0=B0=20=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5=D0=B2?= =?UTF-8?q?=D1=88=D0=B5=D0=B3=D0=BE=20refresh=20=D0=BD=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D1=82=20=D0=BE=D1=82=20=D1=84?= =?UTF-8?q?=D0=BE=D0=BD=D0=BE=D0=B2=D1=8B=D1=85=20=D0=BF=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Джоб на ubuntu упал с «assert 32 == 31» в test_seq_token_prevents_stale_refresh_clobber. Тест сравнивал поколения до и после устаревшего вызова, а HubStateStore — процессный синглтон: фоновый сборщик квот, оставшийся от другого теста, успевает поднять generation между двумя вызовами. В логе прогона рядом видно как раз такую фоновую попытку. Падение случайное и зависит от порядка тестов: headless-джоб гоняет pytest без фиксированного порядка. Тем же объясняется разброс 738/739 в базовом прогоне до начала работы. Проверяется теперь инвариант, а не равенство: устаревший ответ отбрасывается ровно один раз, и состояние не откатывается назад. Пять полных прогонов со случайным порядком — 777 passed. Co-Authored-By: Claude Opus 5 --- .../test_state_layer_and_event_driven_quota.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/test_state_layer_and_event_driven_quota.py b/tests/test_state_layer_and_event_driven_quota.py index 4238dcf..460a931 100644 --- a/tests/test_state_layer_and_event_driven_quota.py +++ b/tests/test_state_layer_and_event_driven_quota.py @@ -72,15 +72,27 @@ def test_seq_token_prevents_stale_refresh_clobber(): seq_fresh = store.next_seq() snap_fresh = store.refresh(force_scan=False, seq=seq_fresh) gen_fresh = snap_fresh.generation + skipped_before = store.refresh_skipped_total # Simulate a delayed/stale response from an earlier seq counter seq_stale = seq_fresh - 1 snap_after_stale = store.refresh(force_scan=False, seq=seq_stale) - # Stale response must be rejected, retaining the fresh generation - assert snap_after_stale.generation == gen_fresh + # Устаревший ответ должен быть отброшен. + # + # Проверяется именно отбрасывание, а не равенство поколений. HubStateStore — + # процессный синглтон, и фоновый сборщик квот, оставшийся от другого теста, + # успевает поднять generation между двумя вызовами. Прежнее + # `generation == gen_fresh` падало на этом с «assert 32 == 31» — примерно раз + # на десяток прогонов, только при случайном порядке тестов. Инвариант же + # другой: устаревший ответ отбрасывается, состояние назад не откатывается. + assert store.refresh_skipped_total == skipped_before + 1, ( + "устаревший ответ должен быть отброшен ровно один раз" + ) + assert snap_after_stale.generation >= gen_fresh, ( + "состояние откатилось назад: устаревший ответ затёр более свежее" + ) assert snap_after_stale.seq != seq_stale - assert store.refresh_skipped_total >= 1 @pytest.mark.unit