validTrimesterConfig(); self::assertSame(PeriodType::TRIMESTER, $config->type); self::assertCount(3, $config->periods); self::assertSame('2025-09-01', $config->startDate()->format('Y-m-d')); self::assertSame('2026-06-30', $config->endDate()->format('Y-m-d')); } #[Test] public function itCreatesValidSemesterConfiguration(): void { $config = new PeriodConfiguration(PeriodType::SEMESTER, [ new AcademicPeriod(1, 'S1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2026-01-31')), new AcademicPeriod(2, 'S2', new DateTimeImmutable('2026-02-01'), new DateTimeImmutable('2026-06-30')), ]); self::assertSame(PeriodType::SEMESTER, $config->type); self::assertCount(2, $config->periods); } #[Test] public function itRejectsWrongPeriodCountForTrimester(): void { $this->expectException(InvalidPeriodCountException::class); new PeriodConfiguration(PeriodType::TRIMESTER, [ new AcademicPeriod(1, 'S1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2026-01-31')), new AcademicPeriod(2, 'S2', new DateTimeImmutable('2026-02-01'), new DateTimeImmutable('2026-06-30')), ]); } #[Test] public function itRejectsWrongPeriodCountForSemester(): void { $this->expectException(InvalidPeriodCountException::class); new PeriodConfiguration(PeriodType::SEMESTER, [ new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-30')), new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')), new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')), ]); } #[Test] public function itRejectsOverlappingPeriods(): void { $this->expectException(PeriodsOverlapException::class); new PeriodConfiguration(PeriodType::TRIMESTER, [ new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-12-01')), new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-11-30'), new DateTimeImmutable('2026-02-28')), new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')), ]); } #[Test] public function itRejectsCoverageGap(): void { $this->expectException(PeriodsCoverageGapException::class); new PeriodConfiguration(PeriodType::TRIMESTER, [ new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-28')), new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')), new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')), ]); } #[Test] public function itSortsPeriodsByStartDate(): void { $config = new PeriodConfiguration(PeriodType::TRIMESTER, [ new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')), new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-30')), new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')), ]); self::assertSame('T1', $config->periods[0]->label); self::assertSame('T2', $config->periods[1]->label); self::assertSame('T3', $config->periods[2]->label); } #[Test] public function itFindsCurrentPeriod(): void { $config = $this->validTrimesterConfig(); $current = $config->currentPeriod(new DateTimeImmutable('2025-10-15')); self::assertNotNull($current); self::assertSame('T1', $current->label); $current = $config->currentPeriod(new DateTimeImmutable('2026-01-15')); self::assertNotNull($current); self::assertSame('T2', $current->label); $current = $config->currentPeriod(new DateTimeImmutable('2026-05-01')); self::assertNotNull($current); self::assertSame('T3', $current->label); } #[Test] public function itReturnsNullWhenNoCurrentPeriod(): void { $config = $this->validTrimesterConfig(); self::assertNull($config->currentPeriod(new DateTimeImmutable('2025-08-01'))); self::assertNull($config->currentPeriod(new DateTimeImmutable('2026-07-01'))); } #[Test] public function itFindsPeriodBySequence(): void { $config = $this->validTrimesterConfig(); $period = $config->periodBySequence(2); self::assertNotNull($period); self::assertSame('T2', $period->label); self::assertNull($config->periodBySequence(4)); } private function validTrimesterConfig(): PeriodConfiguration { return new PeriodConfiguration(PeriodType::TRIMESTER, [ new AcademicPeriod(1, 'T1', new DateTimeImmutable('2025-09-01'), new DateTimeImmutable('2025-11-30')), new AcademicPeriod(2, 'T2', new DateTimeImmutable('2025-12-01'), new DateTimeImmutable('2026-02-28')), new AcademicPeriod(3, 'T3', new DateTimeImmutable('2026-03-01'), new DateTimeImmutable('2026-06-30')), ]); } }