email); self::assertSame('tok123', $invite->token); self::assertSame(RoleManager::STUDENT, $invite->role); self::assertSame(Invite::STATUS_PENDING, $invite->status); self::assertTrue($invite->isPending()); self::assertNull($invite->invitedBy); self::assertNull($invite->acceptedUserId); self::assertNull($invite->id); } public function testFromRowMapsCorrectly(): void { $invite = Invite::fromRow((object) [ 'id' => '5', 'email' => 'a@b.test', 'token' => 'tok123', 'role' => RoleManager::STUDENT, 'status' => Invite::STATUS_ACCEPTED, 'invited_by' => '2', 'accepted_user_id' => '9', 'accepted_at' => '2026-06-02 09:00:00', ]); self::assertSame(5, $invite->id); self::assertSame(2, $invite->invitedBy); self::assertSame(9, $invite->acceptedUserId); self::assertFalse($invite->isPending()); } public function testFromRowHandlesNullableIds(): void { $invite = Invite::fromRow((object) [ 'id' => '5', 'email' => 'a@b.test', 'token' => 'tok123', 'role' => RoleManager::STUDENT, 'status' => Invite::STATUS_PENDING, 'invited_by' => null, 'accepted_user_id' => null, 'accepted_at' => null, ]); self::assertNull($invite->invitedBy); self::assertNull($invite->acceptedUserId); self::assertTrue($invite->isPending()); } public function testToArrayContainsExpectedKeys(): void { $arr = (new Invite('a@b.test', 'tok', id: 1))->toArray(); foreach (['id', 'email', 'token', 'role', 'status', 'invited_by', 'accepted_user_id', 'accepted_at'] as $key) { self::assertArrayHasKey($key, $arr); } } }