Files
portal/app/tests/Feature/TrackEndpointTest.php
T
Дмитрий 50daf92d6d feat(visitors): маячок POST /api/track + кука гостя на .liderra.ru
Task 4 плана 2026-07-13-visitors-analytics.md: VisitTracker (создание/обновление
гостя, запись событий, привязка к клиенту) + публичный TrackController@store,
маршрут POST /api/track (throttle:track 60/мин), лимитер в AppServiceProvider,
CSRF-исключение api/track в bootstrap/app.php.

Отклонения от плана (тест-харнесс, не прод-код):
- getCookie('lid_vid', false) в тесте убрал decrypt=false — с withCookie() это
  давало двойное шифрование (withCookie сам шифрует plain-значение).
- добавлен withCredentials() перед withCookie()+postJson/getJson — Laravel
  тест-клиент по умолчанию не шлёт cookie на JSON-запросы (как XHR
  credentials:'omit'); реальный маячок шлёт fetch с credentials:'include',
  так что прод не затронут.

Pest: 5/5 (TrackEndpointTest). Pint: app/Services/Tracking + TrackController.
2026-07-13 19:46:48 +03:00

75 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Tests\Concerns\SharesSupplierPdo;
uses(DatabaseTransactions::class);
uses(SharesSupplierPdo::class);
test('первое событие view создаёт гостя и ставит куку', function () {
$r = $this->postJson('/api/track', [
'event' => 'view',
'host' => 'liderra.ru',
'path' => '/',
'referrer' => 'https://krasnoyarsk.hh.ru/vacancy/1',
], ['User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X)']);
$r->assertStatus(204);
$r->assertCookie('lid_vid');
$v = DB::table('site_visitors')->first();
expect($v->channel)->toBe('hh');
expect($v->device)->toBe('phone');
expect($v->is_human)->toBeFalse(); // пока только браузер, не человек
expect(DB::table('site_events')->where('event', 'view')->count())->toBe(1);
});
test('utm-метка из ссылки определяет канал', function () {
$this->postJson('/api/track', [
'event' => 'view', 'host' => 'liderra.ru', 'path' => '/',
'utm' => ['utm_source' => 'sms', 'utm_campaign' => 'rassylka-13-07'],
])->assertStatus(204);
$v = DB::table('site_visitors')->first();
expect($v->channel)->toBe('sms');
expect($v->utm_campaign)->toBe('rassylka-13-07');
});
test('повторный view с той же кукой не создаёт второго гостя', function () {
$first = $this->postJson('/api/track', ['event' => 'view', 'host' => 'liderra.ru', 'path' => '/']);
// getCookie() без второго аргумента — decrypt=true (по умолчанию), возвращает
// ПЛОСКОЕ значение (снят слой encrypt+CookieValuePrefix). withCookie() ниже сам
// шифрует заново — если взять уже-зашифрованное значение (decrypt=false), получится
// двойное шифрование и сервер cookie не распознает (заведёт нового гостя).
$vid = $first->getCookie('lid_vid')->getValue();
// withCredentials(): postJson/getJson по умолчанию НЕ прикладывают cookie
// (как XHR с credentials:'omit') — withCookie() без него молча теряется.
// В браузере маячок шлёт fetch с credentials:'include', поэтому куку видит всегда.
$this->withCredentials()->withCookie('lid_vid', $vid)
->postJson('/api/track', ['event' => 'view', 'host' => 'lk.liderra.ru', 'path' => '/login'])
->assertStatus(204);
expect(DB::table('site_visitors')->count())->toBe(1);
expect(DB::table('site_events')->count())->toBe(2);
});
test('событие alive помечает гостя как живого человека', function () {
$first = $this->postJson('/api/track', ['event' => 'view', 'host' => 'liderra.ru', 'path' => '/']);
$vid = $first->getCookie('lid_vid')->getValue();
$this->withCredentials()->withCookie('lid_vid', $vid)
->postJson('/api/track', ['event' => 'alive', 'host' => 'liderra.ru', 'path' => '/'])
->assertStatus(204);
expect((bool) DB::table('site_visitors')->first()->is_human)->toBeTrue();
});
test('неизвестное событие отклоняется', function () {
$this->postJson('/api/track', ['event' => 'drop_table', 'host' => 'liderra.ru'])
->assertStatus(422);
expect(DB::table('site_events')->count())->toBe(0);
});