Skip to content

Commit

Permalink
Make route extractor add full url for non-dynamic hosts and schemes f…
Browse files Browse the repository at this point in the history
…or easier log/debug
  • Loading branch information
Pierstoval committed Jan 2, 2025
1 parent 35a42ae commit 729b951
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
composer.lock
vendor
fixture-app/tests/FunctionalTest.php
reporting
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v1.1.1

* Fixes how routes with dynamic host and schemes were taken in account while they shouldn't have.
* Fix and enhance how Routes extractor references URLs of routes. If your routes have a default `host` or one or multiple `schemes`, the extractor will change the url to be an absolute URL instead of just the path, to ease debugging.

## v1.1.0

* **BC break**: Disable possibility to use an `E_USER_*` constant in `SMOKE_TESTING_ROUTES_METHODS`.
Expand Down
Empty file added fixture-app/.env.dev
Empty file.
10 changes: 5 additions & 5 deletions fixture-app/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="tests/bootstrap.php"
convertDeprecationsToExceptions="false"
colors="true"
executionOrder="depends,defects"
failOnRisky="true"
failOnWarning="true"
cacheDirectory=".phpunit.cache"
>
<php>
<ini name="display_errors" value="1" />
Expand Down
87 changes: 61 additions & 26 deletions fixture-app/src/Controller/TestRoutesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,59 @@

class TestRoutesController extends AbstractController
{
/** @Route("/param/{id}", name="get_param_without_default") */
#[Route("/param/{id}", name: "get_param_without_default")]
/** @Route("/param/{id}", name="get_param_without_default", methods={"GET"}) */
#[Route("/param/{id}", name: "get_param_without_default", methods: ["GET"])]
public function getParameterWithoutDefault(string $id): Response
{
return new Response("Content: $id");
}

/** @Route("/other_param/{id}", name="get_param_with_default", defaults={"id": "default_value"}) */
#[Route("/other_param/{id}", name: "get_param_with_default", defaults: ["id" => "default_value"])]
/** @Route("/other_param/{id}", name="get_param_with_default", defaults={"id": "default_value"}, methods={"GET"}) */
#[Route("/other_param/{id}", name: "get_param_with_default", defaults: ["id" => "default_value"], methods: ["GET"])]
public function getParameterWithDefault(string $id): Response
{
return new Response("Content: $id");
}

/** @Route("/200", name="get_200") */
#[Route("/200", name: "get_200")]
/** @Route("/200", name="get_200", methods={"GET"}) */
#[Route("/200", name: "get_200", methods: ["GET"])]
public function getOk(): Response
{
return new Response('200');
}

/** @Route("/302", name="get_302") */
#[Route("/302", name: "get_302")]
/** @Route("/302", name="get_302", methods={"GET"}) */
#[Route("/302", name: "get_302", methods: ["GET"])]
public function getRedirect(): RedirectResponse
{
return new RedirectResponse('/200');
}

/** @Route("/400", name="get_400") */
#[Route("/400", name: "get_400")]
/** @Route("/400", name="get_400", methods={"GET"}) */
#[Route("/400", name: "get_400", methods: ["GET"])]
public function get400(): Response
{
return new Response('400', 400);
}

/** @Route("/500", name="get_500") */
#[Route("/500", name: "get_500")]
/** @Route("/500", name="get_500", methods={"GET"}) */
#[Route("/500", name: "get_500", methods: ["GET"])]
public function get500(): Response
{
return new Response('500', 500);
}

/** @Route("/payload", name="get_with_payload") */
#[Route("/payload", name: "get_with_payload")]
/** @Route("/payload", name="get_with_payload", methods={"GET"}) */
#[Route("/payload", name: "get_with_payload", methods: ["GET"])]
public function getWithPayload(Request $request): Response
{
$payload = $request->getContent();

return new Response($payload, $payload ? 200 : 400);
}

/** @Route("/json/valid", name="json_valid") */
#[Route("/json/valid", name: "json_valid")]
/** @Route("/json/valid", name="json_valid", methods={"GET"}) */
#[Route("/json/valid", name: "json_valid", methods: ["GET"])]
public function getValidJson(): Response
{
return new JsonResponse([
Expand All @@ -72,8 +72,8 @@ public function getValidJson(): Response
]);
}

/** @Route("/json/valid-header", name="json_valid_header") */
#[Route("/json/valid-header", name: "json_valid_header")]
/** @Route("/json/valid-header", name="json_valid_header", methods={"GET"}) */
#[Route("/json/valid-header", name: "json_valid_header", methods: ["GET"])]
public function getValidJsonHeader(): Response
{
return new JsonResponse([
Expand All @@ -82,8 +82,8 @@ public function getValidJsonHeader(): Response
], 200, ['Content-Type' => 'application/json; charset=utf-8']);
}

/** @Route("/json/missing_header", name="json_missing_header") */
#[Route("/json/missing_header", name: "json_missing_header")]
/** @Route("/json/missing_header", name="json_missing_header", methods={"GET"}) */
#[Route("/json/missing_header", name: "json_missing_header", methods: ["GET"])]
public function getJsonInvalidHeader(): Response
{
return new Response(json_encode([
Expand All @@ -92,24 +92,52 @@ public function getJsonInvalidHeader(): Response
]));
}

/** @Route("/json/invalid", name="json_invalid") */
#[Route("/json/invalid", name: "json_invalid")]
/** @Route("/json/invalid", name="json_invalid", methods={"GET"}) */
#[Route("/json/invalid", name: "json_invalid", methods: ["GET"])]
public function getJsonInvalid(): Response
{
return new Response('{"message":', 200, [
'Content-Type' => 'application/json',
]);
}

/** @Route("/cookie/value", name="cookie_value") */
#[Route("/cookie/value", name: "cookie_value")]
/** @Route("/cookie/value", name="cookie_value", methods={"GET"}) */
#[Route("/cookie/value", name: "cookie_value", methods: ["GET"])]
public function getCookieValue(Request $request): Response
{
return new Response(\sprintf('Value: "%s"', $request->cookies->get('test_cookie')));
}

/** @Route("/content-type", name="content_type") */
#[Route("/content-type", name: "content_type")]
/** @Route("/host/fixed", name="host_fixed", host="test.localhost", methods={"GET"}) */
#[Route("/host/fixed", name: "host_fixed", host: "test.localhost", methods: ["GET"])]
public function getHostFixed(Request $request): Response
{
return new Response(\sprintf('Value: "%s"', $request->getHost()));
}

/** @Route("/host/dynamic", name="host_dynamic", host="{dynamic_host}", methods={"GET"}) */
#[Route("/host/dynamic", name: "host_dynamic", host: "{dynamic_host}", methods: ["GET"])]
public function getHostDynamic(): Response
{
throw new \RuntimeException('This route should not be tested by AllRoutesTest');
}

/** @Route("/scheme/fixed", name="scheme_fixed", schemes="http", methods={"GET"}) */
#[Route("/scheme/fixed", name: "scheme_fixed", schemes: 'http', methods: ['GET'])]
public function getSchemeFixed(Request $request): Response
{
return new Response(\sprintf('Value: "%s"', $request->getScheme()));
}

/** @Route("/scheme/dynamic", name="scheme_dynamic", schemes="{dynamic_scheme}", methods={"GET"}) */
#[Route("/scheme/dynamic", name: "scheme_dynamic", schemes: "{dynamic_scheme}", methods: ["GET"])]
public function getSchemeDynamic(): Response
{
throw new \RuntimeException('This route should not be tested by AllRoutesTest');
}

/** @Route("/content-type", name="content_type", methods={"GET"}) */
#[Route("/content-type", name: "content_type", methods: ["GET"])]
public function getContentType(Request $request): Response
{
$contentType = method_exists($request, 'getContentTypeFormat')
Expand All @@ -123,4 +151,11 @@ public function getContentType(Request $request): Response
'format' => $contentType,
]);
}

/** @Route("/post", name="post_route", methods={"POST"}) */
#[Route('/post', name: 'post_route', methods: ["POST"])]
public function getPostRoute(Request $request): Response
{
throw new \RuntimeException('This route should not be tested by AllRoutesTest');
}
}
34 changes: 17 additions & 17 deletions fixture-app/symfony.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"branch": "main",
"version": "1.10",
"ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05"
},
"files": []
}
},
"phpunit/phpunit": {
"version": "10.5",
Expand All @@ -29,31 +28,32 @@
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.3",
"ref": "da0c8be8157600ad34f10ff0c9cc91232522e047"
"ref": "1781ff40d8a17d87cf53f8d4cf0c8346ed2bb461"
},
"files": [
"./bin/console"
"bin/console"
]
},
"symfony/flex": {
"version": "2.2",
"version": "2.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "146251ae39e06a95be0fe3d13c807bcf3938b172"
"version": "2.4",
"ref": "52e9754527a15e2b79d9a610f98185a1fe46622a"
},
"files": [
"./.env"
".env",
".env.dev"
]
},
"symfony/framework-bundle": {
"version": "7.0",
"version": "6.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "7.0",
"ref": "de6e1b3e2bbbe69e36262d72c3f3db858b1ab391"
"version": "5.4",
"ref": "3cd216a4d007b78d8554d44a5b1c0a446dab24fb"
},
"files": [
"config/packages/cache.yaml",
Expand All @@ -67,7 +67,7 @@
]
},
"symfony/maker-bundle": {
"version": "1.48",
"version": "1.50",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
Expand All @@ -76,12 +76,12 @@
}
},
"symfony/phpunit-bridge": {
"version": "6.4",
"version": "7.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.3",
"ref": "1f5830c331065b6e4c9d5fa2105e322d29fcd573"
"ref": "a411a0480041243d97382cac7984f7dce7813c08"
},
"files": [
".env.test",
Expand All @@ -91,12 +91,12 @@
]
},
"symfony/routing": {
"version": "7.0",
"version": "6.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.2",
"ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6"
"version": "6.1",
"ref": "a44010c0d06989bd4f154aa07d2542d47caf5b83"
},
"files": [
"config/packages/routing.yaml",
Expand Down
16 changes: 16 additions & 0 deletions fixture-app/tests/FunctionalSmokeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,22 @@ public function testGetWithPreRequestCallback(): void
);
}

public function testWithFixedHost(): void
{
$this->runFunctionalTest(
FunctionalTestData::withUrl('http://test.localhost/host/fixed')
->expectRouteName('host_fixed')
->expectStatusCode(200)
->expectTextToBePresent('Value: "test.localhost"')
);
$this->runFunctionalTest(
FunctionalTestData::withUrl('/host/fixed')
->expectRouteName('host_fixed')
->expectStatusCode(200)
->expectTextToBePresent('Value: "test.localhost"')
);
}

public function testGetWithContentType(): void
{
$this->runFunctionalTest(
Expand Down
4 changes: 1 addition & 3 deletions fixture-app/tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@

require dirname(__DIR__).'/vendor/autoload.php';

if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
if (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}

Expand Down
25 changes: 24 additions & 1 deletion src/RoutesExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

namespace Pierstoval\SmokeTesting;

use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouterInterface;

final class RoutesExtractor
{
public static function extractRoutesFromRouter(RouterInterface $router): \Generator
{
foreach ($router->getRouteCollection() as $routeName => $route) {
/** @var Route $route */
$compiledRoute = $route->compile();
$variables = $compiledRoute->getVariables();
if (count($variables) > 0) {
Expand Down Expand Up @@ -39,8 +42,28 @@ public static function extractRoutesFromRouter(RouterInterface $router): \Genera
$methods[] = 'GET';
}

$routerReferenceType = UrlGeneratorInterface::ABSOLUTE_PATH;

$hasDynamicHost = \str_contains($route->getHost(), '{');
if ($hasDynamicHost) {
continue;
}

$hasDynamicScheme = \array_filter(\array_map(static fn($item) => \str_contains($item, '{'), $route->getSchemes()));
if ($hasDynamicScheme) {
continue;
}

$hasNonDynamicHost = $route->getHost() && !$hasDynamicHost;
$hasNonDynamicScheme = $route->getSchemes() && !$hasDynamicScheme;

if ($hasNonDynamicHost || $hasNonDynamicScheme) {
// Generate full URI if route is configured with a host.
$routerReferenceType = UrlGeneratorInterface::ABSOLUTE_URL;
}

foreach ($methods as $method) {
$routePath = $router->generate($routeName);
$routePath = $router->generate($routeName, [], $routerReferenceType);
yield "$method {$routePath}" => ['httpMethod' => $method, 'routeName' => $routeName, 'routePath' => $routePath];
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/MakeSmokeTestsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ public function testMakeStateProvider(bool $useDto): void
public static function provideSmokeTestCases(): \Generator
{
yield 'Generate smoke tests with DTO' => [
'dto' => true,
'useDto' => true,
];

yield 'Generate smoke tests without DTO' => [
'dto' => false,
'useDto' => false,
];
}

Expand Down
Loading

0 comments on commit 729b951

Please sign in to comment.