studentId->equals($studentId)); self::assertTrue($link->guardianId->equals($guardianId)); self::assertSame(RelationshipType::FATHER, $link->relationshipType); self::assertTrue($link->tenantId->equals($tenantId)); self::assertEquals($createdAt, $link->createdAt); self::assertNotNull($link->createdBy); self::assertTrue($link->createdBy->equals($createdBy)); } #[Test] public function lierRecordsParentLieAEleveEvent(): void { $link = $this->createLink(); $events = $link->pullDomainEvents(); self::assertCount(1, $events); self::assertInstanceOf(ParentLieAEleve::class, $events[0]); self::assertTrue($events[0]->linkId->equals($link->id)); self::assertTrue($events[0]->studentId->equals($link->studentId)); self::assertTrue($events[0]->guardianId->equals($link->guardianId)); self::assertSame(RelationshipType::FATHER, $events[0]->relationshipType); self::assertTrue($events[0]->tenantId->equals($link->tenantId)); } #[Test] public function lierWithoutCreatedByAllowsNull(): void { $link = StudentGuardian::lier( studentId: UserId::fromString(self::STUDENT_ID), guardianId: UserId::fromString(self::GUARDIAN_ID), relationshipType: RelationshipType::MOTHER, tenantId: TenantId::fromString(self::TENANT_ID), createdAt: new DateTimeImmutable(), ); self::assertNull($link->createdBy); } #[Test] public function lierGeneratesUniqueId(): void { $link1 = $this->createLink(); $link2 = $this->createLink(); self::assertFalse($link1->id->equals($link2->id)); } #[Test] public function reconstituteRestoresAllProperties(): void { $id = StudentGuardianId::generate(); $studentId = UserId::fromString(self::STUDENT_ID); $guardianId = UserId::fromString(self::GUARDIAN_ID); $tenantId = TenantId::fromString(self::TENANT_ID); $createdBy = UserId::fromString(self::CREATED_BY_ID); $createdAt = new DateTimeImmutable('2026-02-10 10:00:00'); $link = StudentGuardian::reconstitute( id: $id, studentId: $studentId, guardianId: $guardianId, relationshipType: RelationshipType::TUTOR_M, tenantId: $tenantId, createdAt: $createdAt, createdBy: $createdBy, ); self::assertTrue($link->id->equals($id)); self::assertTrue($link->studentId->equals($studentId)); self::assertTrue($link->guardianId->equals($guardianId)); self::assertSame(RelationshipType::TUTOR_M, $link->relationshipType); self::assertTrue($link->tenantId->equals($tenantId)); self::assertEquals($createdAt, $link->createdAt); self::assertNotNull($link->createdBy); self::assertTrue($link->createdBy->equals($createdBy)); self::assertEmpty($link->pullDomainEvents()); } #[Test] public function reconstituteWithNullCreatedBy(): void { $link = StudentGuardian::reconstitute( id: StudentGuardianId::generate(), studentId: UserId::fromString(self::STUDENT_ID), guardianId: UserId::fromString(self::GUARDIAN_ID), relationshipType: RelationshipType::OTHER, tenantId: TenantId::fromString(self::TENANT_ID), createdAt: new DateTimeImmutable(), createdBy: null, ); self::assertNull($link->createdBy); } #[Test] public function delierRecordsParentDelieDEleveEvent(): void { $link = $this->createLink(); $link->pullDomainEvents(); // Drain lier() events $at = new DateTimeImmutable('2026-02-10 12:00:00'); $link->delier($at); $events = $link->pullDomainEvents(); self::assertCount(1, $events); self::assertInstanceOf(ParentDelieDEleve::class, $events[0]); self::assertTrue($events[0]->linkId->equals($link->id)); self::assertTrue($events[0]->studentId->equals($link->studentId)); self::assertTrue($events[0]->guardianId->equals($link->guardianId)); self::assertTrue($events[0]->tenantId->equals($link->tenantId)); self::assertEquals($at, $events[0]->occurredOn()); } #[Test] public function maxGuardiansPerStudentIsTwo(): void { self::assertSame(2, StudentGuardian::MAX_GUARDIANS_PER_STUDENT); } private function createLink(): StudentGuardian { return StudentGuardian::lier( studentId: UserId::fromString(self::STUDENT_ID), guardianId: UserId::fromString(self::GUARDIAN_ID), relationshipType: RelationshipType::FATHER, tenantId: TenantId::fromString(self::TENANT_ID), createdAt: new DateTimeImmutable('2026-02-10 10:00:00'), createdBy: UserId::fromString(self::CREATED_BY_ID), ); } }