repository = new InMemoryTeacherReplacementRepository(); $this->now = new DateTimeImmutable('2026-03-15 10:00:00'); } #[Test] public function itReturnsActiveReplacementsForTenant(): void { $this->saveActiveReplacement(); $handler = $this->createHandler(); $result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID)); self::assertCount(1, $result); self::assertContainsOnlyInstancesOf(ReplacementDto::class, $result); } #[Test] public function itReturnsEmptyArrayWhenNoneExist(): void { $handler = $this->createHandler(); $result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID)); self::assertSame([], $result); } #[Test] public function itMapsCorrectlyToDtos(): void { $replacement = $this->saveActiveReplacement(); $handler = $this->createHandler(); $result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID)); self::assertCount(1, $result); $dto = $result[0]; self::assertSame((string) $replacement->id, $dto->id); self::assertSame(self::REPLACED_TEACHER_ID, $dto->replacedTeacherId); self::assertSame(self::REPLACEMENT_TEACHER_ID, $dto->replacementTeacherId); self::assertEquals(new DateTimeImmutable('2026-03-01'), $dto->startDate); self::assertEquals(new DateTimeImmutable('2026-03-31'), $dto->endDate); self::assertSame(ReplacementStatus::ACTIVE->value, $dto->status); self::assertSame('Congé maladie', $dto->reason); self::assertCount(1, $dto->classes); self::assertSame(self::CLASS_ID, $dto->classes[0]['classId']); self::assertSame(self::SUBJECT_ID, $dto->classes[0]['subjectId']); } #[Test] public function itFiltersOutExpiredReplacements(): void { $this->saveReplacement( startDate: new DateTimeImmutable('2026-01-01'), endDate: new DateTimeImmutable('2026-02-01'), ); $handler = $this->createHandler(); $result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID)); self::assertSame([], $result); } #[Test] public function itFiltersOutEndedReplacements(): void { $replacement = $this->saveActiveReplacement(); $replacement->terminer(new DateTimeImmutable('2026-03-10')); $this->repository->save($replacement); $handler = $this->createHandler(); $result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID)); self::assertSame([], $result); } #[Test] public function itDoesNotReturnReplacementsFromOtherTenants(): void { $this->saveReplacement(tenantId: self::OTHER_TENANT_ID); $handler = $this->createHandler(); $result = $handler(new GetActiveReplacementsQuery(self::TENANT_ID)); self::assertSame([], $result); } private function saveActiveReplacement(): TeacherReplacement { return $this->saveReplacement(); } private function saveReplacement( ?string $tenantId = null, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null, ): TeacherReplacement { $replacement = TeacherReplacement::designer( tenantId: TenantId::fromString($tenantId ?? self::TENANT_ID), replacedTeacherId: UserId::fromString(self::REPLACED_TEACHER_ID), replacementTeacherId: UserId::fromString(self::REPLACEMENT_TEACHER_ID), startDate: $startDate ?? new DateTimeImmutable('2026-03-01'), endDate: $endDate ?? new DateTimeImmutable('2026-03-31'), classes: [ new ClassSubjectPair( ClassId::fromString(self::CLASS_ID), SubjectId::fromString(self::SUBJECT_ID), ), ], reason: 'Congé maladie', createdBy: UserId::fromString(self::CREATED_BY_ID), now: new DateTimeImmutable('2026-02-15 10:00:00'), ); $this->repository->save($replacement); return $replacement; } private function createHandler(): GetActiveReplacementsHandler { $now = $this->now; $clock = new class($now) implements Clock { public function __construct(private readonly DateTimeImmutable $now) { } public function now(): DateTimeImmutable { return $this->now; } }; return new GetActiveReplacementsHandler($this->repository, $clock); } }