trove/tests/Unit/Models/PageCrawlTest.php

111 lines
3.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Models;
use App\Enums\CrawlOutcomeEnum;
use App\Models\Page;
use App\Models\PageCrawl;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PageCrawlTest extends TestCase
{
use RefreshDatabase;
public function test_page_crawl_fillable_fields_persist_and_casts_are_applied(): void
{
$page = Page::factory()->create(['url' => 'https://example.com/page-1']);
$scheduledFor = Carbon::parse('2026-05-01 10:00:00');
$lockedAt = Carbon::parse('2026-05-01 10:01:00');
$completedAt = Carbon::parse('2026-05-01 10:01:05');
$crawl = PageCrawl::create([
'page_id' => $page->id,
'domain' => 'example.com',
'priority' => 5,
'scheduled_for' => $scheduledFor,
'locked_at' => $lockedAt,
'completed_at' => $completedAt,
'outcome' => CrawlOutcomeEnum::Success,
'status_code' => 200,
'error_message' => null,
]);
$fresh = $crawl->fresh();
$this->assertNotNull($fresh);
// domain / priority round-trip
$this->assertSame('example.com', $fresh->domain);
$this->assertSame(5, $fresh->priority);
// outcome is cast to the enum
$this->assertInstanceOf(CrawlOutcomeEnum::class, $fresh->outcome);
$this->assertSame(CrawlOutcomeEnum::Success, $fresh->outcome);
// datetime casts
$this->assertInstanceOf(Carbon::class, $fresh->scheduled_for);
$this->assertInstanceOf(Carbon::class, $fresh->locked_at);
$this->assertInstanceOf(Carbon::class, $fresh->completed_at);
$this->assertTrue($scheduledFor->equalTo($fresh->scheduled_for));
$this->assertTrue($lockedAt->equalTo($fresh->locked_at));
$this->assertTrue($completedAt->equalTo($fresh->completed_at));
// nullable columns
$this->assertNull($fresh->error_message);
// status_code persists
$this->assertSame(200, $fresh->status_code);
}
public function test_page_crawl_belongs_to_a_page(): void
{
$page = Page::factory()->create(['url' => 'https://example.com/page-2']);
$crawl = PageCrawl::create([
'page_id' => $page->id,
'domain' => 'example.com',
'priority' => 1,
'scheduled_for' => Carbon::now(),
]);
$related = $crawl->page;
$this->assertInstanceOf(Page::class, $related);
$this->assertSame($page->id, $related->id);
}
public function test_deleting_a_page_cascades_to_its_page_crawls(): void
{
$page = Page::factory()->create(['url' => 'https://example.com/page-cascade']);
PageCrawl::factory()->page($page)->create();
PageCrawl::factory()->page($page)->successful()->create();
PageCrawl::factory()->page($page)->failed('timeout during fetch')->create();
$this->assertSame(3, PageCrawl::count());
$page->delete();
$this->assertSame(0, PageCrawl::count());
}
public function test_pending_crawls_are_filtered_by_null_outcome(): void
{
$page = Page::factory()->create(['url' => 'https://example.com/page-pending']);
$pending = PageCrawl::factory()->page($page)->create();
PageCrawl::factory()->page($page)->successful()->create();
PageCrawl::factory()->page($page)->failed('connection refused')->create();
$this->assertSame(1, PageCrawl::whereNull('outcome')->count());
$this->assertSame($pending->id, PageCrawl::whereNull('outcome')->first()->id);
$this->assertSame(2, PageCrawl::whereNotNull('outcome')->count());
}
}