108 lines
4.1 KiB
PHP
108 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Admin\TenantAlivenessService;
|
|
use Carbon\CarbonImmutable;
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
uses(DatabaseTransactions::class);
|
|
|
|
/** Кладёт строку в журнал входов на указанное московское время. */
|
|
function alivenessLog(int $tenantId, ?int $userId, string $event, string $mskTime): void
|
|
{
|
|
DB::table('auth_log')->insert([
|
|
'actor_type' => 'tenant_user',
|
|
'tenant_id' => $tenantId,
|
|
'user_id' => $userId,
|
|
'email' => 'client@example.test',
|
|
'event' => $event,
|
|
'ip_address' => '5.166.40.65',
|
|
'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
|
'created_at' => CarbonImmutable::parse($mskTime, 'Europe/Moscow')->utc(),
|
|
]);
|
|
}
|
|
|
|
function alivenessTenant(): array
|
|
{
|
|
$tenant = Tenant::factory()->create();
|
|
$user = User::factory()->create(['tenant_id' => $tenant->id]);
|
|
|
|
return [(int) $tenant->id, (int) $user->id];
|
|
}
|
|
|
|
it('считает клиента живым, если он заходил не позже трёх суток назад', function () {
|
|
[$tenantId, $userId] = alivenessTenant();
|
|
$now = CarbonImmutable::parse('2026-08-07 12:00:00', 'Europe/Moscow');
|
|
|
|
alivenessLog($tenantId, $userId, 'login_success', '2026-08-04 09:00:00');
|
|
|
|
$result = app(TenantAlivenessService::class)->forTenant($tenantId, $now);
|
|
|
|
expect($result['state'])->toBe('active');
|
|
expect($result['days_since_login'])->toBe(3);
|
|
});
|
|
|
|
it('считает клиента остывающим на четвёртые сутки', function () {
|
|
[$tenantId, $userId] = alivenessTenant();
|
|
$now = CarbonImmutable::parse('2026-08-07 12:00:00', 'Europe/Moscow');
|
|
|
|
alivenessLog($tenantId, $userId, 'login_success', '2026-08-03 09:00:00');
|
|
|
|
$result = app(TenantAlivenessService::class)->forTenant($tenantId, $now);
|
|
|
|
expect($result['state'])->toBe('cooling');
|
|
expect($result['days_since_login'])->toBe(4);
|
|
});
|
|
|
|
it('считает клиента пропавшим на четырнадцатые сутки', function () {
|
|
[$tenantId, $userId] = alivenessTenant();
|
|
$now = CarbonImmutable::parse('2026-08-07 12:00:00', 'Europe/Moscow');
|
|
|
|
alivenessLog($tenantId, $userId, 'login_success', '2026-07-24 09:00:00');
|
|
|
|
$result = app(TenantAlivenessService::class)->forTenant($tenantId, $now);
|
|
|
|
expect($result['state'])->toBe('lost');
|
|
expect($result['days_since_login'])->toBe(14);
|
|
});
|
|
|
|
it('не путает сутки в первые три часа ночи по Москве', function () {
|
|
[$tenantId, $userId] = alivenessTenant();
|
|
// 01:30 МСК седьмого августа — это ещё 22:30 UTC шестого.
|
|
// Наивная граница дала бы «вчера» вместо «сегодня».
|
|
$now = CarbonImmutable::parse('2026-08-07 01:30:00', 'Europe/Moscow');
|
|
|
|
alivenessLog($tenantId, $userId, 'login_success', '2026-08-07 01:00:00');
|
|
|
|
$result = app(TenantAlivenessService::class)->forTenant($tenantId, $now);
|
|
|
|
expect($result['days_since_login'])->toBe(0);
|
|
expect($result['state'])->toBe('active');
|
|
});
|
|
|
|
it('отличает зарегистрировавшегося без единого входа', function () {
|
|
[$tenantId, $userId] = alivenessTenant();
|
|
$now = CarbonImmutable::parse('2026-08-07 12:00:00', 'Europe/Moscow');
|
|
|
|
alivenessLog($tenantId, $userId, 'register_success', '2026-08-06 09:00:00');
|
|
|
|
$result = app(TenantAlivenessService::class)->forTenant($tenantId, $now);
|
|
|
|
expect($result['state'])->toBe('never_logged_in');
|
|
});
|
|
|
|
it('отдаёт no_data, когда в журнале нет ни одной строки', function () {
|
|
[$tenantId] = alivenessTenant();
|
|
$now = CarbonImmutable::parse('2026-08-07 12:00:00', 'Europe/Moscow');
|
|
|
|
$result = app(TenantAlivenessService::class)->forTenant($tenantId, $now);
|
|
|
|
expect($result['state'])->toBe('no_data');
|
|
expect($result['last_login_at'])->toBeNull();
|
|
expect($result['days'])->toBe([]);
|
|
});
|