ResponseCallsTest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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([
  48. 'routes.0.apply.response_calls' => [],
  49. 'strategies.responses' => [
  50. [ResponseCalls::class,
  51. ['only' => 'POST *']
  52. ],
  53. ]
  54. ]);
  55. $parsed = (new Extractor())->processRoute($route, config('scribe.routes.0.apply'));
  56. $responses = $parsed->responses->toArray();
  57. $this->assertCount(1, $responses);
  58. $this->assertArraySubset([
  59. "status" => 200,
  60. "description" => null,
  61. "content" => '{"filename":"scribe.php","filepath":"config","name":"cat.jpg"}',
  62. ], $responses[0]);
  63. }
  64. /** @test */
  65. public function uses_configured_settings_when_calling_route()
  66. {
  67. $route = LaravelRouteFacade::post('/echo/{id}', [TestController::class, 'echoesRequestValues']);
  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' => 'headerValue',
  85. ],
  86. ]);
  87. $strategy = new ResponseCalls(new DocumentationConfig([]));
  88. $results = $strategy($endpointData,
  89. $this->convertRules($rules));
  90. $this->assertEquals(200, $results[0]['status']);
  91. $responseContent = json_decode($results[0]['content'], true);
  92. $this->assertEquals('queryValue', $responseContent['queryParam']);
  93. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  94. $this->assertEquals('headerValue', $responseContent['header']);
  95. $this->assertEquals('Bearer bearerToken', $responseContent['auth']);
  96. }
  97. /** @test */
  98. public function can_override_application_config_during_response_call()
  99. {
  100. $route = LaravelRouteFacade::post('/echoesConfig', [TestController::class, 'echoesConfig']);
  101. $rules = [
  102. 'response_calls' => [
  103. 'methods' => ['*'],
  104. ],
  105. ];
  106. $strategy = new ResponseCalls(new DocumentationConfig([]));
  107. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  108. $originalValue = json_decode($results[0]['content'], true)['app.env'];
  109. $now = time();
  110. $rules = [
  111. 'response_calls' => [
  112. 'methods' => ['*'],
  113. 'config' => [
  114. 'app.env' => $now,
  115. ],
  116. ],
  117. ];
  118. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  119. $newValue = json_decode($results[0]['content'], true)['app.env'];
  120. $this->assertEquals($now, $newValue);
  121. $this->assertNotEquals($originalValue, $newValue);
  122. }
  123. /** @test */
  124. public function calls_beforeResponseCall_hook()
  125. {
  126. Scribe::beforeResponseCall(function (Request $request, ExtractedEndpointData $endpointData) {
  127. $request->headers->set("header", "overridden_".$request->headers->get("header"));
  128. $request->headers->set("Authorization", "overridden_".$request->headers->get("Authorization"));
  129. $request->query->set("queryParam", "overridden_".$request->query->get("queryParam"));
  130. $request->request->set("bodyParam", "overridden_".$endpointData->uri.$request->request->get("bodyParam"));
  131. });
  132. $route = LaravelRouteFacade::post('/echo/{id}', [TestController::class, 'echoesRequestValues']);
  133. $rules = [
  134. 'response_calls' => [
  135. 'methods' => ['*'],
  136. 'queryParams' => [
  137. 'queryParam' => 'queryValue',
  138. ],
  139. 'bodyParams' => [
  140. 'bodyParam' => 'bodyValue',
  141. ],
  142. ],
  143. ];
  144. $endpointData = ExtractedEndpointData::fromRoute($route, [
  145. 'auth' => ['headers', 'Authorization', 'Bearer bearerToken'],
  146. 'headers' => [
  147. 'Content-Type' => 'application/json',
  148. 'Accept' => 'application/json',
  149. 'header' => 'headerValue',
  150. ],
  151. ]);
  152. $strategy = new ResponseCalls(new DocumentationConfig([]));
  153. $results = $strategy($endpointData, $this->convertRules($rules));
  154. $this->assertEquals(200, $results[0]['status']);
  155. $responseContent = json_decode($results[0]['content'], true);
  156. $this->assertEquals('overridden_queryValue', $responseContent['queryParam']);
  157. $this->assertEquals('overridden_headerValue', $responseContent['header']);
  158. $this->assertEquals('overridden_Bearer bearerToken', $responseContent['auth']);
  159. $this->assertEquals('overridden_echo/{id}bodyValue', $responseContent['bodyParam']);
  160. Scribe::beforeResponseCall(fn() => null);
  161. }
  162. /**
  163. * @test
  164. * @group dingo
  165. */
  166. public function can_call_route_and_fetch_response_with_dingo()
  167. {
  168. $route = $this->registerDingoRoute('post', '/shouldFetchRouteResponse', 'shouldFetchRouteResponse');
  169. $rules = [
  170. 'response_calls' => [
  171. 'methods' => ['*'],
  172. ],
  173. ];
  174. $strategy = new ResponseCalls(new DocumentationConfig());
  175. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  176. $this->assertEquals(200, $results[0]['status']);
  177. $this->assertArraySubset([
  178. 'id' => 4,
  179. 'name' => 'banana',
  180. 'color' => 'red',
  181. 'weight' => '1 kg',
  182. 'delicious' => true,
  183. ], json_decode($results[0]['content'], true));
  184. }
  185. /**
  186. * @test
  187. * @group dingo
  188. */
  189. public function uses_configured_settings_when_calling_route_with_dingo()
  190. {
  191. $route = $this->registerDingoRoute('post', '/echo/{id}', 'echoesRequestValues');
  192. $rules = [
  193. 'response_calls' => [
  194. 'methods' => ['*'],
  195. 'queryParams' => [
  196. 'queryParam' => 'queryValue',
  197. ],
  198. 'bodyParams' => [
  199. 'bodyParam' => 'bodyValue',
  200. ],
  201. ],
  202. ];
  203. $endpointData = ExtractedEndpointData::fromRoute($route, [
  204. 'headers' => [
  205. 'Content-Type' => 'application/json',
  206. 'Accept' => 'application/json',
  207. 'header' => 'headerValue',
  208. ],
  209. ]);
  210. $strategy = new ResponseCalls(new DocumentationConfig());
  211. $results = $strategy($endpointData, $this->convertRules($rules));
  212. $this->assertEquals(200, $results[0]['status']);
  213. $responseContent = json_decode($results[0]['content'], true);
  214. $this->assertEquals('queryValue', $responseContent['queryParam']);
  215. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  216. $this->assertEquals('headerValue', $responseContent['header']);
  217. }
  218. /**
  219. * @test
  220. * @group dingo
  221. */
  222. public function can_override_application_config_during_response_call_with_dingo()
  223. {
  224. $route = $this->registerDingoRoute('post', '/echoesConfig', 'echoesConfig');
  225. $rules = [
  226. 'response_calls' => [
  227. 'methods' => ['*'],
  228. ],
  229. ];
  230. $strategy = new ResponseCalls(new DocumentationConfig());
  231. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  232. $originalValue = json_decode($results[0]['content'], true)['app.env'];
  233. $now = time();
  234. $rules = [
  235. 'response_calls' => [
  236. 'methods' => ['*'],
  237. 'config' => [
  238. 'app.env' => $now,
  239. ],
  240. ],
  241. ];
  242. $results = $strategy(ExtractedEndpointData::fromRoute($route), $this->convertRules($rules));
  243. $newValue = json_decode($results[0]['content'], true)['app.env'];
  244. $this->assertEquals($now, $newValue);
  245. $this->assertNotEquals($originalValue, $newValue);
  246. }
  247. /** @test */
  248. public function does_not_make_response_call_if_success_response_already_gotten()
  249. {
  250. $route = LaravelRouteFacade::post('/shouldFetchRouteResponse', [TestController::class, 'shouldFetchRouteResponse']);
  251. $rules = [
  252. 'response_calls' => [
  253. 'methods' => ['*'],
  254. ],
  255. ];
  256. $endpointData = ExtractedEndpointData::fromRoute($route, [
  257. 'responses' => new ResponseCollection([
  258. [
  259. 'status' => 200,
  260. 'content' => json_encode(['message' => 'LOL']),
  261. ],
  262. ]),
  263. ]);
  264. $strategy = new ResponseCalls(new DocumentationConfig([]));
  265. $results = $strategy($endpointData, $this->convertRules($rules));
  266. $this->assertNull($results);
  267. }
  268. public function registerDingoRoute(string $httpMethod, string $path, string $controllerMethod)
  269. {
  270. $desiredRoute = null;
  271. /** @var Router $api */
  272. $api = app(Router::class);
  273. $api->version('v1', function (Router $api) use ($controllerMethod, $path, $httpMethod, &$desiredRoute) {
  274. $desiredRoute = $api->$httpMethod($path, [TestController::class, $controllerMethod]);
  275. });
  276. $routes = app(\Dingo\Api\Routing\Router::class)->getRoutes('v1');
  277. /*
  278. * Doing this bc we want an instance of Dingo\Api\Routing\Route, not Illuminate\Routing\Route, which the method above returns
  279. */
  280. return collect($routes)
  281. ->first(function (Route $route) use ($desiredRoute) {
  282. return $route->uri() === $desiredRoute->uri();
  283. });
  284. }
  285. protected function convertRules(array $rules): mixed
  286. {
  287. return Extractor::transformOldRouteRulesIntoNewSettings('responses', $rules, ResponseCalls::class);
  288. }
  289. }