shouldReceive('cancellationCutoffHours')->andReturn($studioDefaultHours); return new CancellationPolicy($settings); } public function testCutoffHoursFallsBackToStudioDefaultWhenOverrideNull(): void { self::assertSame(24, $this->policy(24)->cutoffHours(null)); } public function testCutoffHoursUsesOverrideWhenSet(): void { // Even a zero override wins over the studio default — it is a deliberate // "cancel any time" choice, not an absent value. self::assertSame(0, $this->policy(24)->cutoffHours(0)); self::assertSame(72, $this->policy(24)->cutoffHours(72)); } public function testCutoffHoursIgnoresNegativeOverride(): void { self::assertSame(24, $this->policy(24)->cutoffHours(-5)); } public function testStudentMayCancelWhenOutsideWindow(): void { // 48h before a 24h cutoff — comfortably outside. self::assertTrue( $this->policy(24)->studentMayCancel('2026-07-03 10:00:00', null, '2026-07-01 10:00:00') ); } public function testStudentMayNotCancelWhenInsideWindow(): void { // 12h before a 24h cutoff — inside the window. self::assertFalse( $this->policy(24)->studentMayCancel('2026-07-01 22:00:00', null, '2026-07-01 10:00:00') ); } public function testExactlyAtCutoffBoundaryIsAllowed(): void { // Exactly 24h ahead of a 24h cutoff is still cancellable. self::assertTrue( $this->policy(24)->studentMayCancel('2026-07-02 10:00:00', null, '2026-07-01 10:00:00') ); } public function testZeroCutoffAlwaysAllowsCancellation(): void { self::assertTrue( $this->policy(0)->studentMayCancel('2026-07-01 10:00:01', null, '2026-07-01 10:00:00') ); } public function testOfferingOverrideNarrowsWindow(): void { // Studio default is 0 (any time), but this offering demands 48h notice. self::assertFalse( $this->policy(0)->studentMayCancel('2026-07-02 10:00:00', 48, '2026-07-01 10:00:00') ); } public function testPastLessonCannotBeCancelled(): void { self::assertFalse( $this->policy(24)->studentMayCancel('2026-07-01 09:00:00', null, '2026-07-01 10:00:00') ); } public function testDescribeCutoffUsesDaysForWholeDays(): void { $policy = $this->policy(24); self::assertSame('1 day', $policy->describeCutoff(24)); self::assertSame('2 days', $policy->describeCutoff(48)); } public function testDescribeCutoffUsesHoursOtherwise(): void { $policy = $this->policy(24); self::assertSame('12 hours', $policy->describeCutoff(12)); self::assertSame('1 hour', $policy->describeCutoff(1)); } }