Files
hado/core/tests/Unit/Scheduling/DueResolverTest.php
nikita.hohlov e48e48aa4a feat: due resolution and cascade math
Also: StoresUtc trait normalizes datetime casts to UTC (controller ruling).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
2026-09-04 06:19:00 -03:00

100 lines
4.1 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
namespace Tests\Unit\Scheduling;
use App\Enums\AfterDue;
use App\Enums\DueMode;
use App\Enums\EventState;
use App\Models\Event;
use App\Models\User;
use App\Scheduling\DueResolver;
use Carbon\CarbonImmutable;
use Tests\TestCase; // Laravel TestCase: Eloquent date-casts need the connection grammar even in memory
class DueResolverTest extends TestCase
{
private function user(string $tz = 'America/Montevideo'): User
{
return new User(['login' => 'n', 'tz' => $tz, 'quiet_start' => '22:00:00', 'quiet_end' => '09:00:00']);
}
private function local(string $date, string $time = '24:00:00', ?string $fireOn = null): Event
{
return new Event([
'due_mode' => DueMode::Local, 'due_date' => $date, 'due_time' => $time,
'fire_on' => $fireOn ?? $date, 'after_due' => AfterDue::Expire,
'state' => EventState::Scheduled, 'payload' => ['title' => 't'],
]);
}
private function fixed(string $instant, string $fireOn): Event
{
return new Event([
'due_mode' => DueMode::Fixed, 'due_instant' => CarbonImmutable::parse($instant),
'fire_on' => $fireOn, 'after_due' => AfterDue::Expire,
'state' => EventState::Scheduled, 'payload' => ['title' => 't'],
]);
}
public function test_whole_day_local_event(): void
{
$due = (new DueResolver)->resolve($this->local('2026-08-14'), $this->user());
$this->assertSame('2026-08-14 00:00 America/Montevideo', $due->day->format('Y-m-d H:i e'));
$this->assertSame('2026-08-15 00:00 America/Montevideo', $due->moment->format('Y-m-d H:i e'));
}
public function test_timed_local_event(): void
{
$due = (new DueResolver)->resolve($this->local('2026-09-10', '14:00:00'), $this->user());
$this->assertSame('2026-09-10 00:00', $due->day->format('Y-m-d H:i'));
$this->assertSame('2026-09-10 14:00', $due->moment->format('Y-m-d H:i'));
}
public function test_local_event_follows_user_timezone(): void
{
$event = $this->local('2026-09-10', '14:00:00');
$uy = (new DueResolver)->resolve($event, $this->user('America/Montevideo'));
$cn = (new DueResolver)->resolve($event, $this->user('Asia/Shanghai'));
$this->assertSame('2026-09-10 14:00', $uy->moment->format('Y-m-d H:i'));
$this->assertSame('2026-09-10 14:00', $cn->moment->format('Y-m-d H:i'));
$this->assertNotEquals($uy->moment->utc()->toIso8601String(), $cn->moment->utc()->toIso8601String());
}
public function test_fixed_event_is_converted_into_user_timezone(): void
{
// Вылет из Мадрида 08:15+02:00 = 06:15Z = 03:15 в Монтевидео (-03:00)
$due = (new DueResolver)->resolve($this->fixed('2026-09-10T08:15:00+02:00', '2026-09-03'), $this->user());
$this->assertSame('2026-09-10 03:15 America/Montevideo', $due->moment->format('Y-m-d H:i e'));
$this->assertSame('2026-09-10 00:00', $due->day->format('Y-m-d H:i'));
}
public function test_fire_start_is_local_midnight(): void
{
$start = (new DueResolver)->fireStart($this->local('2026-09-30', '24:00:00', '2026-08-31'), $this->user());
$this->assertSame('2026-08-31 00:00 America/Montevideo', $start->format('Y-m-d H:i e'));
}
public function test_today_starts_at_midnight_for_normal_moment(): void
{
$r = new DueResolver;
$due = $r->resolve($this->local('2026-09-10', '14:00:00'), $this->user());
$this->assertSame('2026-09-10 00:00', $r->todayStart($due, $this->user())->format('Y-m-d H:i'));
}
public function test_today_starts_the_evening_before_for_early_moment(): void
{
$r = new DueResolver;
$due = $r->resolve($this->local('2026-09-10', '07:00:00'), $this->user());
// quiet_start 22:00 2h = 20:00 накануне
$this->assertSame('2026-09-09 20:00', $r->todayStart($due, $this->user())->format('Y-m-d H:i'));
$exactlyAtActiveStart = $r->resolve($this->local('2026-09-10', '09:00:00'), $this->user());
$this->assertSame('2026-09-09 20:00', $r->todayStart($exactlyAtActiveStart, $this->user())->format('Y-m-d H:i'));
}
}