Тесты по модулям предметных областей
Пункт 12 ревью 2026-09-28 17:35 (docs/changes/026): tests/test_app.py (936 строк) разнесён на test_security, test_devices, test_operations, test_backups, test_events, test_ids_migrations, test_architecture; общие хелперы — tests/helpers.py. Тела тестов не менялись (AST совпадает). security.reset_all_failures() — фикстура сбрасывает счётчики попыток по всем ключам, тесты не зависят от порядка. Тесты: 36 из 36 полным прогоном, каждый модуль отдельно и в обратном порядке; тест-линтер после переноса проверен мутацией. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5367cb5e9c
commit
e1f197ccff
14 files changed
+1133
-995
No files matched your search
@@ -0,0 +1,48 @@
|
||||
# План: 026 — тесты по модулям (п. 12 ревью)
|
||||
|
||||
## Context
|
||||
Ревью `docs/reviews/2026-09-28-1735-codebase-review.md`, п. 12: все тесты — в одном `tests/test_app.py` (936 строк, 36 тестов),
|
||||
файл растёт с каждым изменением (570 → 753 → 936). Трудно найти тест по области, конфликтуют правки параллельных изменений.
|
||||
Нужно разнести тесты по модулям предметных областей **без изменения логики тестов**.
|
||||
|
||||
Решений пользователя не требуется: чисто структурное изменение; код приложения и стенд не затрагиваются (тесты не входят в образ).
|
||||
|
||||
## Изменения
|
||||
|
||||
### Модули (`tests/`)
|
||||
| Файл | Тесты |
|
||||
|---|---|
|
||||
| `test_security.py` | `test_password_encryption_roundtrip`, `test_api_auth_and_no_password_leak`, `test_insecure_settings_rejects_weak_secrets_without_leaking_them`, `test_insecure_settings_rejects_invalid_trusted_proxies`, `test_client_ip_trusted_proxies`, `test_app_refuses_to_start_with_insecure_config`, `test_login_lockout_by_ip`, `test_login_lockout_by_ip_behind_trusted_proxy`, `test_move_redirect_rejects_open_redirect_next`, `test_process_lock_blocks_second_process` |
|
||||
| `test_devices.py` | `test_status_parsing`, `test_chr_status_and_version_compare`, `test_groups_and_device_filters`, `test_create_device_via_ui_binds_group`, `test_device_name_is_immutable`, `test_poll_marks_offline_and_recovers`, `test_ros_state_values` |
|
||||
| `test_operations.py` | `test_firmware_reboots_on_log_message`, `test_batch_channel_runs_as_jobs`, `test_run_ros_downgrade_order_and_failures`, `test_downgrade_api_endpoints`, `test_ui_downgrade_dialog_and_mismatch` |
|
||||
| `test_backups.py` | `test_backup_flow`, `test_backup_filters`, `test_bulk_delete_backups`, `test_backup_files_get_ids_and_deletion_is_recorded`, `test_bucket_list_is_cached_between_reads` |
|
||||
| `test_events.py` | `test_events_link_entities`, `test_events_api_and_id_validation`, `test_journal_rotation_by_age_and_count`, `test_journal_clear_requires_password_through_modal`, `test_journal_page_filters_cursor_dialogs_and_settings` (+ хелпер `_fill_events`) |
|
||||
| `test_ids_migrations.py` | `test_ids_are_unique_typed_and_ordered`, `test_migration_replaces_numeric_ids`, `test_migration_adds_legacy_columns_before_id_migration` (+ `LEGACY_DDL`, `LEGACY_DDL_NO_EXTRA_COLUMNS`) |
|
||||
| `test_architecture.py` | `test_no_sync_db_calls_in_async_functions` (путь к `app/` — через `Path(__file__)`, работает и из нового файла) |
|
||||
|
||||
- Общие хелперы `ros_client`, `_request` → `tests/helpers.py` (импорт `from tests.helpers import …`); константы секретов остаются в `conftest.py`.
|
||||
- В каждом модуле — только нужные ему импорты (без «всё из всего»).
|
||||
- Тела тестов переносятся **без изменений**. Допустимы только правки импортов и, при необходимости, изоляции (ниже).
|
||||
- `tests/test_app.py` удаляется.
|
||||
|
||||
### Изоляция тестов (`tests/conftest.py`)
|
||||
- Счётчики неудачных попыток (`security._fails`, `security._locked_until`) — состояние процесса; сейчас фикстура сбрасывает только ключи
|
||||
`admin` и `login:testclient`, а тесты используют и другие IP (`10.0.0.x`, `1.1.1.1`). После разбиения порядок выполнения меняется —
|
||||
сбрасывать **все** счётчики в автоиспользуемой фикстуре: добавить в `app/security.py` функцию `reset_all_failures()` (для тестов и
|
||||
будущего администрирования) и вызывать её вместо двух точечных сбросов.
|
||||
|
||||
## Тесты
|
||||
Логика тестов не меняется. Критерий — тот же набор из 36 тестов, все зелёные, в том числе при запуске каждого модуля отдельно.
|
||||
|
||||
## Документация
|
||||
README: «Архитектура» (дерево — `tests/` по модулям), «Тесты» (как запустить модуль), строка 026 в истории. `summary.md` — оркестратор.
|
||||
|
||||
## Исполнение
|
||||
Исполнитель (Sonnet): перенос, `helpers.py`, `conftest.py`, `reset_all_failures`, README. Тесты не запускает, не коммитит. Стенд не пересобирается
|
||||
(код приложения меняется только добавлением `reset_all_failures`, которую приложение не вызывает).
|
||||
|
||||
## Проверка (оркестратор)
|
||||
- Набор имён тестов до и после совпадает (36; список «до» сохранён оркестратором).
|
||||
- `pytest -q` — все зелёные; **каждый модуль отдельно** — зелёный (ловит скрытые зависимости от порядка и импортов);
|
||||
обратный порядок модулей (`pytest tests/test_security.py … в обратном порядке`) — зелёный.
|
||||
- `tests/test_app.py` отсутствует; `grep` тел тестов: построчное сравнение тел функций до/после (AST-дамп тел тестов совпадает).
|
||||
@@ -0,0 +1,20 @@
|
||||
# Итоги: 026 — тесты по модулям (п. 12 ревью)
|
||||
|
||||
Источник — ревью `docs/reviews/2026-09-28-1735-codebase-review.md`, п. 12.
|
||||
|
||||
## Сделано
|
||||
- `tests/test_app.py` (936 строк, 36 тестов) разнесён по модулям без изменения тел тестов:
|
||||
`test_security.py` (10), `test_devices.py` (7), `test_operations.py` (5), `test_backups.py` (5), `test_events.py` (5),
|
||||
`test_ids_migrations.py` (3), `test_architecture.py` (1). Общие `ros_client`, `_request` — `tests/helpers.py`; каждый модуль импортирует только нужное (pyflakes чисто).
|
||||
- Изоляция: `security.reset_all_failures()` (очистка всех счётчиков неудачных попыток под блокировкой); фикстура `env` вызывает её вместо двух
|
||||
точечных сбросов — тесты с разными IP не влияют друг на друга при любом порядке. Приложение функцию не вызывает.
|
||||
- README: дерево `tests/` в «Архитектуре», запуск одного модуля в «Тестах», строка 026.
|
||||
|
||||
## Проверено
|
||||
- AST всех 36 тестовых функций (с декораторами) совпадает с `tests/test_app.py` из HEAD; имена тестов до и после совпадают.
|
||||
- `pytest`: 36 из 36 полным прогоном; каждый модуль отдельно — зелёный; модули в обратном порядке — 36 из 36.
|
||||
- Тест-линтер после переноса находит `app/`: мутация (прямой `get_device` в `refresh_device`) → падение с точным местом; файл восстановлен.
|
||||
- Стенд не пересобирался (тесты не входят в образ).
|
||||
|
||||
## Оговорки
|
||||
- Ссылки на `tests/test_app.py` в прежних отчётах ревью (`docs/reviews/*`) оставлены как есть — это исторические документы.
|
||||
Reference in new issue
Block a user