From 1bc3940ee893a684ab79b0c8cc5b54c0aaf98361 Mon Sep 17 00:00:00 2001 From: NotBigGhost Date: Sat, 19 Sep 2026 00:01:19 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A1=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=B8:=20=D1=81=D0=B8=D0=B4=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=20=D0=BD=D0=B5=20=D0=BE=D1=82=D0=BA=D0=B0=D1=82=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D0=B5=D1=82=20=D0=B8=D0=BC=D1=8F=20=D1=84=D1=80?= =?UTF-8?q?=D0=B0=D0=BA=D1=86=D0=B8=D0=B8,=20=D0=B7=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=B5=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=BE?= =?UTF-8?q?=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit seed_reference_data идёт при каждом старте (entrypoint.sh, lifespan) и перезаписывал name_ru существующих фракций значением из кода — переименование из админки жило до ближайшего рестарта. Теперь имя из кода получает только новая фракция; дополнение и порядок синхронизируются как раньше. #72 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd --- backend/app/seed/reference_data.py | 9 ++++++-- backend/tests/test_admin_extra.py | 35 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/backend/app/seed/reference_data.py b/backend/app/seed/reference_data.py index 48c72a0..89d4611 100644 --- a/backend/app/seed/reference_data.py +++ b/backend/app/seed/reference_data.py @@ -33,7 +33,13 @@ FACTIONS: list[tuple[str, str, str, int]] = [ def seed_reference_data(session: Session) -> None: - """Создаёт/обновляет дополнения и фракции. Безопасно вызывать многократно.""" + """Создаёт/обновляет дополнения и фракции. Безопасно вызывать многократно. + + Идёт при каждом старте (entrypoint.sh, lifespan dev), поэтому не перетирает то, что + правят руками: имя существующей фракции меняет админка (admin_service.rename_faction), + и после создания записи источник правды для него — БД (#72). Имя из кода получает + только новая фракция; поправить имя существующей — админкой или миграцией. + Служебные поля (дополнение, порядок) и всё у дополнений синхронизируются с кодом.""" code_to_expansion: dict[str, Expansion] = {} for code, name_ru, is_base, order in EXPANSIONS: @@ -61,7 +67,6 @@ def seed_reference_data(session: Session) -> None: ) session.add(fac) else: - fac.name_ru = name_ru fac.expansion_id = expansion.id # type: ignore[assignment] fac.sort_order = order diff --git a/backend/tests/test_admin_extra.py b/backend/tests/test_admin_extra.py index 9d603f0..53f7997 100644 --- a/backend/tests/test_admin_extra.py +++ b/backend/tests/test_admin_extra.py @@ -2,7 +2,11 @@ from __future__ import annotations from fastapi.testclient import TestClient +from sqlalchemy.pool import StaticPool +from sqlmodel import Session, SQLModel, create_engine, select +from app.models import Faction +from app.seed.reference_data import FACTIONS, seed_reference_data from tests.conftest import add_group_member, create_finished_match, csrf_headers, login, start_match @@ -205,6 +209,37 @@ def test_admin_rename_faction_system_wide(client: TestClient, make_admin, engine assert ap["faction_name"] == "Орки WAAAGH" +def test_faction_rename_survives_restart_seeding(client: TestClient, make_admin, engine): + """Сидинг идёт при каждом старте (entrypoint.sh, lifespan) и не должен откатывать + имя, заданное админом (#72).""" + login(client, "Кто-то") + orks = next(f for f in client.get("/api/factions").json() if f["code"] == "orks") + _admin_login(client, make_admin) + r = client.patch( + f"/api/admin/factions/{orks['id']}", + json={"name_ru": "Орки WAAAGH"}, + headers=csrf_headers(client), + ) + assert r.status_code == 200, r.text + + with Session(engine) as s: + seed_reference_data(s) # то же, что делает рестарт + assert s.get(Faction, orks["id"]).name_ru == "Орки WAAAGH" + + +def test_seeding_is_complete_and_idempotent(): + """Пустая БД: фракции из кода создаются со своими именами, повтор ничего не ломает.""" + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as s: + seed_reference_data(s) + seed_reference_data(s) + names = {f.code: f.name_ru for f in s.exec(select(Faction)).all()} + assert names == {code: name for code, name, _exp, _order in FACTIONS} + + def test_admin_delete_group_with_matches_is_conflict(client: TestClient, make_admin, engine): """Группу с партиями удалять нельзя — но ответ должен быть внятным 409. -- 2.54.0