Files
portal/app/tests/Feature/Database/TestDbRebuildGuardTest.php
T
Дмитрий dc56c95e13 fix тесты: тестовая база перестала рваться посреди прогона
Починка не моя и к Этапу 5 отношения не имеет — она взята из ветки
fix/robot-yandex-zamok, где написана и доказана 31.07. Сюда перенесена
потому, что без неё прогон СМС-модуля в этой ветке недостоверен.

Корень в самом Laravel: в конце каждого теста он проверяет, осталось ли
соединение в своей черновой транзакции, и если тест её закрыл сам —
сбрасывает признак «база собрана». Следующий тест пересобирает базу целиком
прямо посреди прогона, снося её под всеми остальными. В проекте полно кода
со своими транзакциями, поэтому срабатывало через раз.

Что сделано:
- пересборка базы теперь один раз и в самом начале прогона, а не лениво
  в середине по первому файлу, который её закажет;
- признак «собрано» держится взведённым — пересборка посреди прогона
  стала невозможна;
- отказ заливки схемы стал громким: раньше отказ мог вернуться без ошибки,
  и прогон ехал дальше по неполной схеме;
- добавлена сверка полноты сборки: сколько шагов миграции записано против
  того, сколько их лежит файлами. Не сошлось — прогон умирает сразу
  и внятно, а не через сотни «таблицы X не существует»;
- тест-ловушка TestDbRebuildGuardTest: первый тест выходит из транзакции
  и ставит метку, второй метку проверяет. Без защиты второй падает.

Замер в этой ветке, СМС-модуль, 44 файла:
- общая тестовая база и без починки — 267 зелёных, 4 красных пачки
  из 15 даже с шестью попытками на пачку;
- своя база liderra_testing_sms и с починкой — 383 из 383 одним прогоном.

Заодно вскрылась вторая, более грубая причина: общая база liderra_testing
держала 143 записи о применённых миграциях при 137 файлах в этой ветке.
Записей больше файлов — доказательство, что в базу пишет чужая рабочая
папка. Датчик простой: сравнить число записей с числом файлов; любое
неравенство значит «база не твоя, верить прогону нельзя». Лечение —
своя база на рабочую папку, как уже сделано у соседних веток.

Прогон всех 4000 тестов ветки с этой починкой я НЕ делал — мерил только
область СМС-модуля.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 13:25:55 +03:00

42 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
uses(RefreshDatabase::class);
/**
* Сторож против пересборки тестовой БД ПОСРЕДИ прогона.
*
* Laravel заворачивает каждый тест в «черновую» транзакцию, а в конце проверяет, что
* соединение в ней осталось. Если тест черновик закрыл сам, Laravel сбрасывает свой
* признак «БД собрана» (RefreshDatabase.php, строки 158-160) и следующий тест
* пересобирает базу целиком — посреди прогона, снося её под всеми остальными. Отсюда
* и «прогон рвётся через раз», и «таблица X не существует» за сотни шагов от причины.
*
* Tests\TestCase взводит признак на каждом тесте, поэтому пересборки не происходит.
*
* ПРИЁМКА ВЫРЕЗАНИЕМ: убрать строку `RefreshDatabaseState::$migrated = true;` из
* TestCase::setUpTraits() — второй тест этого файла обязан УПАСТЬ (метки не будет,
* потому что базу пересоберут между тестами). Вернуть строку — снова зелёный.
*/
it('ловушка: тест закрывает черновую транзакцию и оставляет метку в БД', function () {
// Именно так ведёт себя боевой код со своими DB::transaction/SET LOCAL —
// здесь мы воспроизводим это нарочно и в чистом виде.
DB::commit();
DB::statement('DROP TABLE IF EXISTS proverka_peresborki');
DB::statement('CREATE TABLE proverka_peresborki (x int)');
expect(Schema::hasTable('proverka_peresborki'))->toBeTrue();
});
it('метка на месте — значит пересборки посреди прогона не было', function () {
expect(Schema::hasTable('proverka_peresborki'))->toBeTrue();
DB::statement('DROP TABLE IF EXISTS proverka_peresborki');
});