diff --git a/src/antigravity_provider/router/unified_health.py b/src/antigravity_provider/router/unified_health.py index bc3514f..2bab523 100644 --- a/src/antigravity_provider/router/unified_health.py +++ b/src/antigravity_provider/router/unified_health.py @@ -170,6 +170,17 @@ class ProviderSummary: last_refresh_at: str +def _plural_roles(n: int) -> str: + """Согласовать число со словом «роль»: 1 роль, 2 роли, 5 ролей.""" + tail = n % 100 + if 11 <= tail <= 14: + word = "ролей" + else: + last = n % 10 + word = "роль" if last == 1 else "роли" if 2 <= last <= 4 else "ролей" + return f"{n} {word}" + + @dataclass class SystemReadiness: state: str # HEALTHY | LIMITED | DEGRADED | CRITICAL @@ -567,6 +578,12 @@ class UnifiedHealthService: break if has_working_fallback: + # Роль, обслуживаемая резервом, РАБОТАЕТ — она просто + # деградировала. Раньше она не попадала в roles_ready, и + # интерфейс писал «Ролей в строю: 0/6», пока пять ролей + # исправно отвечали. Это вводит в заблуждение в худшую + # сторону: пользователь видит отказ там, где всё работает. + roles_ready += 1 degraded_roles += 1 warnings.append(f"Роль '{rname}' работает через резервный аккаунт (Primary недоступен).") else: @@ -577,11 +594,11 @@ class UnifiedHealthService: if dead_roles > 0: state = READINESS_CRITICAL title_ru = "Критическое состояние" - summary_ru = f"Есть {dead_roles} ролей без рабочего маршрута!" + summary_ru = f"Без рабочего маршрута: {_plural_roles(dead_roles)}." elif degraded_roles > 0: state = READINESS_DEGRADED title_ru = "Деградация маршрутов" - summary_ru = f"{degraded_roles} ролей работают через резерв." + summary_ru = f"Через резерв работают: {_plural_roles(degraded_roles)}." elif connected_accounts < total_accounts: state = READINESS_LIMITED title_ru = "Ограниченная готовность" diff --git a/tests/test_readiness_counting.py b/tests/test_readiness_counting.py new file mode 100644 index 0000000..8384142 --- /dev/null +++ b/tests/test_readiness_counting.py @@ -0,0 +1,43 @@ +"""Роль на резерве считается работающей. + +Дефект со скриншота владельца: заголовок «Ролей в строю: 0/6», а ниже +шесть предупреждений вида «роль работает через резервный аккаунт». Пять +ролей исправно отвечали, а интерфейс сообщал, что не работает ни одна. + +roles_ready считал только роли со здоровым ОСНОВНЫМ профилем. Роль, +обслуживаемая резервом, попадала в degraded, но не в ready. Это ошибка в +худшую сторону: пользователь видит отказ там, где всё работает. +""" + +from __future__ import annotations + +from antigravity_provider.router.unified_health import _plural_roles + + +def test_role_on_fallback_counts_as_ready(): + """Роль, у которой жив резерв, обязана попадать в «в строю».""" + import inspect + + from antigravity_provider.router import unified_health + + src = inspect.getsource(unified_health.UnifiedHealthService.get_system_readiness) + marker = "if has_working_fallback:" + assert marker in src + tail = src.split(marker, 1)[1].split("else:", 1)[0] + assert "roles_ready += 1" in tail, ( + "роль на резерве снова не засчитывается как работающая" + ) + assert "degraded_roles += 1" in tail, ( + "признак деградации потерян — состояние должно оставаться отличимым" + ) + + +def test_plural_roles_agrees_with_number(): + """«Есть 1 ролей» — так по-русски не пишут.""" + assert _plural_roles(1) == "1 роль" + assert _plural_roles(2) == "2 роли" + assert _plural_roles(4) == "4 роли" + assert _plural_roles(5) == "5 ролей" + assert _plural_roles(11) == "11 ролей" + assert _plural_roles(21) == "21 роль" + assert _plural_roles(112) == "112 ролей"