suggester = new ValidDueDateSuggester(new HomeworkRulesValidator()); } #[Test] public function suggestsNextValidDatesForMinimumDelay(): void { $rules = $this->creerRulesAvecDelai(3); $creationDate = new DateTimeImmutable('2026-03-18 10:00'); // mercredi // Requested date is too soon (jeudi 19 mars, only 1 day) $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-19'), $rules, $creationDate, ); self::assertNotEmpty($suggestions); self::assertLessThanOrEqual(3, count($suggestions)); // Each suggestion must be valid against the rules $validator = new HomeworkRulesValidator(); foreach ($suggestions as $date) { self::assertInstanceOf(DateTimeImmutable::class, $date); $result = $validator->valider($rules, $date, $creationDate); self::assertTrue($result->estValide(), 'Suggested date ' . $date->format('Y-m-d') . ' should be valid'); } } #[Test] public function suggestsUpToMaxSuggestions(): void { $rules = $this->creerRulesAvecDelai(3); $creationDate = new DateTimeImmutable('2026-03-18 10:00'); $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-19'), $rules, $creationDate, maxSuggestions: 5, ); self::assertCount(5, $suggestions); } #[Test] public function skipsWeekends(): void { $rules = $this->creerRulesAvecDelai(3); // Jeudi 19 mars, creation date is mercredi 18 mars $creationDate = new DateTimeImmutable('2026-03-18 10:00'); $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-19'), $rules, $creationDate, ); foreach ($suggestions as $date) { $dayOfWeek = (int) $date->format('N'); self::assertLessThanOrEqual(5, $dayOfWeek, 'Suggested date ' . $date->format('Y-m-d') . ' should not be a weekend'); } } #[Test] public function suggestsDatesInChronologicalOrder(): void { $rules = $this->creerRulesAvecDelai(3); $creationDate = new DateTimeImmutable('2026-03-18 10:00'); $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-19'), $rules, $creationDate, ); for ($i = 1; $i < count($suggestions); ++$i) { self::assertGreaterThan( $suggestions[$i - 1]->format('Y-m-d'), $suggestions[$i]->format('Y-m-d'), 'Suggestions should be in chronological order', ); } } #[Test] public function returnsEmptyWhenRulesDisabled(): void { $rules = $this->creerRulesAvecDelai(3, EnforcementMode::DISABLED, false); $creationDate = new DateTimeImmutable('2026-03-18 10:00'); $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-19'), $rules, $creationDate, ); self::assertEmpty($suggestions); } #[Test] public function searchIsBoundedAndDoesNotRunForever(): void { $rules = $this->creerRulesAvecDelai(30); $creationDate = new DateTimeImmutable('2026-03-18 10:00'); $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-19'), $rules, $creationDate, maxSuggestions: 100, ); // La recherche est bornée à 60 jours. Avec un délai de 30 jours, // seules les dates après le 17 avril sont valides (~21 jours ouvrés). self::assertNotEmpty($suggestions); self::assertLessThan(100, count($suggestions)); } #[Test] public function suggestsValidDatesForMondayRule(): void { $rules = $this->creerRulesAvecLundi(); // Samedi 21 mars, trying to create homework for lundi 23 mars (after friday cutoff) $creationDate = new DateTimeImmutable('2026-03-21 10:00'); $suggestions = $this->suggester->suggerer( new DateTimeImmutable('2026-03-23'), // lundi $rules, $creationDate, ); self::assertNotEmpty($suggestions); $validator = new HomeworkRulesValidator(); foreach ($suggestions as $date) { $result = $validator->valider($rules, $date, $creationDate); self::assertTrue($result->estValide(), 'Suggested date ' . $date->format('Y-m-d') . ' should be valid'); } } private function creerRulesAvecDelai( int $days, EnforcementMode $mode = EnforcementMode::HARD, bool $enabled = true, ): HomeworkRules { $rules = HomeworkRules::creer( tenantId: TenantId::fromString('550e8400-e29b-41d4-a716-446655440001'), now: new DateTimeImmutable('2026-03-01'), ); $rules->mettreAJour( rules: [new HomeworkRule(RuleType::MINIMUM_DELAY, ['days' => $days])], enforcementMode: $mode, enabled: $enabled, now: new DateTimeImmutable('2026-03-01'), ); return $rules; } private function creerRulesAvecLundi( EnforcementMode $mode = EnforcementMode::HARD, ): HomeworkRules { $rules = HomeworkRules::creer( tenantId: TenantId::fromString('550e8400-e29b-41d4-a716-446655440001'), now: new DateTimeImmutable('2026-03-01'), ); $rules->mettreAJour( rules: [new HomeworkRule(RuleType::NO_MONDAY_AFTER, ['day' => 'friday', 'time' => '12:00'])], enforcementMode: $mode, enabled: true, now: new DateTimeImmutable('2026-03-01'), ); return $rules; } }