ExtractedEndpointDataTest.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <?php
  2. namespace Knuckles\Scribe\Tests\Unit;
  3. use Illuminate\Routing\Route as LaravelRoute;
  4. use Illuminate\Support\Facades\Route;
  5. use Knuckles\Camel\Extraction\ExtractedEndpointData;
  6. use Knuckles\Scribe\Matching\RouteMatcher;
  7. use Knuckles\Scribe\Scribe;
  8. use Knuckles\Scribe\Tests\BaseLaravelTest;
  9. use Knuckles\Scribe\Tests\Fixtures\TestController;
  10. use Knuckles\Scribe\Tools\Utils as u;
  11. class ExtractedEndpointDataTest extends BaseLaravelTest
  12. {
  13. /** @test */
  14. public function normalizes_resource_url_params()
  15. {
  16. Route::apiResource('things', TestController::class)->only('show');
  17. $route = $this->getRoute(['prefixes' => '*']);
  18. $this->assertEquals('things/{thing}', $this->originalUri($route));
  19. $this->assertEquals('things/{id}', $this->expectedUri($route));
  20. Route::apiResource('things.otherthings', TestController::class)->only('destroy');
  21. $route = $this->getRoute(['prefixes' => '*/otherthings/*']);
  22. $this->assertEquals('things/{thing}/otherthings/{otherthing}', $this->originalUri($route));
  23. $this->assertEquals('things/{thing_id}/otherthings/{id}', $this->expectedUri($route));
  24. }
  25. /** @test */
  26. public function allows_user_specified_normalization()
  27. {
  28. Scribe::normalizeEndpointUrlUsing(function (string $url, LaravelRoute $route) {
  29. if ($url == 'things/{thing}') return 'things/{the_id_of_the_thing}';
  30. if ($route->named('things.otherthings.destroy')) return 'things/{thing-id}/otherthings/{other_thing-id}';
  31. });
  32. Route::apiResource('things', TestController::class)->only('show');
  33. $route = $this->getRoute(['prefixes' => '*']);
  34. $this->assertEquals('things/{thing}', $this->originalUri($route));
  35. $this->assertEquals('things/{the_id_of_the_thing}', $this->expectedUri($route));
  36. Route::apiResource('things.otherthings', TestController::class)->only('destroy');
  37. $route = $this->getRoute(['prefixes' => '*/otherthings/*']);
  38. $this->assertEquals('things/{thing}/otherthings/{otherthing}', $this->originalUri($route));
  39. $this->assertEquals('things/{thing-id}/otherthings/{other_thing-id}', $this->expectedUri($route));
  40. Scribe::normalizeEndpointUrlUsing(null);
  41. }
  42. /** @test */
  43. public function allows_user_specified_normalization_fallback_to_default()
  44. {
  45. Scribe::normalizeEndpointUrlUsing(function (string $url, LaravelRoute $route,
  46. \ReflectionFunctionAbstract $method, ?\ReflectionClass $controller, callable $default) {
  47. if ($route->named('things.otherthings.destroy')) return 'things/{thing-id}/otherthings/{other_thing-id}';
  48. return $default();
  49. });
  50. Route::apiResource('things', TestController::class)->only('show');
  51. $route = $this->getRoute(['prefixes' => '*']);
  52. $this->assertEquals('things/{thing}', $this->originalUri($route));
  53. $this->assertEquals('things/{id}', $this->expectedUri($route));
  54. Route::apiResource('things.otherthings', TestController::class)->only('destroy');
  55. $route = $this->getRoute(['prefixes' => '*/otherthings/*']);
  56. $this->assertEquals('things/{thing}/otherthings/{otherthing}', $this->originalUri($route));
  57. $this->assertEquals('things/{thing-id}/otherthings/{other_thing-id}', $this->expectedUri($route));
  58. Scribe::normalizeEndpointUrlUsing(null);
  59. }
  60. /** @test */
  61. public function normalizes_resource_url_params_from_underscores_to_hyphens()
  62. {
  63. Route::apiResource('audio-things', TestController::class)->only('show');
  64. $route = $this->getRoute(['prefixes' => '*']);
  65. $this->assertEquals('audio-things/{audio_thing}', $this->originalUri($route));
  66. $this->assertEquals('audio-things/{id}', $this->expectedUri($route));
  67. Route::apiResource('big-users.audio-things.things', TestController::class)->only('store');
  68. $route = $this->getRoute(['prefixes' => '*big-users*']);
  69. $this->assertEquals('big-users/{big_user}/audio-things/{audio_thing}/things', $this->originalUri($route));
  70. $this->assertEquals('big-users/{big_user_id}/audio-things/{audio_thing_id}/things', $this->expectedUri($route));
  71. }
  72. /** @test */
  73. public function normalizes_nonresource_url_params_with_inline_bindings()
  74. {
  75. Route::get('things/{thing:slug}', [TestController::class, 'show']);
  76. $route = $this->getRoute(['prefixes' => '*']);
  77. $this->assertEquals('things/{thing}', $this->originalUri($route));
  78. $this->assertEquals('things/{thing_slug}', $this->expectedUri($route));
  79. }
  80. /** @test */
  81. public function normalizes_url_param_with_eloquent_model_binding()
  82. {
  83. Route::get("test-posts/{test_post}", [TestController::class, 'withInjectedModelFullParamName']);
  84. $route = $this->getRoute(['prefixes' => '*']);
  85. $this->assertEquals('test-posts/{test_post}', $this->originalUri($route));
  86. $this->assertEquals('test-posts/{test_post_slug}', $this->expectedUri($route, withReflectedMethod: true));
  87. }
  88. protected function expectedUri(LaravelRoute $route, $withReflectedMethod = false): string
  89. {
  90. return $this->endpoint($route, $withReflectedMethod)->uri;
  91. }
  92. protected function originalUri(LaravelRoute $route): string
  93. {
  94. return $route->uri;
  95. }
  96. protected function endpoint(LaravelRoute $route, $withReflectedMethod = false): ExtractedEndpointData
  97. {
  98. if ($withReflectedMethod) {
  99. [$controllerName, $methodName] = u::getRouteClassAndMethodNames($route);
  100. $method = u::getReflectedRouteMethod([$controllerName, $methodName]);
  101. } else {
  102. // We're testing resource routes, and we may not have methods that exist for all of them (show, index, etc).
  103. // Just use this dummy so we don't have null
  104. $method = new \ReflectionFunction('dump');
  105. }
  106. return new ExtractedEndpointData([
  107. 'route' => $route,
  108. 'uri' => $route->uri,
  109. 'httpMethods' => $route->methods,
  110. 'method' => $method,
  111. ]);
  112. }
  113. protected function getRoute(array $matchRules): LaravelRoute
  114. {
  115. $routeRules[0]['match'] = array_merge($matchRules, ['domains' => '*']);
  116. $matchedRoutes = (new RouteMatcher)->getRoutes($routeRules);
  117. $this->assertCount(1, $matchedRoutes);
  118. return $matchedRoutes[0]->getRoute();
  119. }
  120. }