setRawAttributes([ 'source_catalog_item_id' => '20', 'source_variant_id' => '30', 'validity_time_id' => '40', 'used_at' => null, 'scanner_user_id' => '15', 'user_id' => '10', ]); $this->assertSame('tickets', $ticket->getTable()); $this->assertFalse($ticket->usesTimestamps()); $this->assertSame(20, $ticket->source_catalog_item_id); $this->assertSame(30, $ticket->source_variant_id); $this->assertSame(40, $ticket->validity_time_id); $this->assertNull($ticket->used_at); $this->assertSame(15, $ticket->scanner_user_id); $this->assertSame(10, $ticket->user_id); $this->assertInstanceOf(Tenant::class, $ticket->tenant()->getRelated()); $this->assertInstanceOf(User::class, $ticket->user()->getRelated()); $this->assertInstanceOf(User::class, $ticket->scannerUser()->getRelated()); $this->assertInstanceOf(CatalogItem::class, $ticket->sourceCatalogItem()->getRelated()); $this->assertInstanceOf(Variant::class, $ticket->sourceVariant()->getRelated()); $this->assertInstanceOf(ValidityTime::class, $ticket->validityTime()->getRelated()); } public function test_unused_ticket_without_validity_time_is_valid(): void { $this->assertTrue((new Ticket)->isValid()); } public function test_fixed_window_controls_ticket_validity(): void { Carbon::setTestNow('2026-07-21 10:00:00'); $ticket = $this->ticketWithValidityTime(new ValidityTime([ 'type' => ValidityTimeType::FixedWindow, 'fixed_starts_at' => now()->subHour(), 'fixed_expires_at' => now()->addHour(), ])); $this->assertTrue($ticket->isValid()); $this->assertFalse($ticket->is_expired); } public function test_time_window_is_resolved_for_current_date(): void { Carbon::setTestNow('2026-07-21 12:00:00'); $ticket = new Ticket; $ticket->setRelation('validityTime', new ValidityTime([ 'type' => ValidityTimeType::TimeWindow, 'start_time' => '10:00:00', 'end_time' => '14:00:00', ])); $this->assertSame('2026-07-21 10:00:00', $ticket->getEffectiveStartsAt()->format('Y-m-d H:i:s')); $this->assertSame('2026-07-21 14:00:00', $ticket->getEffectiveExpiresAt()->format('Y-m-d H:i:s')); $this->assertTrue($ticket->isValid()); } public function test_ticket_is_invalid_when_validity_time_expires(): void { Carbon::setTestNow('2026-07-21 10:00:00'); $ticket = $this->ticketWithValidityTime(new ValidityTime([ 'type' => ValidityTimeType::FixedWindow, 'fixed_expires_at' => now(), ])); $this->assertFalse($ticket->isValid()); $this->assertTrue($ticket->is_expired); } public function test_used_ticket_is_invalid_and_not_reported_as_expired(): void { $ticket = $this->ticketWithValidityTime(new ValidityTime([ 'type' => ValidityTimeType::FixedWindow, 'fixed_expires_at' => now()->subHour(), ])); $ticket->used_at = now()->subMinute(); $this->assertFalse($ticket->is_valid); $this->assertFalse($ticket->is_expired); $this->assertTrue($ticket->is_used); } private function ticketWithValidityTime(ValidityTime $validityTime): Ticket { $ticket = new Ticket; $ticket->setRelation('validityTime', $validityTime); return $ticket; } }