ResponseCallsTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php
  2. namespace Knuckles\Scribe\Tests\Strategies\Responses;
  3. use Dingo\Api\Routing\Router;
  4. use Illuminate\Routing\Route;
  5. use Illuminate\Support\Facades\Route as RouteFacade;
  6. use Knuckles\Camel\Extraction\ExtractedEndpointData;
  7. use Knuckles\Camel\Extraction\ResponseCollection;
  8. use Knuckles\Scribe\Extracting\Extractor;
  9. use Knuckles\Scribe\Extracting\Strategies\Responses\ResponseCalls;
  10. use Knuckles\Scribe\ScribeServiceProvider;
  11. use Knuckles\Scribe\Tests\BaseLaravelTest;
  12. use Knuckles\Scribe\Tests\Fixtures\TestController;
  13. use Knuckles\Scribe\Tools\DocumentationConfig;
  14. use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
  15. use Illuminate\Support\Facades\Route as LaravelRouteFacade;
  16. class ResponseCallsTest extends BaseLaravelTest
  17. {
  18. use ArraySubsetAsserts;
  19. protected function setUp(): void
  20. {
  21. parent::setUp();
  22. config(['scribe.database_connections_to_transact' => []]);
  23. }
  24. /** @test */
  25. public function can_call_route_and_fetch_response()
  26. {
  27. $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
  28. $rules = [
  29. 'headers' => [
  30. 'Content-Type' => 'application/json',
  31. 'Accept' => 'application/json',
  32. ],
  33. 'response_calls' => [
  34. 'methods' => ['*'],
  35. ],
  36. ];
  37. $strategy = new ResponseCalls(new DocumentationConfig([]));
  38. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  39. ExtractedEndpointData::fromRoute($route), $rules
  40. );
  41. $this->assertEquals(200, $results[0]['status']);
  42. $this->assertArraySubset([
  43. 'id' => 4,
  44. 'name' => 'banana',
  45. 'color' => 'red',
  46. 'weight' => '1 kg',
  47. 'delicious' => true,
  48. ], json_decode($results[0]['content'], true));
  49. }
  50. /** @test */
  51. public function can_upload_file_parameters_in_response_calls()
  52. {
  53. $route = RouteFacade::post('/withFormDataParams', [TestController::class, 'withFormDataParams']);
  54. config(['scribe.routes.0.apply.response_calls.methods' => ['POST']]);
  55. $parsed = (new Extractor())->processRoute($route, config('scribe.routes.0.apply'));
  56. $this->assertEquals([
  57. [
  58. "status" => 200,
  59. "description" => null,
  60. "content" => '{"filename":"scribe.php","filepath":"config","name":"cat.jpg"}',
  61. ],
  62. ], $parsed->responses->toArray());
  63. }
  64. /** @test */
  65. public function uses_configured_settings_when_calling_route()
  66. {
  67. $route = LaravelRouteFacade::post('/echo/{id}', [TestController::class, 'shouldFetchRouteResponseWithEchoedSettings']);
  68. $rules = [
  69. 'response_calls' => [
  70. 'methods' => ['*'],
  71. 'queryParams' => [
  72. 'queryParam' => 'queryValue',
  73. ],
  74. 'bodyParams' => [
  75. 'bodyParam' => 'bodyValue',
  76. ],
  77. ],
  78. ];
  79. $endpointData = ExtractedEndpointData::fromRoute($route, [
  80. 'auth' => ['headers', 'Authorization', 'Bearer bearerToken'],
  81. 'headers' => [
  82. 'Content-Type' => 'application/json',
  83. 'Accept' => 'application/json',
  84. 'header' => 'value',
  85. ],
  86. ]);
  87. $strategy = new ResponseCalls(new DocumentationConfig([]));
  88. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses($endpointData, $rules);
  89. $this->assertEquals(200, $results[0]['status']);
  90. $responseContent = json_decode($results[0]['content'], true);
  91. $this->assertEquals('queryValue', $responseContent['queryParam']);
  92. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  93. $this->assertEquals('value', $responseContent['header']);
  94. $this->assertEquals('Bearer bearerToken', $responseContent['auth']);
  95. }
  96. /** @test */
  97. public function can_override_application_config_during_response_call()
  98. {
  99. $route = LaravelRouteFacade::post('/echoesConfig', [TestController::class, 'echoesConfig']);
  100. $rules = [
  101. 'response_calls' => [
  102. 'methods' => ['*'],
  103. ],
  104. ];
  105. $strategy = new ResponseCalls(new DocumentationConfig([]));
  106. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  107. ExtractedEndpointData::fromRoute($route), $rules
  108. );
  109. $originalValue = json_decode($results[0]['content'], true)['app.env'];
  110. $now = time();
  111. $rules = [
  112. 'response_calls' => [
  113. 'methods' => ['*'],
  114. 'config' => [
  115. 'app.env' => $now,
  116. ],
  117. ],
  118. ];
  119. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  120. ExtractedEndpointData::fromRoute($route), $rules
  121. );
  122. $newValue = json_decode($results[0]['content'], true)['app.env'];
  123. $this->assertEquals($now, $newValue);
  124. $this->assertNotEquals($originalValue, $newValue);
  125. }
  126. /**
  127. * @test
  128. * @group dingo
  129. */
  130. public function can_call_route_and_fetch_response_with_dingo()
  131. {
  132. $route = $this->registerDingoRoute('post', '/shouldFetchRouteResponse', 'shouldFetchRouteResponse');
  133. $rules = [
  134. 'headers' => [
  135. 'Content-Type' => 'application/json',
  136. 'Accept' => 'application/json',
  137. ],
  138. 'response_calls' => [
  139. 'methods' => ['*'],
  140. ],
  141. ];
  142. $strategy = new ResponseCalls(new DocumentationConfig(['router' => 'dingo']));
  143. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  144. ExtractedEndpointData::fromRoute($route), $rules
  145. );
  146. $this->assertEquals(200, $results[0]['status']);
  147. $this->assertArraySubset([
  148. 'id' => 4,
  149. 'name' => 'banana',
  150. 'color' => 'red',
  151. 'weight' => '1 kg',
  152. 'delicious' => true,
  153. ], json_decode($results[0]['content'], true));
  154. }
  155. /**
  156. * @test
  157. * @group dingo
  158. */
  159. public function uses_configured_settings_when_calling_route_with_dingo()
  160. {
  161. $route = $this->registerDingoRoute('post', '/echo/{id}', 'shouldFetchRouteResponseWithEchoedSettings');
  162. $rules = [
  163. 'response_calls' => [
  164. 'methods' => ['*'],
  165. 'queryParams' => [
  166. 'queryParam' => 'queryValue',
  167. ],
  168. 'bodyParams' => [
  169. 'bodyParam' => 'bodyValue',
  170. ],
  171. ],
  172. ];
  173. $endpointData = ExtractedEndpointData::fromRoute($route, [
  174. 'headers' => [
  175. 'Content-Type' => 'application/json',
  176. 'Accept' => 'application/json',
  177. 'header' => 'value',
  178. ],
  179. ]);
  180. $strategy = new ResponseCalls(new DocumentationConfig(['router' => 'dingo']));
  181. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses($endpointData, $rules);
  182. $this->assertEquals(200, $results[0]['status']);
  183. $responseContent = json_decode($results[0]['content'], true);
  184. $this->assertEquals('queryValue', $responseContent['queryParam']);
  185. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  186. $this->assertEquals('value', $responseContent['header']);
  187. }
  188. /**
  189. * @test
  190. * @group dingo
  191. */
  192. public function can_override_application_config_during_response_call_with_dingo()
  193. {
  194. $route = $this->registerDingoRoute('post', '/echoesConfig', 'echoesConfig');
  195. $rules = [
  196. 'response_calls' => [
  197. 'methods' => ['*'],
  198. ],
  199. ];
  200. $strategy = new ResponseCalls(new DocumentationConfig(['router' => 'dingo']));
  201. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  202. ExtractedEndpointData::fromRoute($route), $rules
  203. );
  204. $originalValue = json_decode($results[0]['content'], true)['app.env'];
  205. $now = time();
  206. $rules = [
  207. 'response_calls' => [
  208. 'methods' => ['*'],
  209. 'config' => [
  210. 'app.env' => $now,
  211. ],
  212. ],
  213. ];
  214. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  215. ExtractedEndpointData::fromRoute($route), $rules
  216. );
  217. $newValue = json_decode($results[0]['content'], true)['app.env'];
  218. $this->assertEquals($now, $newValue);
  219. $this->assertNotEquals($originalValue, $newValue);
  220. }
  221. /** @test */
  222. public function does_not_make_response_call_if_success_response_already_gotten()
  223. {
  224. $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
  225. $rules = [
  226. 'headers' => [
  227. 'Content-Type' => 'application/json',
  228. 'Accept' => 'application/json',
  229. ],
  230. 'response_calls' => [
  231. 'methods' => ['*'],
  232. ],
  233. ];
  234. $endpointData = ExtractedEndpointData::fromRoute($route, [
  235. 'responses' => new ResponseCollection([
  236. [
  237. 'status' => 200,
  238. 'content' => json_encode(['message' => 'LOL']),
  239. ],
  240. ]),
  241. ]);
  242. $strategy = new ResponseCalls(new DocumentationConfig([]));
  243. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses($endpointData, $rules);
  244. $this->assertNull($results);
  245. }
  246. /** @test */
  247. public function does_not_make_response_call_if_forbidden_by_config()
  248. {
  249. $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
  250. $rules = [
  251. 'response_calls' => [
  252. 'methods' => [],
  253. ],
  254. ];
  255. $strategy = new ResponseCalls(new DocumentationConfig([]));
  256. $results = $strategy->makeResponseCallIfEnabledAndNoSuccessResponses(
  257. ExtractedEndpointData::fromRoute($route), $rules
  258. );
  259. $this->assertNull($results);
  260. }
  261. public function registerDingoRoute(string $httpMethod, string $path, string $controllerMethod)
  262. {
  263. $desiredRoute = null;
  264. /** @var Router $api */
  265. $api = app(Router::class);
  266. $api->version('v1', function (Router $api) use ($controllerMethod, $path, $httpMethod, &$desiredRoute) {
  267. $desiredRoute = $api->$httpMethod($path, [TestController::class, $controllerMethod]);
  268. });
  269. $routes = app(\Dingo\Api\Routing\Router::class)->getRoutes('v1');
  270. /*
  271. * Doing this bc we want an instance of Dingo\Api\Routing\Route, not Illuminate\Routing\Route, which the method above returns
  272. */
  273. return collect($routes)
  274. ->first(function (Route $route) use ($desiredRoute) {
  275. return $route->uri() === $desiredRoute->uri();
  276. });
  277. }
  278. }