ResponseCallsTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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\Scribe;
  11. use Knuckles\Scribe\Tests\BaseLaravelTest;
  12. use Knuckles\Scribe\Tests\Fixtures\TestController;
  13. use Knuckles\Scribe\Tools\DocumentationConfig;
  14. use Illuminate\Support\Facades\Route as LaravelRouteFacade;
  15. use Symfony\Component\HttpFoundation\Request;
  16. class ResponseCallsTest extends BaseLaravelTest
  17. {
  18. protected function setUp(): void
  19. {
  20. parent::setUp();
  21. $this->setConfig(['database_connections_to_transact' => []]);
  22. }
  23. /** @test */
  24. public function can_call_route_and_fetch_response()
  25. {
  26. $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
  27. $rules = [
  28. 'response_calls' => [
  29. 'methods' => ['*'],
  30. ],
  31. ];
  32. $strategy = new ResponseCalls(new DocumentationConfig([]));
  33. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  34. $this->assertEquals(200, $results[0]['status']);
  35. $this->assertArraySubset([
  36. 'id' => 4,
  37. 'name' => 'banana',
  38. 'color' => 'red',
  39. 'weight' => '1 kg',
  40. 'delicious' => true,
  41. ], json_decode($results[0]['content'], true));
  42. }
  43. /** @test */
  44. public function can_upload_file_parameters_in_response_calls()
  45. {
  46. $route = RouteFacade::post('/withFormDataParams', [TestController::class, 'withFormDataParams']);
  47. $this->setConfig(['routes.0.apply.response_calls.methods' => ['POST']]);
  48. $parsed = (new Extractor())->processRoute($route, config('scribe.routes.0.apply'));
  49. $responses = $parsed->responses->toArray();
  50. $this->assertCount(1, $responses);
  51. $this->assertArraySubset([
  52. "status" => 200,
  53. "description" => null,
  54. "content" => '{"filename":"scribe.php","filepath":"config","name":"cat.jpg"}',
  55. ], $responses[0]);
  56. }
  57. /** @test */
  58. public function uses_configured_settings_when_calling_route()
  59. {
  60. $route = LaravelRouteFacade::post('/echo/{id}', [TestController::class, 'echoesRequestValues']);
  61. $rules = [
  62. 'response_calls' => [
  63. 'methods' => ['*'],
  64. 'queryParams' => [
  65. 'queryParam' => 'queryValue',
  66. ],
  67. 'bodyParams' => [
  68. 'bodyParam' => 'bodyValue',
  69. ],
  70. ],
  71. ];
  72. $endpointData = ExtractedEndpointData::fromRoute($route, [
  73. 'auth' => ['headers', 'Authorization', 'Bearer bearerToken'],
  74. 'headers' => [
  75. 'Content-Type' => 'application/json',
  76. 'Accept' => 'application/json',
  77. 'header' => 'headerValue',
  78. ],
  79. ]);
  80. $strategy = new ResponseCalls(new DocumentationConfig([]));
  81. $results = $strategy($endpointData,
  82. $this->convertRules($rules));
  83. $this->assertEquals(200, $results[0]['status']);
  84. $responseContent = json_decode($results[0]['content'], true);
  85. $this->assertEquals('queryValue', $responseContent['queryParam']);
  86. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  87. $this->assertEquals('headerValue', $responseContent['header']);
  88. $this->assertEquals('Bearer bearerToken', $responseContent['auth']);
  89. }
  90. /** @test */
  91. public function can_override_application_config_during_response_call()
  92. {
  93. $route = LaravelRouteFacade::post('/echoesConfig', [TestController::class, 'echoesConfig']);
  94. $rules = [
  95. 'response_calls' => [
  96. 'methods' => ['*'],
  97. ],
  98. ];
  99. $strategy = new ResponseCalls(new DocumentationConfig([]));
  100. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  101. $originalValue = json_decode($results[0]['content'], true)['app.env'];
  102. $now = time();
  103. $rules = [
  104. 'response_calls' => [
  105. 'methods' => ['*'],
  106. 'config' => [
  107. 'app.env' => $now,
  108. ],
  109. ],
  110. ];
  111. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  112. $newValue = json_decode($results[0]['content'], true)['app.env'];
  113. $this->assertEquals($now, $newValue);
  114. $this->assertNotEquals($originalValue, $newValue);
  115. }
  116. /** @test */
  117. public function calls_beforeResponseCall_hook()
  118. {
  119. Scribe::beforeResponseCall(function (Request $request, ExtractedEndpointData $endpointData) {
  120. $request->headers->set("header", "overridden_".$request->headers->get("header"));
  121. $request->headers->set("Authorization", "overridden_".$request->headers->get("Authorization"));
  122. $request->query->set("queryParam", "overridden_".$request->query->get("queryParam"));
  123. $request->request->set("bodyParam", "overridden_".$endpointData->uri.$request->request->get("bodyParam"));
  124. });
  125. $route = LaravelRouteFacade::post('/echo/{id}', [TestController::class, 'echoesRequestValues']);
  126. $rules = [
  127. 'response_calls' => [
  128. 'methods' => ['*'],
  129. 'queryParams' => [
  130. 'queryParam' => 'queryValue',
  131. ],
  132. 'bodyParams' => [
  133. 'bodyParam' => 'bodyValue',
  134. ],
  135. ],
  136. ];
  137. $endpointData = ExtractedEndpointData::fromRoute($route, [
  138. 'auth' => ['headers', 'Authorization', 'Bearer bearerToken'],
  139. 'headers' => [
  140. 'Content-Type' => 'application/json',
  141. 'Accept' => 'application/json',
  142. 'header' => 'headerValue',
  143. ],
  144. ]);
  145. $strategy = new ResponseCalls(new DocumentationConfig([]));
  146. $results = $strategy($endpointData, $this->convertRules($rules));
  147. $this->assertEquals(200, $results[0]['status']);
  148. $responseContent = json_decode($results[0]['content'], true);
  149. $this->assertEquals('overridden_queryValue', $responseContent['queryParam']);
  150. $this->assertEquals('overridden_headerValue', $responseContent['header']);
  151. $this->assertEquals('overridden_Bearer bearerToken', $responseContent['auth']);
  152. $this->assertEquals('overridden_echo/{id}bodyValue', $responseContent['bodyParam']);
  153. Scribe::beforeResponseCall(fn() => null);
  154. }
  155. /**
  156. * @test
  157. * @group dingo
  158. */
  159. public function can_call_route_and_fetch_response_with_dingo()
  160. {
  161. $route = $this->registerDingoRoute('post', '/shouldFetchRouteResponse', 'shouldFetchRouteResponse');
  162. $rules = [
  163. 'response_calls' => [
  164. 'methods' => ['*'],
  165. ],
  166. ];
  167. $strategy = new ResponseCalls(new DocumentationConfig());
  168. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  169. $this->assertEquals(200, $results[0]['status']);
  170. $this->assertArraySubset([
  171. 'id' => 4,
  172. 'name' => 'banana',
  173. 'color' => 'red',
  174. 'weight' => '1 kg',
  175. 'delicious' => true,
  176. ], json_decode($results[0]['content'], true));
  177. }
  178. /**
  179. * @test
  180. * @group dingo
  181. */
  182. public function uses_configured_settings_when_calling_route_with_dingo()
  183. {
  184. $route = $this->registerDingoRoute('post', '/echo/{id}', 'echoesRequestValues');
  185. $rules = [
  186. 'response_calls' => [
  187. 'methods' => ['*'],
  188. 'queryParams' => [
  189. 'queryParam' => 'queryValue',
  190. ],
  191. 'bodyParams' => [
  192. 'bodyParam' => 'bodyValue',
  193. ],
  194. ],
  195. ];
  196. $endpointData = ExtractedEndpointData::fromRoute($route, [
  197. 'headers' => [
  198. 'Content-Type' => 'application/json',
  199. 'Accept' => 'application/json',
  200. 'header' => 'headerValue',
  201. ],
  202. ]);
  203. $strategy = new ResponseCalls(new DocumentationConfig());
  204. $results = $strategy($endpointData, $this->convertRules($rules));
  205. $this->assertEquals(200, $results[0]['status']);
  206. $responseContent = json_decode($results[0]['content'], true);
  207. $this->assertEquals('queryValue', $responseContent['queryParam']);
  208. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  209. $this->assertEquals('headerValue', $responseContent['header']);
  210. }
  211. /**
  212. * @test
  213. * @group dingo
  214. */
  215. public function can_override_application_config_during_response_call_with_dingo()
  216. {
  217. $route = $this->registerDingoRoute('post', '/echoesConfig', 'echoesConfig');
  218. $rules = [
  219. 'response_calls' => [
  220. 'methods' => ['*'],
  221. ],
  222. ];
  223. $strategy = new ResponseCalls(new DocumentationConfig());
  224. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  225. $originalValue = json_decode($results[0]['content'], true)['app.env'];
  226. $now = time();
  227. $rules = [
  228. 'response_calls' => [
  229. 'methods' => ['*'],
  230. 'config' => [
  231. 'app.env' => $now,
  232. ],
  233. ],
  234. ];
  235. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  236. $newValue = json_decode($results[0]['content'], true)['app.env'];
  237. $this->assertEquals($now, $newValue);
  238. $this->assertNotEquals($originalValue, $newValue);
  239. }
  240. /** @test */
  241. public function does_not_make_response_call_if_success_response_already_gotten()
  242. {
  243. $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
  244. $rules = [
  245. 'response_calls' => [
  246. 'methods' => ['*'],
  247. ],
  248. ];
  249. $endpointData = ExtractedEndpointData::fromRoute($route, [
  250. 'responses' => new ResponseCollection([
  251. [
  252. 'status' => 200,
  253. 'content' => json_encode(['message' => 'LOL']),
  254. ],
  255. ]),
  256. ]);
  257. $strategy = new ResponseCalls(new DocumentationConfig([]));
  258. $results = $strategy($endpointData, $this->convertRules($rules));
  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. protected function convertRules(array $rules): mixed
  279. {
  280. return Extractor::transformOldRouteRulesIntoNewSettings('responses', $rules, ResponseCalls::class);
  281. }
  282. }