Files
portal/app/tests/Unit/External/SelfRenderLivenessProbeTest.php
T
Дмитрий cfc2c55a75 fix,поиск: сторож виртуалки читает ok — прежний не мог покраснеть никогда
Виртуалка с 03.08 — единственная точка отказа «Поиска клиентов»: чужой xf4.ru
умер и снят. Сторож у неё был болен ровно тем же, от чего ослеп сторож xf4:
«ответ меньше 500 = жив». Поле ok, которое виртуалка честно отдаёт в /health,
не читалось вовсе, а прежний тест «зелёный при 404» этот изъян ОХРАНЯЛ.

Замер живой ручки боевого: {"ok":true,"active":0,"max":8,"proxies":1}, HTTP 200.

Теперь зелёный — только когда виртуалка сама сказала ok: true. Ответ 200 без
признака ok — красный: раз ручка отвечает не тем, о чём договаривались, мы про
единственную точку отказа ничего не знаем, и это обязано быть тревогой. Числа
занятости и прокси показываем человеку рядом со светофором.

Пустой адрес проверки больше НЕ подменяется ручкой рендера: она принимает
только POST и на GET отдаёт 404/405 — именно эта подмена и делала сторожа
вечнозелёным. Нечем проверить — говорим «нечем».

🪤 Записано в шапке класса честно: ok — рассказ виртуалки о себе, а не
доказательство, что она отрисует страницу. Настоящая проверка рендера стоит
живой отрисовки на каждый показ плитки, такого решения владелец не принимал.

Приёмка вырезанием: пять прогонов из шести были красными до правки. Сторожа
внешних служб и админки 202 из 202, статанализ 0.
2026-08-03 14:56:28 +03:00

91 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Services\External\SelfRenderLivenessProbe;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
/**
* Сторож живости своей рендер-виртуалки.
*
* 🔴 03.08.2026 виртуалка стала ЕДИНСТВЕННОЙ точкой отказа «Поиска клиентов»:
* чужой xf4.ru умер и снят. А сторож у неё был болен ровно тем же, чем сторож
* xf4: считал сервис живым по «ответ меньше 500». Поле `ok`, которое виртуалка
* честно отдаёт в `/health`, не читалось вовсе — значит зелёный не мог означать
* ничего, а прежний тест «зелёный при 404» этот изъян ещё и ОХРАНЯЛ.
*
* Новое правило: зелёный только когда виртуалка сама сказала `ok: true`.
* Молчание про `ok` — это красный, а не зелёный: раз ручка перестала отвечать
* так, как договаривались, мы про неё больше ничего не знаем.
*
* 🪤 Чего этот сторож НЕ доказывает: что виртуалка отрисует страницу. `ok` — её
* рассказ о себе. Настоящая проверка рендера стоит живой отрисовки на каждый
* показ плитки, и такого решения владелец не принимал.
*/
uses(TestCase::class);
/** Ответ health-ручки виртуалки — тот же вид, что она отдаёт на бою. */
function vmHealth(array $body, int $status = 200): void
{
config()->set('services.self_render.health_url', 'https://vm.local/health');
Http::fake(['vm.local/health' => Http::response($body, $status)]);
}
it('серый, когда адрес проверки не задан', function () {
config()->set('services.self_render.endpoint', 'https://vm.local/render');
config()->set('services.self_render.health_url', '');
// 🪤 Раньше при пустом адресе проверки сторож пинговал саму ручку рендера.
// Она принимает только POST и на GET отдаёт 404/405 — то есть сторож был
// ЗЕЛЁНЫМ навсегда. Честный ответ здесь — «проверить нечем».
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('grey');
});
it('зелёный, когда виртуалка сказала ok', function () {
vmHealth(['ok' => true, 'active' => 0, 'max' => 8, 'proxies' => 1]);
$r = (new SelfRenderLivenessProbe)->check();
expect($r->light)->toBe('green')
// Числа показываем человеку: занятость и наличие прокси видно сразу,
// без похода на виртуалку руками.
->and($r->detail)->toContain('0 из 8')
->and($r->detail)->toContain('прокси 1');
});
it('КРАСНЫЙ, когда виртуалка сказала ok: false', function () {
// Ради этого прогона всё и затевалось: раньше такой ответ был бы зелёным,
// потому что 200 меньше 500.
vmHealth(['ok' => false, 'active' => 8, 'max' => 8, 'proxies' => 0]);
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('red');
});
it('КРАСНЫЙ, когда про ok в ответе не сказано ничего', function () {
// Ручка ответила 200, но не тем, о чём договаривались, — значит мы про
// виртуалку ничего не знаем. Молчание засчитывать за «жива» нельзя.
config()->set('services.self_render.health_url', 'https://vm.local/health');
Http::fake(['vm.local/health' => Http::response('ok', 200)]);
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('red');
});
it('КРАСНЫЙ на 404 и 405 — это GET к ручке, которая их не принимает', function () {
// Прежний тест требовал здесь ЗЕЛЁНЫЙ. Именно это правило сделало сторожа
// xf4 слепым навсегда, и его нельзя повторять.
vmHealth(['ok' => true], 404);
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('red');
vmHealth(['ok' => true], 405);
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('red');
});
it('красный при 500 и при сетевой ошибке', function () {
vmHealth(['ok' => false], 500);
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('red');
config()->set('services.self_render.health_url', 'https://vm.local/health');
Http::fake(fn () => throw new RuntimeException('down'));
expect((new SelfRenderLivenessProbe)->check()->light)->toBe('red');
});