create(); $product = Product::factory()->create(['manage_stock' => false]); return $product->purchases()->create(array_merge([ 'purchaser_id' => $user->id, 'purchaser_type' => User::class, 'quantity' => 1, 'amount' => 0, 'amount_paid' => 0, 'status' => PurchaseStatus::PENDING, ], $attrs)); } #[Test] public function is_booking_is_true_only_when_both_from_and_until_are_set(): void { $this->assertFalse($this->purchase()->isBooking()); $this->assertFalse( $this->purchase(['from' => Carbon::parse('2026-01-01 10:00:00')])->isBooking(), 'only from set → not a booking' ); $this->assertFalse( $this->purchase(['until' => Carbon::parse('2026-01-10 10:00:00')])->isBooking(), 'only until set → not a booking' ); $this->assertTrue( $this->purchase([ 'from' => Carbon::parse('2026-01-01 10:00:00'), 'until' => Carbon::parse('2026-01-10 10:00:00'), ])->isBooking() ); } #[Test] public function is_booking_ended_returns_false_for_non_bookings(): void { $purchase = $this->purchase(); $this->assertFalse($purchase->isBookingEnded()); } #[Test] public function is_booking_ended_returns_true_only_after_until(): void { Carbon::setTestNow(Carbon::parse('2026-01-05 10:00:00')); $future = $this->purchase([ 'from' => Carbon::parse('2026-01-01 10:00:00'), 'until' => Carbon::parse('2026-01-10 10:00:00'), ]); $this->assertFalse($future->isBookingEnded(), 'mid-booking → not ended'); Carbon::setTestNow(Carbon::parse('2026-01-11 10:00:00')); $this->assertTrue($future->isBookingEnded(), 'past until → ended'); } #[Test] public function bookings_scope_returns_only_rows_with_both_dates(): void { $bookingId = $this->purchase([ 'from' => Carbon::parse('2026-01-01 10:00:00'), 'until' => Carbon::parse('2026-01-10 10:00:00'), ])->id; $partialId = $this->purchase(['from' => Carbon::parse('2026-01-01 10:00:00')])->id; $plainId = $this->purchase()->id; $ids = ProductPurchase::query()->bookings()->pluck('id')->all(); $this->assertContains($bookingId, $ids); $this->assertNotContains($partialId, $ids); $this->assertNotContains($plainId, $ids); } #[Test] public function ended_bookings_scope_is_past_until_intersected_with_bookings(): void { Carbon::setTestNow(Carbon::parse('2026-05-15 10:00:00')); $past = $this->purchase([ 'from' => Carbon::parse('2026-01-01 10:00:00'), 'until' => Carbon::parse('2026-01-10 10:00:00'), ]); $future = $this->purchase([ 'from' => Carbon::parse('2026-09-01 10:00:00'), 'until' => Carbon::parse('2026-09-10 10:00:00'), ]); $ids = ProductPurchase::query()->endedBookings()->pluck('id')->all(); $this->assertContains($past->id, $ids); $this->assertNotContains($future->id, $ids); } }