* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ declare(strict_types=1); namespace League\Uri\UriTemplate; use JsonException; use League\Uri\Exceptions\SyntaxError; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use RuntimeException; use Throwable; use const JSON_THROW_ON_ERROR; #[CoversClass(Template::class)] final class TemplateTest extends TestCase { private static string $rootPath = __DIR__.'/../../vendor/uri-templates/uritemplate-test'; /** @var array */ private static array $testFilenames = [ 'spec-examples.json', 'negative-tests.json', 'extended-tests.json', ]; #[DataProvider('uriTemplateSpecificationDataProvider')] #[Test] public function testItCompliesWithUriTemplatesExpansionTests( array $variables, string $input, string|array|false $expected ): void { if (false === $expected) { $this->expectException(Throwable::class); } $result = Template::new($input)->expand(new VariableBag($variables)); if (is_array($expected)) { self::assertContains($result, $expected); } else { self::assertSame($expected, $result); } } /** * @throws JsonException * @throws RuntimeException * @return iterable|false * }> */ public static function uriTemplateSpecificationDataProvider(): iterable { foreach (static::$testFilenames as $path) { $path = static::$rootPath.'/'.ltrim($path, '/'); if (false === $content = file_get_contents($path)) { throw new RuntimeException("unable to connect to the path `$path`."); } /** @var array $records */ $records = json_decode($content, true, 512, JSON_THROW_ON_ERROR); foreach ($records as $title => $testSuite) { $level = $testSuite['level'] ?? null; $variables = $testSuite['variables']; foreach ($testSuite['testcases'] as $offset => [$input, $expected]) { yield $title.' - '.$level.' # '.($offset + 1) => [ 'variables' => $variables, 'input' => $input, 'expected' => $expected, ]; } } } } #[DataProvider('providesValidNotation')] public function testItCanBeInstantiatedWithAValidNotation(string $notation): void { self::assertSame($notation, Template::new($notation)->value); self::assertSame($notation, (string) Template::new($notation)); } public static function providesValidNotation(): iterable { return [ 'complex template' => ['http://example.com{+path}{/segments}{?query,more*,foo[]*}'], 'template without expression' => ['foobar'], ]; } #[DataProvider('providesInvalidNotation')] public function testItFailsToInstantiatedWithAnInvalidNotation(string $notation): void { self::expectException(SyntaxError::class); Template::new($notation); } public static function providesInvalidNotation(): iterable { return [ ['fooba{r'], ['fooba}r'], ['fooba}{r'], ['{foo{bar'], ['{foo}}bar'], ]; } #[DataProvider('expectedVariableNames')] public function testGetVariableNames(string $template, array $expected): void { self::assertSame($expected, Template::new($template)->variableNames); } public static function expectedVariableNames(): iterable { return [ [ 'template' => '', 'expected' => [], ], [ 'template' => '{foo}{bar}{420}', 'expected' => ['foo', 'bar', '420'], ], [ 'template' => '{foo}{foo:2}{+foo}', 'expected' => ['foo'], ], [ 'template' => '{bar}{foo}', 'expected' => ['bar', 'foo'], ], ]; } #[DataProvider('providesExpansion')] public function testItCanExpandVariables(string $notation, array $variables, string $expected): void { self::assertSame($expected, Template::new($notation)->expand($variables)); } public static function providesExpansion(): iterable { return [ 'with variables' => [ 'notation' => 'foobar{var}', 'variables' => ['var' => 'yolo'], 'expected' => 'foobaryolo', ], 'with no variables' => [ 'notation' => 'foobar', 'variables' => [], 'expected' => 'foobar', ], ]; } public function testExpandOrFailIfAtLeastOneVariableIsMissing(): void { $this->expectException(TemplateCanNotBeExpanded::class); Template::new('{var}{baz}')->expandOrFail(['var' => 'bar']); } #[Test] public function it_can_expand_with_expand_or_fail_when_all_variables_are_present(): void { self::assertSame('barfoo', Template::new('{var}{baz}')->expandOrFail(['var' => 'bar', 'baz' => 'foo'])); } }