From 6a93cfcbd7b9dd6c0bc66a7ec010e8429f17ab95 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 8 Sep 2026 19:26:57 +0300 Subject: [PATCH] =?UTF-8?q?:white=5Fcheck=5Fmark:=20fix(tests):=20pytest-?= =?UTF-8?q?=D1=81=D1=8C=D1=8E=D1=82=20backend=20=D1=81=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=D1=81=D1=8F=20=D1=86=D0=B5=D0=BB=D0=B8=D0=BA=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `docker compose run backend-app pytest` (то, что делает scripts/check.ps1) не доходил до первого теста. Две независимые причины, обе давние. 1. Область сбора. В pytest.ini не было ни `testpaths`, ни `norecursedirs`, а compose монтирует `data/media` в `/app/apps/backend/media`. На установке с демо-данными это 741 каталог организаций, и сбор падал через 2:46 с `OSError: [Errno 12] Cannot allocate memory` на обходе медиа. `testpaths` перечисляет оба пакета с тестами: только `chatballs` было бы мало — в `chatballs_backend/test_surfaces.py` лежат проверки изоляции поверхностей (loopback-only admin, раздельные cookie, исключение gateway-эндпоинта из SSL-редиректа), и они бы молча выпали из прогона. `norecursedirs` добавлен сверх этого: на запуск с явным путём `testpaths` не распространяется. 2. Боевая функция, собираемая как тест. `ai/test_demo_provider.py` импортировал на уровне модуля `integrations.services.test_integration`; pytest считает тестом любое имя `test_*` в тестовом модуле и падал на ненайденных фикстурах `context` и `integration`. Функция не переименована намеренно: в `integrations/tests.py` и `integrations/test_email.py` она уже импортируется под алиасом `run_integration_test` — конвенция в проекте есть, в этом файле её забыли применить. Добавлен алиас и комментарий, зачем он. Проверка: сбор — 625 позиций за 15 с вместо падения через 2:46; полный прогон — 624 passed, 0 failed (21 мин). Остальные наборы зелёные: internal-ui vitest 76/76, typecheck internal-ui и web-chat, CLI 9/9. Co-Authored-By: Claude Opus 5 --- apps/backend/chatballs/ai/test_demo_provider.py | 10 ++++++++-- apps/backend/pytest.ini | 10 ++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/backend/chatballs/ai/test_demo_provider.py b/apps/backend/chatballs/ai/test_demo_provider.py index 4176569..67b5291 100644 --- a/apps/backend/chatballs/ai/test_demo_provider.py +++ b/apps/backend/chatballs/ai/test_demo_provider.py @@ -9,7 +9,13 @@ from chatballs.ai.runtime import HANDOFF_TOKEN from chatballs.identity.bootstrap import bootstrap_owner from chatballs.identity.models import Organization from chatballs.integrations.models import Integration, IntegrationKind, IntegrationProvider, IntegrationStatus -from chatballs.integrations.services import IntegrationInput, create_integration, test_integration +from chatballs.integrations.services import ( + IntegrationInput, + create_integration, + # Алиас обязателен: имя test_* на уровне модуля pytest собирает как тест + # и падает на ненайденных фикстурах (как в integrations/tests.py). + test_integration as run_integration_test, +) from chatballs.testing import system_tenant_context KNOWLEDGE = ( @@ -73,6 +79,6 @@ class DemoIntegrationTests(TestCase): self.assertEqual(integration.kind, IntegrationKind.LLM_PROVIDER) self.assertEqual(integration.config["default_model"], "demo") self.assertTrue(integration.secret) - checked = test_integration(context=self.context, integration=integration) + checked = run_integration_test(context=self.context, integration=integration) self.assertEqual(checked.status, IntegrationStatus.OK) self.assertIsInstance(_provider_from_integration(Integration.objects.get(pk=integration.pk)), DemoProvider) diff --git a/apps/backend/pytest.ini b/apps/backend/pytest.ini index b8f33dd..9519a89 100644 --- a/apps/backend/pytest.ini +++ b/apps/backend/pytest.ini @@ -1,3 +1,13 @@ [pytest] DJANGO_SETTINGS_MODULE = chatballs_backend.settings python_files = tests.py test_*.py *_tests.py + +# Область сбора. Без неё pytest без аргументов обходит весь каталог backend, +# включая примонтированный media/ с каталогом на каждую организацию: на живой +# установке это сотни тысяч файлов, и сбор падает раньше первого теста +# (OSError: Cannot allocate memory). Тесты живут только в двух пакетах. +testpaths = chatballs chatballs_backend + +# Страховка на случай запуска с явным путём или из другого каталога: +# media и staticfiles — данные и артефакты сборки, тестов там нет. +norecursedirs = media staticfiles .venv node_modules __pycache__ *.egg-info