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()); } }