tenant = Tenant::factory()->create([ 'contact_email' => 'expiry-svc-tenant@example.ru', ]); $this->adminId = DB::table('saas_admin_users')->insertGetId([ 'email' => 'admin-expiry-svc@liderra.ru', 'full_name' => 'Expiry Svc Admin', 'password_hash' => '$2y$04$dummy-hash-for-test', 'role' => 'support', 'is_active' => true, 'sso_provider' => 'local', 'is_break_glass' => false, ]); }); it('endSession ставит session_ended_at и шлёт письмо клиенту', function () { Mail::fake(); $token = ImpersonationToken::create([ 'tenant_id' => $this->tenant->id, 'requested_by' => $this->adminId, 'code_hash' => 'active', 'reason' => 'active session '.str_repeat('y', 30), 'sent_to_email' => 'client-notify@example.ru', 'expires_at' => now()->addMinutes(15), 'used_at' => now()->subMinutes(5), ]); app(ImpersonationExpiryService::class)->endSession($token); expect($token->fresh()->session_ended_at)->not->toBeNull(); Mail::assertQueued( ImpersonationEndedMail::class, fn ($m) => $m->hasTo('client-notify@example.ru'), ); }); it('endSession идемпотентен — уже завершённая сессия не шлёт повторное письмо', function () { Mail::fake(); $endedAt = now()->subMinutes(10); $token = ImpersonationToken::create([ 'tenant_id' => $this->tenant->id, 'requested_by' => $this->adminId, 'code_hash' => 'completed', 'reason' => 'completed session '.str_repeat('z', 30), 'sent_to_email' => 'already-ended@example.ru', 'expires_at' => now()->subMinutes(45), 'used_at' => now()->subMinutes(40), 'session_ended_at' => $endedAt, ]); app(ImpersonationExpiryService::class)->endSession($token); Mail::assertNothingQueued(); // session_ended_at не перезаписан expect($token->fresh()->session_ended_at->timestamp)->toBe($endedAt->timestamp); });