delete(); DB::table('site_visitors')->delete(); $this->tenant = Tenant::factory()->create(); }); /** Гость, который уже был на лендинге (пришёл по смс). */ function landingGuest(): string { return app(VisitTracker::class)->ensureVisitor(null, [ 'ip' => '176.59.133.176', 'user_agent' => 'Mozilla/5.0 (Linux; Android 10; K)', 'path' => '/', 'utm' => ['utm_source' => 'sms'], ]); } test('успешный вход пишет login_done и связывает гостя с клиентом', function () { $vid = landingGuest(); $user = User::factory()->create([ 'tenant_id' => $this->tenant->id, 'email' => 'track-login@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'totp_enabled' => false, 'is_active' => true, ]); $this->withCredentials()->withCookie('lid_vid', $vid) ->postJson('/api/auth/login', [ 'email' => 'track-login@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); expect(DB::table('site_events')->where('event', 'login_done')->count())->toBe(1); $v = DB::table('site_visitors')->where('id', $vid)->first(); expect((int) $v->user_id)->toBe($user->id); expect((int) $v->tenant_id)->toBe($this->tenant->id); expect((bool) $v->is_human)->toBeTrue(); // Канал первого захода не перетёрся — именно он привёл клиента. expect($v->channel)->toBe('sms'); }); test('вход без куки гостя не ломается и ничего не пишет', function () { User::factory()->create([ 'tenant_id' => $this->tenant->id, 'email' => 'track-nocookie@example.ru', 'password_hash' => Hash::make('secret-pass-123'), 'totp_enabled' => false, 'is_active' => true, ]); $this->postJson('/api/auth/login', [ 'email' => 'track-nocookie@example.ru', 'password' => 'secret-pass-123', ])->assertOk(); expect(DB::table('site_events')->count())->toBe(0); expect(DB::table('site_visitors')->count())->toBe(0); }); test('подтверждение почты пишет register_done — регистрация состоялась', function () { $vid = landingGuest(); // Регистрация: код подтверждения в dev-режиме возвращается в ответе. $reg = $this->withCredentials()->withCookie('lid_vid', $vid) ->postJson('/api/auth/register', [ 'email' => 'track-reg@example.ru', 'password' => 'fresh-pass-123', 'accept_offer' => true, 'accept_pdn' => true, 'captcha_token' => 'tok-123', ]); $code = $reg->json('_dev_plain_code'); expect($code)->not->toBeNull('регистрация должна вернуть dev-код подтверждения'); $this->withCredentials()->withCookie('lid_vid', $vid) ->postJson('/api/auth/confirm-email', [ 'email' => 'track-reg@example.ru', 'code' => $code, ])->assertOk(); expect(DB::table('site_events')->where('event', 'register_done')->count())->toBe(1); $v = DB::table('site_visitors')->where('id', $vid)->first(); expect($v->user_id)->not->toBeNull(); expect($v->channel)->toBe('sms'); });