55bf6348b3
HTTP-клиент к своей виртуалке (замена xf4). Реализует BatchPageFetcher (и PageFetcher): для 2ГИС просит initialState, для Яндекса/сайтов — HTML. Секрет в заголовке X-Render-Key (из .env). Без ключа/адреса — молча пусто (деградация). Http::pool для батча, ретраи. 7/7 тестов, Pint чисто. - config services.self_render (endpoint/key/concurrency, всё из .env). - config autopodbor: флаги self_render + гранулярные self_render_2gis/_yandex (наследуют master), дефолт ВЫКЛ. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
82 lines
3.1 KiB
PHP
82 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Services\Autopodbor\Agent\Fetch\BatchPageFetcher;
|
|
use App\Services\Autopodbor\Agent\Fetch\PageFetcher;
|
|
use App\Services\Autopodbor\Agent\Fetch\SelfRenderClient;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
// SelfRenderClient — HTTP-клиент к рендер-сервису на виртуалке (замена xf4). Как XfetchClient: и
|
|
// PageFetcher (html), и BatchPageFetcher (htmlBatch). Для 2ГИС просит initialState, для Яндекса — HTML.
|
|
// Без ключа/адреса — молча пусто (деградация, не падение).
|
|
|
|
const EP = 'https://render.local/render';
|
|
|
|
function client(array $over = []): SelfRenderClient
|
|
{
|
|
return new SelfRenderClient(
|
|
endpoint: $over['endpoint'] ?? EP,
|
|
key: $over['key'] ?? 'secret',
|
|
concurrency: $over['concurrency'] ?? 4,
|
|
retries: $over['retries'] ?? 2,
|
|
);
|
|
}
|
|
|
|
it('реализует оба интерфейса', function () {
|
|
expect(client())->toBeInstanceOf(PageFetcher::class)
|
|
->and(client())->toBeInstanceOf(BatchPageFetcher::class);
|
|
});
|
|
|
|
it('html: POST на endpoint, тип initialState для 2ГИС, отдаёт data', function () {
|
|
Http::fake([EP => Http::response(['data' => '{"state":1}'], 200)]);
|
|
|
|
$out = client()->html('https://2gis.ru/krasnoyarsk/search/ломбард');
|
|
|
|
expect($out)->toBe('{"state":1}');
|
|
Http::assertSent(fn ($r) => $r->url() === EP
|
|
&& $r['url'] === 'https://2gis.ru/krasnoyarsk/search/ломбард'
|
|
&& $r['type'] === 'initialState'
|
|
&& $r->hasHeader('X-Render-Key', 'secret'));
|
|
});
|
|
|
|
it('html: тип html для Яндекса и прочего', function () {
|
|
Http::fake([EP => Http::response(['data' => '<html>ya</html>'], 200)]);
|
|
|
|
expect(client()->html('https://yandex.ru/maps/org/1'))->toBe('<html>ya</html>');
|
|
Http::assertSent(fn ($r) => $r['type'] === 'html');
|
|
});
|
|
|
|
it('без ключа или адреса → пусто, запрос не шлётся (деградация)', function () {
|
|
Http::fake([EP => Http::response(['data' => 'x'], 200)]);
|
|
|
|
expect(client(['key' => ''])->html('https://2gis.ru/x'))->toBe('')
|
|
->and(client(['endpoint' => ''])->html('https://2gis.ru/x'))->toBe('');
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('htmlBatch: карта url => data', function () {
|
|
Http::fake([EP => function ($request) {
|
|
return Http::response(['data' => 'R:'.$request['url']], 200);
|
|
}]);
|
|
|
|
$urls = ['https://2gis.ru/a', 'https://2gis.ru/b'];
|
|
expect(client()->htmlBatch($urls))->toBe([
|
|
'https://2gis.ru/a' => 'R:https://2gis.ru/a',
|
|
'https://2gis.ru/b' => 'R:https://2gis.ru/b',
|
|
]);
|
|
});
|
|
|
|
it('htmlBatch: пустой ключ → все пусто, без запросов', function () {
|
|
Http::fake([EP => Http::response(['data' => 'x'], 200)]);
|
|
|
|
expect(client(['key' => ''])->htmlBatch(['https://2gis.ru/a']))->toBe(['https://2gis.ru/a' => '']);
|
|
Http::assertNothingSent();
|
|
});
|
|
|
|
it('html: неуспешный ответ → пусто', function () {
|
|
Http::fake([EP => Http::response('', 500)]);
|
|
|
|
expect(client()->html('https://2gis.ru/x'))->toBe('');
|
|
});
|