gendesign/backend/tests/services/test_2464_kn_lock_key.py
bot-backend fd2fc945a7
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m22s
CI / backend-tests (pull_request) Successful in 17m12s
fix(ptica): ключ синглтон-лока kn-свипа зависит от множества, а не от порядка (#2464)
devs_key = ",".join(developers) — список джойнился как пришёл, а приходит он прямо
из тела запроса (`developers` в admin_scrape, порядок произвольный). Один и тот же
набор в другом порядке давал ДРУГОЙ ключ: синглтон молча переставал быть
синглтоном, два свипа шли параллельно по одним и тем же разработчикам.

Второе следствие того же корня: force_release_lock строит ключ этой же функцией.
Оператор, снимающий залипший лок и перечисливший разработчиков в ином порядке,
молча не снимал ничего.

sorted(set(...)) закрывает оба случая: порядок и повторы ('A','A' ≡ 'A').

Про переход: формат ключа меняется, поэтому лок, удерживаемый ПРЯМО СЕЙЧАС под
старым ключом, осиротеет и истечёт по TTL. Практического риска нет — kn-свип
не отрабатывает с 28.06 (DOM.РФ отдаёт страницу блокировки, см. #2443).

Тесты сравнивают ЗНАЧЕНИЯ ключей, поэтому красное на origin/main — про разошедшиеся
ключи, а не про отсутствующую функцию:

  один набор в двух порядках → два разных ключа  → падает
  повтор в списке меняет ключ                    → падает
  разные наборы остаются разными    — контроль, зелёный с обеих сторон
  регион остаётся в ключе           — контроль, зелёный с обеих сторон
  None/[] по-прежнему дают '*'      — контроль, зелёный с обеих сторон

Первый контроль не для симметрии: ловит «починку» через огрубление ключа
(отбросить developers вовсе) — тогда свипы по разным разработчикам блокировали бы
друг друга.

Прогоны: -k "scrape_kn or lock" — 114 passed rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 15:15:05 +05:00

59 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Ключ синглтон-лока kn-свипа зависит от множества, а не от порядка (#2464).
`developers` приходит прямо из тела запроса (`admin_scrape`), порядок произвольный.
Раньше список джойнился как пришёл: один и тот же набор в другом порядке давал ДРУГОЙ
ключ — синглтон молча переставал быть синглтоном, два свипа шли параллельно по одним и
тем же разработчикам.
Второе следствие того же корня: `force_release_lock` строит ключ этой же функцией.
Оператор, снимающий залипший лок и перечисливший разработчиков в ином порядке, молча не
снимал ничего.
Тесты сравнивают ЗНАЧЕНИЯ ключей — на origin/main они не совпадают, то есть красное
означает «ключи разошлись», а не «функции нет».
"""
from __future__ import annotations
import os
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
def _key(region: int, devs: list[str] | None) -> str:
from app.workers.tasks.scrape_kn import _lock_key
return _lock_key(region, devs)
def test_same_set_different_order_gives_same_key() -> None:
"""Порядок не должен создавать второй лок для того же набора."""
a = _key(66, ["6208_0", "1234_5", "9999_1"])
b = _key(66, ["9999_1", "6208_0", "1234_5"])
assert a == b, f"один набор дал два ключа:\n {a}\n {b}"
def test_duplicates_do_not_change_the_key() -> None:
"""Повтор в списке — тот же набор, тот же лок."""
assert _key(66, ["6208_0", "6208_0"]) == _key(66, ["6208_0"])
def test_different_sets_stay_separate() -> None:
"""Контроль: разные наборы обязаны остаться разными локами.
Ловит «починку» через огрубление ключа (напр. отбросить developers вовсе) —
тогда свипы по разным разработчикам блокировали бы друг друга.
"""
assert _key(66, ["A"]) != _key(66, ["B"])
assert _key(66, ["A"]) != _key(66, ["A", "B"])
def test_region_still_separates_locks() -> None:
"""Контроль: регион остаётся частью ключа."""
assert _key(66, ["A"]) != _key(74, ["A"])
def test_empty_and_none_mean_all_developers() -> None:
"""Контроль: прежняя семантика '*' для пустого/None сохранена."""
assert _key(66, None).endswith(":*")
assert _key(66, []).endswith(":*")