check-file-length: тесты в корневой tests/ меряются порогами src (300/500) вместо tests (600/1000) #31

Closed
opened 2026-09-17 15:45:50 +07:00 by claude-secretary · 0 comments

Проблема

scripts/check_file_length.sh, блок категоризации:

case "$file" in
    *.md)      warn=$SPEC_WARN;  fail=$SPEC_FAIL  ;;
    */tests/*) warn=$TESTS_WARN; fail=$TESTS_FAIL ;;
    *)         warn=$SRC_WARN;   fail=$SRC_FAIL   ;;
esac

*/tests/* требует хотя бы один символ перед tests/. Пути, которые pre-commit передаёт хуку, — относительные от корня репозитория, без ведущего слэша. Поэтому tests/test_foo.py — файл прямо в корневой tests/, то есть самая обычная раскладка — не матчится, падает в *) и меряется SRC-порогами 300/500 вместо TESTS-порогов 600/1000.

Репро:

case "tests/test_foo.py" in */tests/*) echo yes;; *) echo no;; esac   # no

Проверено на main (v0.23.0) — воспроизводится.

Этот же класс бага в этом же файле уже починен — но только наполовину

Блоком выше, в исключениях, сравнение переведено на case "/$file" (v0.18.0, #17), и в комментарии там прямо написано, почему именно так:

Ведущий слэш в сравниваемой строке, а не вторая копия каждого паттерна: */x/* не матчит верхнеуровневый x/..., а миграции и пакеты сплошь и рядом лежат в корне репозитория. С /$file верхнеуровневый путь выглядит как вложенный, и одна форма покрывает обе — следующему исключению дублировать строку уже не придётся.

Категоризация осталась на старом case "$file", и наступает ровно на те же грабли.

Что сделать

Перевести второй case на ту же форму, что и первый:

case "/$file" in
    *.md)      warn=$SPEC_WARN;  fail=$SPEC_FAIL  ;;
    */tests/*) warn=$TESTS_WARN; fail=$TESTS_FAIL ;;
    *)         warn=$SRC_WARN;   fail=$SRC_FAIL   ;;
esac

Это надёжнее варианта tests/*|*/tests/*: покрывает любую глубину, не требует дублировать паттерн при добавлении следующей категории и совпадает с уже принятым в файле решением.

Граница по сегменту пути при этом сохраняется: /mytests/foo.py и /contests/foo.py под */tests/* по-прежнему не подходят — слэш обязан стоять непосредственно перед tests.

Кого задевает

Любой consumer-репозиторий с тестами в корневой tests/ — то есть почти каждый — тихо получает заниженный порог для тестовых файлов. Тест не production-код, его длина нормируется иначе; занижение порога выглядит как внезапный FAIL на правке в +3 строки.

Так это и поймали: в volody/dispatcher файл tests/test_release_mode.py (593 строки) упал FAIL при росте на 3 строки, хотя по TESTS-порогу 600/1000 он ещё даже не в WARN. Разбор — https://gitlab.homedevlab.ru/volody/dispatcher/-/issues/189.

Проверить заодно стоит jamzap/backend, jamzap/frontend, jamzap/mobile, jamzap/infra, volody/dispatcher — чинить на их стороне нечего, но после релиза им нужен бамп rev.

Приёмка

  • tests/test_foo.py в корне репозитория меряется порогами 600/1000
  • вложенный pkg/tests/test_foo.py продолжает меряться так же, как раньше
  • mytests/foo.py и contests/foo.py в категорию тестов не попадают
  • случай покрыт так же, как покрыты остальные хуки репозитория
  • версия в pyproject.toml и тег релиза сходятся (гейт из #28), в README актуальный rev
## Проблема `scripts/check_file_length.sh`, блок категоризации: ```bash case "$file" in *.md) warn=$SPEC_WARN; fail=$SPEC_FAIL ;; */tests/*) warn=$TESTS_WARN; fail=$TESTS_FAIL ;; *) warn=$SRC_WARN; fail=$SRC_FAIL ;; esac ``` `*/tests/*` требует хотя бы один символ **перед** `tests/`. Пути, которые pre-commit передаёт хуку, — относительные от корня репозитория, без ведущего слэша. Поэтому `tests/test_foo.py` — файл прямо в корневой `tests/`, то есть самая обычная раскладка — не матчится, падает в `*)` и меряется SRC-порогами 300/500 вместо TESTS-порогов 600/1000. Репро: ```bash case "tests/test_foo.py" in */tests/*) echo yes;; *) echo no;; esac # no ``` Проверено на `main` (v0.23.0) — воспроизводится. ## Этот же класс бага в этом же файле уже починен — но только наполовину Блоком выше, в исключениях, сравнение переведено на `case "/$file"` (v0.18.0, #17), и в комментарии там прямо написано, почему именно так: > Ведущий слэш в сравниваемой строке, а не вторая копия каждого паттерна: `*/x/*` не матчит верхнеуровневый `x/...`, а миграции и пакеты сплошь и рядом лежат в корне репозитория. С `/$file` верхнеуровневый путь выглядит как вложенный, и одна форма покрывает обе — следующему исключению дублировать строку уже не придётся. Категоризация осталась на старом `case "$file"`, и наступает ровно на те же грабли. ## Что сделать Перевести второй `case` на ту же форму, что и первый: ```bash case "/$file" in *.md) warn=$SPEC_WARN; fail=$SPEC_FAIL ;; */tests/*) warn=$TESTS_WARN; fail=$TESTS_FAIL ;; *) warn=$SRC_WARN; fail=$SRC_FAIL ;; esac ``` Это надёжнее варианта `tests/*|*/tests/*`: покрывает любую глубину, не требует дублировать паттерн при добавлении следующей категории и совпадает с уже принятым в файле решением. Граница по сегменту пути при этом сохраняется: `/mytests/foo.py` и `/contests/foo.py` под `*/tests/*` по-прежнему не подходят — слэш обязан стоять непосредственно перед `tests`. ## Кого задевает Любой consumer-репозиторий с тестами в корневой `tests/` — то есть почти каждый — тихо получает заниженный порог для тестовых файлов. Тест не production-код, его длина нормируется иначе; занижение порога выглядит как внезапный FAIL на правке в +3 строки. Так это и поймали: в `volody/dispatcher` файл `tests/test_release_mode.py` (593 строки) упал FAIL при росте на 3 строки, хотя по TESTS-порогу 600/1000 он ещё даже не в WARN. Разбор — https://gitlab.homedevlab.ru/volody/dispatcher/-/issues/189. Проверить заодно стоит `jamzap/backend`, `jamzap/frontend`, `jamzap/mobile`, `jamzap/infra`, `volody/dispatcher` — чинить на их стороне нечего, но после релиза им нужен бамп `rev`. ## Приёмка - `tests/test_foo.py` в корне репозитория меряется порогами 600/1000 - вложенный `pkg/tests/test_foo.py` продолжает меряться так же, как раньше - `mytests/foo.py` и `contests/foo.py` в категорию тестов **не** попадают - случай покрыт так же, как покрыты остальные хуки репозитория - версия в `pyproject.toml` и тег релиза сходятся (гейт из #28), в README актуальный `rev`
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
senokosov/pre-commit-hooks#31
No description provided.