generator = new CoercingSchemaGenerator(); } public function testNullableIntegerScalarAlsoAcceptsString(): void { $schema = $this->generator->generate(new ReflectionMethod(ListTasksTool::class, '__invoke')); // ?int $projectId -> ["null","integer"] relaxed with "string". self::assertSame(['null', 'integer', 'string'], $schema['properties']['projectId']['type']); } public function testRequiredIntegerScalarAlsoAcceptsString(): void { $schema = $this->generator->generate(new ReflectionMethod(ListTasksTool::class, '__invoke')); // int $limit = 100 -> "integer" relaxed to ["integer","string"]. self::assertSame(['integer', 'string'], $schema['properties']['limit']['type']); } public function testBooleanScalarAlsoAcceptsString(): void { $schema = $this->generator->generate(new ReflectionMethod(CreateTaskTool::class, '__invoke')); // ?bool $syncToCalendar -> ["boolean","null"] relaxed with "string". $type = $schema['properties']['syncToCalendar']['type']; self::assertContains('boolean', $type); self::assertContains('string', $type); self::assertContains('null', $type); } public function testArrayItemTypeAlsoAcceptsString(): void { $schema = $this->generator->generate(new ReflectionMethod(CreateTaskTool::class, '__invoke')); // int[] $tagIds -> items {type: integer} relaxed to {type: [integer, string]}. self::assertSame(['integer', 'string'], $schema['properties']['tagIds']['items']['type']); } public function testStringScalarIsLeftUntouched(): void { $schema = $this->generator->generate(new ReflectionMethod(CreateTaskTool::class, '__invoke')); // string $title stays a plain string (no spurious relaxation). self::assertSame('string', $schema['properties']['title']['type']); } public function testArrayContainerTypeIsNotRelaxed(): void { $schema = $this->generator->generate(new ReflectionMethod(CreateTaskTool::class, '__invoke')); // The array container itself must not gain "string". self::assertSame(['array', 'null'], $schema['properties']['tagIds']['type']); } }