ResponseCallsTest.php 12 KB

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