Files
hado/core/tests/Feature/EventUpserterTest.php

139 lines
5.8 KiB
PHP
Raw Permalink 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\Feature;
use App\Enums\ChannelType;
use App\Enums\EventState;
use App\Ingest\EventUpserter;
use App\Models\Event;
use App\Models\Source;
use App\Models\User;
use Carbon\CarbonImmutable;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class EventUpserterTest extends TestCase
{
use RefreshDatabase;
private Source $source;
private EventUpserter $upserter;
protected function setUp(): void
{
parent::setUp();
config(['hado.default_tz' => 'America/Montevideo']);
CarbonImmutable::setTestNow('2026-09-01T12:00:00-03:00');
$this->source = Source::create(['name' => 'docs', 'token_hash' => hash('sha256', 'x')]);
$this->upserter = app(EventUpserter::class);
}
protected function tearDown(): void
{
CarbonImmutable::setTestNow();
parent::tearDown();
}
private function item(array $over = []): array
{
return array_replace([
'user' => 'nikita', 'source_ref' => 'document:918', 'topic' => 'expiry',
'due_at' => '2026-09-30', 'fire_at' => '2026-08-31', 'after_due' => 'keep',
'payload' => ['title' => 'Седула протухает 30 сен'],
], $over);
}
public function test_creates_event_user_and_web_channel_with_computed_state(): void
{
$result = $this->upserter->upsert($this->source, [$this->item()]);
$this->assertSame([['source_ref' => 'document:918', 'topic' => 'expiry', 'id' => 1, 'state' => 'preparing']], $result);
$user = User::where('login', 'nikita')->firstOrFail();
$this->assertSame('America/Montevideo', $user->tz);
$this->assertSame(ChannelType::Web, $user->channels()->sole()->type);
$event = Event::sole();
$this->assertSame(EventState::Preparing, $event->state);
$this->assertTrue($event->quiet_until->lt(CarbonImmutable::now()));
}
public function test_same_due_updates_fields_and_keeps_terminal_state(): void
{
$this->upserter->upsert($this->source, [$this->item()]);
Event::sole()->update(['state' => EventState::Done, 'done_at' => now()]);
$this->upserter->upsert($this->source, [$this->item(['payload' => ['title' => 'Новый заголовок'], 'fire_at' => '2026-09-15'])]);
$event = Event::sole();
$this->assertSame(EventState::Done, $event->state);
$this->assertSame('Новый заголовок', $event->payload['title']);
$this->assertSame('2026-09-15', $event->fire_on->format('Y-m-d'));
}
public function test_same_due_recomputes_non_terminal_state(): void
{
$this->upserter->upsert($this->source, [$this->item()]);
$this->assertSame(EventState::Preparing, Event::sole()->state);
$this->upserter->upsert($this->source, [$this->item(['fire_at' => '2026-09-20'])]);
$this->assertSame(EventState::Scheduled, Event::sole()->state, 'старт отодвинули в будущее');
}
public function test_new_due_resets_even_from_terminal(): void
{
$this->upserter->upsert($this->source, [$this->item()]);
Event::sole()->update(['state' => EventState::Done, 'done_at' => now(), 'quiet_until' => CarbonImmutable::now()->addDays(3)]);
$this->upserter->upsert($this->source, [$this->item(['due_at' => '2026-10-15'])]);
$event = Event::sole();
$this->assertSame(EventState::Preparing, $event->state);
$this->assertNull($event->done_at);
$this->assertTrue($event->quiet_until->lt(CarbonImmutable::now()));
$this->assertSame('2026-10-15', $event->due_date->format('Y-m-d'));
}
public function test_reschedule_on_the_day_resets_today_event(): void
{
// Задокументированное поведение: перенос в день срока сбрасывает состояние.
// Поэтому повторяющиеся события кладут период в topic, а не меняют due_at.
CarbonImmutable::setTestNow('2026-09-30T10:00:00-03:00');
$this->upserter->upsert($this->source, [$this->item()]);
$this->assertSame(EventState::Today, Event::sole()->state);
$this->upserter->upsert($this->source, [$this->item(['due_at' => '2027-09-30', 'fire_at' => '2027-08-31'])]);
$this->assertSame(EventState::Scheduled, Event::sole()->state);
}
public function test_occurrences_are_independent_rows(): void
{
CarbonImmutable::setTestNow('2026-10-05T10:00:00-03:00');
$this->upserter->upsert($this->source, [
$this->item(['source_ref' => 'rent', 'topic' => 'payment:2026-09', 'due_at' => '2026-09-10', 'fire_at' => '2026-09-10']),
$this->item(['source_ref' => 'rent', 'topic' => 'payment:2026-10', 'due_at' => '2026-10-10', 'fire_at' => '2026-10-10']),
]);
$states = Event::orderBy('topic')->pluck('state')->map->value->all();
$this->assertSame(['overdue', 'scheduled'], $states);
}
public function test_fixed_due_is_stored_as_instant(): void
{
$this->upserter->upsert($this->source, [$this->item(['due_at' => '2026-09-10T08:15+02:00', 'fire_at' => '2026-09-03'])]);
$event = Event::sole();
$this->assertSame('fixed', $event->due_mode->value);
$this->assertSame('2026-09-10T06:15:00+00:00', $event->due_instant->utc()->toIso8601String());
$this->assertNull($event->due_date);
}
public function test_withdraw_marks_withdrawn(): void
{
$this->upserter->upsert($this->source, [$this->item()]);
$this->upserter->withdraw($this->upserter->find($this->source, 'document:918', 'expiry'));
$this->assertSame(EventState::Withdrawn, Event::sole()->state);
$this->assertNull($this->upserter->find($this->source, 'document:918', 'other'));
}
}