(int) round($p->diffInDays($due)), $points); } public function test_no_preparation_gives_no_points(): void { $due = CarbonImmutable::parse('2026-08-14', 'UTC'); $this->assertSame([], Cascade::points($due, $due)); } public function test_halving_points(): void { $due = CarbonImmutable::parse('2026-09-30', 'UTC'); $this->assertSame([1], $this->daysBefore(Cascade::points($due->subDay(), $due), $due)); $this->assertSame([7, 3, 1], $this->daysBefore(Cascade::points($due->subDays(7), $due), $due)); $this->assertSame([30, 15, 7, 3, 1], $this->daysBefore(Cascade::points($due->subDays(30), $due), $due)); $this->assertSame([60, 30, 15, 7, 3, 1], $this->daysBefore(Cascade::points($due->subDays(60), $due), $due)); $this->assertSame([180, 90, 45, 22, 11, 5, 2, 1], $this->daysBefore(Cascade::points($due->subDays(180), $due), $due)); } public function test_points_are_local_midnights_ascending(): void { $due = CarbonImmutable::parse('2026-09-30', 'Asia/Shanghai'); $points = Cascade::points($due->subDays(7), $due); $this->assertSame('2026-09-23 00:00 Asia/Shanghai', $points[0]->format('Y-m-d H:i e')); $this->assertSame('2026-09-29 00:00 Asia/Shanghai', $points[2]->format('Y-m-d H:i e')); } }