GeneratorTestCase.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. <?php
  2. /** @noinspection ALL */
  3. namespace Mpociot\ApiDoc\Tests\Unit;
  4. use Illuminate\Support\Arr;
  5. use Orchestra\Testbench\TestCase;
  6. use Mpociot\ApiDoc\Tools\Generator;
  7. use Mpociot\ApiDoc\Tests\Fixtures\TestUser;
  8. use Mpociot\ApiDoc\Tools\DocumentationConfig;
  9. use Mpociot\ApiDoc\Tests\Fixtures\TestController;
  10. use Mpociot\ApiDoc\ApiDocGeneratorServiceProvider;
  11. use Mpociot\ApiDoc\Tests\Fixtures\TestResourceController;
  12. abstract class GeneratorTestCase extends TestCase
  13. {
  14. /**
  15. * @var \Mpociot\ApiDoc\Tools\Generator
  16. */
  17. protected $generator;
  18. private $config = [
  19. 'strategies' => [
  20. 'metadata' => [
  21. \Mpociot\ApiDoc\Strategies\Metadata\GetFromDocBlocks::class,
  22. ],
  23. 'bodyParameters' => [
  24. \Mpociot\ApiDoc\Strategies\BodyParameters\GetFromBodyParamTag::class,
  25. ],
  26. 'queryParameters' => [
  27. \Mpociot\ApiDoc\Strategies\QueryParameters\GetFromQueryParamTag::class,
  28. ],
  29. 'responses' => [
  30. \Mpociot\ApiDoc\Strategies\Responses\UseResponseTag::class,
  31. \Mpociot\ApiDoc\Strategies\Responses\UseResponseFileTag::class,
  32. \Mpociot\ApiDoc\Strategies\Responses\UseApiResourceTags::class,
  33. \Mpociot\ApiDoc\Strategies\Responses\UseTransformerTags::class,
  34. \Mpociot\ApiDoc\Strategies\Responses\ResponseCalls::class,
  35. ],
  36. ],
  37. 'default_group' => 'general',
  38. ];
  39. public static $globalValue = null;
  40. protected function getPackageProviders($app)
  41. {
  42. return [
  43. ApiDocGeneratorServiceProvider::class,
  44. ];
  45. }
  46. /**
  47. * Setup the test environment.
  48. */
  49. public function setUp(): void
  50. {
  51. parent::setUp();
  52. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  53. $factory->define(TestUser::class, function () {
  54. return [
  55. 'id' => 4,
  56. 'first_name' => 'Tested',
  57. 'last_name' => 'Again',
  58. 'email' => 'a@b.com',
  59. ];
  60. });
  61. $this->generator = new Generator(new DocumentationConfig($this->config));
  62. }
  63. /** @test */
  64. public function can_parse_endpoint_description()
  65. {
  66. $route = $this->createRoute('GET', '/api/test', 'withEndpointDescription');
  67. $parsed = $this->generator->processRoute($route);
  68. $this->assertSame('Example title.', $parsed['title']);
  69. $this->assertSame("This will be the long description.\nIt can also be multiple lines long.", $parsed['description']);
  70. }
  71. /** @test */
  72. public function can_parse_body_parameters()
  73. {
  74. $route = $this->createRoute('GET', '/api/test', 'withBodyParameters');
  75. $bodyParameters = $this->generator->processRoute($route)['bodyParameters'];
  76. $this->assertArraySubset([
  77. 'user_id' => [
  78. 'type' => 'integer',
  79. 'required' => true,
  80. 'description' => 'The id of the user.',
  81. 'value' => 9,
  82. ],
  83. 'room_id' => [
  84. 'type' => 'string',
  85. 'required' => false,
  86. 'description' => 'The id of the room.',
  87. ],
  88. 'forever' => [
  89. 'type' => 'boolean',
  90. 'required' => false,
  91. 'description' => 'Whether to ban the user forever.',
  92. 'value' => false,
  93. ],
  94. 'another_one' => [
  95. 'type' => 'number',
  96. 'required' => false,
  97. 'description' => 'Just need something here.',
  98. ],
  99. 'yet_another_param' => [
  100. 'type' => 'object',
  101. 'required' => true,
  102. 'description' => 'Some object params.',
  103. ],
  104. 'yet_another_param.name' => [
  105. 'type' => 'string',
  106. 'description' => 'Subkey in the object param.',
  107. 'required' => true,
  108. ],
  109. 'even_more_param' => [
  110. 'type' => 'array',
  111. 'required' => false,
  112. 'description' => 'Some array params.',
  113. ],
  114. 'even_more_param.*' => [
  115. 'type' => 'float',
  116. 'description' => 'Subkey in the array param.',
  117. 'required' => false,
  118. ],
  119. 'book.name' => [
  120. 'type' => 'string',
  121. 'description' => '',
  122. 'required' => false,
  123. ],
  124. 'book.author_id' => [
  125. 'type' => 'integer',
  126. 'description' => '',
  127. 'required' => false,
  128. ],
  129. 'book[pages_count]' => [
  130. 'type' => 'integer',
  131. 'description' => '',
  132. 'required' => false,
  133. ],
  134. 'ids.*' => [
  135. 'type' => 'integer',
  136. 'description' => '',
  137. 'required' => false,
  138. ],
  139. 'users.*.first_name' => [
  140. 'type' => 'string',
  141. 'description' => 'The first name of the user.',
  142. 'required' => false,
  143. 'value' => 'John',
  144. ],
  145. 'users.*.last_name' => [
  146. 'type' => 'string',
  147. 'description' => 'The last name of the user.',
  148. 'required' => false,
  149. 'value' => 'Doe',
  150. ],
  151. ], $bodyParameters);
  152. }
  153. /** @test */
  154. public function it_ignores_non_commented_form_request()
  155. {
  156. $route = $this->createRoute('GET', '/api/test', 'withNonCommentedFormRequestParameter');
  157. $bodyParameters = $this->generator->processRoute($route)['bodyParameters'];
  158. $this->assertArraySubset([
  159. 'direct_one' => [
  160. 'type' => 'string',
  161. 'description' => 'Is found directly on the method.',
  162. ],
  163. ], $bodyParameters);
  164. }
  165. /** @test */
  166. public function can_parse_form_request_body_parameters()
  167. {
  168. $route = $this->createRoute('GET', '/api/test', 'withFormRequestParameter');
  169. $bodyParameters = $this->generator->processRoute($route)['bodyParameters'];
  170. $this->assertArraySubset([
  171. 'user_id' => [
  172. 'type' => 'integer',
  173. 'required' => true,
  174. 'description' => 'The id of the user.',
  175. 'value' => 9,
  176. ],
  177. 'room_id' => [
  178. 'type' => 'string',
  179. 'required' => false,
  180. 'description' => 'The id of the room.',
  181. ],
  182. 'forever' => [
  183. 'type' => 'boolean',
  184. 'required' => false,
  185. 'description' => 'Whether to ban the user forever.',
  186. 'value' => false,
  187. ],
  188. 'another_one' => [
  189. 'type' => 'number',
  190. 'required' => false,
  191. 'description' => 'Just need something here.',
  192. ],
  193. 'yet_another_param' => [
  194. 'type' => 'object',
  195. 'required' => true,
  196. 'description' => '',
  197. ],
  198. 'even_more_param' => [
  199. 'type' => 'array',
  200. 'required' => false,
  201. 'description' => '',
  202. ],
  203. ], $bodyParameters);
  204. }
  205. /** @test */
  206. public function can_parse_multiple_form_request_body_parameters()
  207. {
  208. $route = $this->createRoute('GET', '/api/test', 'withMultipleFormRequestParameters');
  209. $bodyParameters = $this->generator->processRoute($route)['bodyParameters'];
  210. $this->assertArraySubset([
  211. 'user_id' => [
  212. 'type' => 'integer',
  213. 'required' => true,
  214. 'description' => 'The id of the user.',
  215. 'value' => 9,
  216. ],
  217. 'room_id' => [
  218. 'type' => 'string',
  219. 'required' => false,
  220. 'description' => 'The id of the room.',
  221. ],
  222. 'forever' => [
  223. 'type' => 'boolean',
  224. 'required' => false,
  225. 'description' => 'Whether to ban the user forever.',
  226. 'value' => false,
  227. ],
  228. 'another_one' => [
  229. 'type' => 'number',
  230. 'required' => false,
  231. 'description' => 'Just need something here.',
  232. ],
  233. 'yet_another_param' => [
  234. 'type' => 'object',
  235. 'required' => true,
  236. 'description' => '',
  237. ],
  238. 'even_more_param' => [
  239. 'type' => 'array',
  240. 'required' => false,
  241. 'description' => '',
  242. ],
  243. ], $bodyParameters);
  244. }
  245. /** @test */
  246. public function can_parse_query_parameters()
  247. {
  248. $route = $this->createRoute('GET', '/api/test', 'withQueryParameters');
  249. $queryParameters = $this->generator->processRoute($route)['queryParameters'];
  250. $this->assertArraySubset([
  251. 'location_id' => [
  252. 'required' => true,
  253. 'description' => 'The id of the location.',
  254. ],
  255. 'user_id' => [
  256. 'required' => true,
  257. 'description' => 'The id of the user.',
  258. 'value' => 'me',
  259. ],
  260. 'page' => [
  261. 'required' => true,
  262. 'description' => 'The page number.',
  263. 'value' => '4',
  264. ],
  265. 'filters' => [
  266. 'required' => false,
  267. 'description' => 'The filters.',
  268. ],
  269. ], $queryParameters);
  270. }
  271. /** @test */
  272. public function it_does_not_generate_values_for_excluded_params_and_excludes_them_from_clean_params()
  273. {
  274. $route = $this->createRoute('GET', '/api/test', 'withExcludedExamples');
  275. $parsed = $this->generator->processRoute($route);
  276. $cleanBodyParameters = $parsed['cleanBodyParameters'];
  277. $cleanQueryParameters = $parsed['cleanQueryParameters'];
  278. $bodyParameters = $parsed['bodyParameters'];
  279. $queryParameters = $parsed['queryParameters'];
  280. $this->assertArrayHasKey('included', $cleanBodyParameters);
  281. $this->assertArrayNotHasKey('excluded_body_param', $cleanBodyParameters);
  282. $this->assertEmpty($cleanQueryParameters);
  283. $this->assertArraySubset([
  284. 'included' => [
  285. 'required' => true,
  286. 'type' => 'string',
  287. 'description' => 'Exists in examples.',
  288. ],
  289. 'excluded_body_param' => [
  290. 'type' => 'integer',
  291. 'description' => 'Does not exist in examples.',
  292. ],
  293. ], $bodyParameters);
  294. $this->assertArraySubset([
  295. 'excluded_query_param' => [
  296. 'description' => 'Does not exist in examples.',
  297. ],
  298. ], $queryParameters);
  299. }
  300. /** @test */
  301. public function can_parse_route_group()
  302. {
  303. $route = $this->createRoute('GET', '/api/test', 'dummy');
  304. $routeGroup = $this->generator->processRoute($route)['groupName'];
  305. $this->assertSame('Group A', $routeGroup);
  306. }
  307. /** @test */
  308. public function method_can_override_controller_group()
  309. {
  310. $route = $this->createRoute('GET', '/group/1', 'withGroupOverride');
  311. $parsedRoute = $this->generator->processRoute($route);
  312. $this->assertSame('Group B', $parsedRoute['groupName']);
  313. $this->assertSame('', $parsedRoute['groupDescription']);
  314. $route = $this->createRoute('GET', '/group/2', 'withGroupOverride2');
  315. $parsedRoute = $this->generator->processRoute($route);
  316. $this->assertSame('Group B', $parsedRoute['groupName']);
  317. $this->assertSame('', $parsedRoute['groupDescription']);
  318. $this->assertSame('This is also in Group B. No route description. Route title before gropp.', $parsedRoute['title']);
  319. $route = $this->createRoute('GET', '/group/3', 'withGroupOverride3');
  320. $parsedRoute = $this->generator->processRoute($route);
  321. $this->assertSame('Group B', $parsedRoute['groupName']);
  322. $this->assertSame('', $parsedRoute['groupDescription']);
  323. $this->assertSame('This is also in Group B. Route title after group.', $parsedRoute['title']);
  324. $route = $this->createRoute('GET', '/group/4', 'withGroupOverride4');
  325. $parsedRoute = $this->generator->processRoute($route);
  326. $this->assertSame('Group C', $parsedRoute['groupName']);
  327. $this->assertSame('Group description after group.', $parsedRoute['groupDescription']);
  328. $this->assertSame('This is in Group C. Route title before group.', $parsedRoute['title']);
  329. }
  330. /** @test */
  331. public function can_parse_auth_tags()
  332. {
  333. $route = $this->createRoute('GET', '/api/test', 'withAuthenticatedTag');
  334. $authenticated = $this->generator->processRoute($route)['authenticated'];
  335. $this->assertTrue($authenticated);
  336. $route = $this->createRoute('GET', '/api/test', 'dummy');
  337. $authenticated = $this->generator->processRoute($route)['authenticated'];
  338. $this->assertFalse($authenticated);
  339. }
  340. /** @test */
  341. public function can_parse_route_methods()
  342. {
  343. $route = $this->createRoute('GET', '/get', 'withEndpointDescription');
  344. $parsed = $this->generator->processRoute($route);
  345. $this->assertSame(['GET'], $parsed['methods']);
  346. $route = $this->createRoute('POST', '/post', 'withEndpointDescription');
  347. $parsed = $this->generator->processRoute($route);
  348. $this->assertSame(['POST'], $parsed['methods']);
  349. $route = $this->createRoute('PUT', '/put', 'withEndpointDescription');
  350. $parsed = $this->generator->processRoute($route);
  351. $this->assertSame(['PUT'], $parsed['methods']);
  352. $route = $this->createRoute('DELETE', '/delete', 'withEndpointDescription');
  353. $parsed = $this->generator->processRoute($route);
  354. $this->assertSame(['DELETE'], $parsed['methods']);
  355. }
  356. /** @test */
  357. public function can_parse_apiresource_tags()
  358. {
  359. $route = $this->createRoute('POST', '/withEloquentApiResource', 'withEloquentApiResource');
  360. $config = $this->config;
  361. $config['strategies']['responses'] = [\Mpociot\ApiDoc\Strategies\Responses\UseApiResourceTags::class];
  362. $generator = new Generator(new DocumentationConfig($config));
  363. $parsed = $this->generator->processRoute($route);
  364. $response = Arr::first($parsed['responses']);
  365. $this->assertTrue(is_array($parsed));
  366. $this->assertArrayHasKey('showresponse', $parsed);
  367. $this->assertTrue($parsed['showresponse']);
  368. $this->assertTrue(is_array($response));
  369. $this->assertEquals(200, $response['status']);
  370. $this->assertArraySubset([
  371. 'id' => 4,
  372. 'name' => 'Tested Again',
  373. 'email' => 'a@b.com',
  374. ], json_decode($response['content'], true));
  375. }
  376. /** @test */
  377. public function can_parse_apiresourcecollection_tags()
  378. {
  379. $route = $this->createRoute('POST', '/withEloquentApiResourceCollection', 'withEloquentApiResourceCollection');
  380. $config = $this->config;
  381. $config['strategies']['responses'] = [\Mpociot\ApiDoc\Strategies\Responses\UseApiResourceTags::class];
  382. $generator = new Generator(new DocumentationConfig($config));
  383. $parsed = $this->generator->processRoute($route);
  384. $response = Arr::first($parsed['responses']);
  385. $this->assertTrue(is_array($parsed));
  386. $this->assertArrayHasKey('showresponse', $parsed);
  387. $this->assertTrue($parsed['showresponse']);
  388. $this->assertTrue(is_array($response));
  389. $this->assertEquals(200, $response['status']);
  390. $content = json_decode($response['content'], true);
  391. $this->assertIsArray($content);
  392. $this->assertArraySubset([
  393. 'id' => 4,
  394. 'name' => 'Tested Again',
  395. 'email' => 'a@b.com',
  396. ], $content[0]);
  397. $this->assertArraySubset([
  398. 'id' => 4,
  399. 'name' => 'Tested Again',
  400. 'email' => 'a@b.com',
  401. ], $content[1]);
  402. }
  403. /** @test */
  404. public function can_parse_apiresourcecollection_tags_with_collection_class()
  405. {
  406. $route = $this->createRoute('POST', '/withEloquentApiResourceCollectionClass', 'withEloquentApiResourceCollectionClass');
  407. $config = $this->config;
  408. $config['strategies']['responses'] = [\Mpociot\ApiDoc\Strategies\Responses\UseApiResourceTags::class];
  409. $generator = new Generator(new DocumentationConfig($config));
  410. $parsed = $this->generator->processRoute($route);
  411. $response = Arr::first($parsed['responses']);
  412. $this->assertTrue(is_array($parsed));
  413. $this->assertArrayHasKey('showresponse', $parsed);
  414. $this->assertTrue($parsed['showresponse']);
  415. $this->assertTrue(is_array($response));
  416. $this->assertEquals(200, $response['status']);
  417. $content = json_decode($response['content'], true);
  418. $this->assertIsArray($content);
  419. $this->assertArraySubset([
  420. 'data' => [
  421. [
  422. 'id' => 4,
  423. 'name' => 'Tested Again',
  424. 'email' => 'a@b.com',
  425. ],
  426. [
  427. 'id' => 4,
  428. 'name' => 'Tested Again',
  429. 'email' => 'a@b.com',
  430. ],
  431. ],
  432. 'links' => [
  433. 'self' => 'link-value',
  434. ],
  435. ], $content);
  436. }
  437. /** @test */
  438. public function can_parse_response_tag()
  439. {
  440. $route = $this->createRoute('POST', '/responseTag', 'withResponseTag');
  441. $parsed = $this->generator->processRoute($route);
  442. $response = Arr::first($parsed['responses']);
  443. $this->assertTrue(is_array($parsed));
  444. $this->assertArrayHasKey('showresponse', $parsed);
  445. $this->assertTrue($parsed['showresponse']);
  446. $this->assertTrue(is_array($response));
  447. $this->assertEquals(200, $response['status']);
  448. $this->assertArraySubset([
  449. 'id' => 4,
  450. 'name' => 'banana',
  451. 'color' => 'red',
  452. 'weight' => '1 kg',
  453. 'delicious' => true,
  454. ], json_decode($response['content'], true));
  455. }
  456. /** @test */
  457. public function can_parse_response_tag_with_status_code()
  458. {
  459. $route = $this->createRoute('POST', '/responseTag', 'withResponseTagAndStatusCode');
  460. $parsed = $this->generator->processRoute($route);
  461. $response = Arr::first($parsed['responses']);
  462. $this->assertTrue(is_array($parsed));
  463. $this->assertArrayHasKey('showresponse', $parsed);
  464. $this->assertTrue($parsed['showresponse']);
  465. $this->assertTrue(is_array($response));
  466. $this->assertEquals(422, $response['status']);
  467. $this->assertArraySubset([
  468. 'message' => 'Validation error',
  469. ], json_decode($response['content'], true));
  470. }
  471. /** @test */
  472. public function can_parse_multiple_response_tags()
  473. {
  474. $route = $this->createRoute('POST', '/responseTag', 'withMultipleResponseTagsAndStatusCode');
  475. $parsed = $this->generator->processRoute($route);
  476. $this->assertTrue(is_array($parsed));
  477. $this->assertArrayHasKey('showresponse', $parsed);
  478. $this->assertTrue($parsed['showresponse']);
  479. $this->assertTrue(is_array($parsed['responses'][0]));
  480. $this->assertEquals(200, $parsed['responses'][0]['status']);
  481. $this->assertArraySubset([
  482. 'id' => 4,
  483. 'name' => 'banana',
  484. 'color' => 'red',
  485. 'weight' => '1 kg',
  486. 'delicious' => true,
  487. ], json_decode($parsed['responses'][0]['content'], true));
  488. $this->assertTrue(is_array($parsed['responses'][1]));
  489. $this->assertEquals(401, $parsed['responses'][1]['status']);
  490. $this->assertArraySubset([
  491. 'message' => 'Unauthorized',
  492. ], json_decode($parsed['responses'][1]['content'], true));
  493. }
  494. /**
  495. * @param $serializer
  496. * @param $expected
  497. *
  498. * @test
  499. * @dataProvider dataResources
  500. */
  501. public function can_parse_transformer_tag($serializer, $expected)
  502. {
  503. config(['apidoc.fractal.serializer' => $serializer]);
  504. $route = $this->createRoute('GET', '/transformerTag', 'transformerTag');
  505. $parsed = $this->generator->processRoute($route);
  506. $response = Arr::first($parsed['responses']);
  507. $this->assertTrue(is_array($parsed));
  508. $this->assertArrayHasKey('showresponse', $parsed);
  509. $this->assertTrue($parsed['showresponse']);
  510. $this->assertTrue(is_array($response));
  511. $this->assertEquals(200, $response['status']);
  512. $this->assertSame(
  513. $expected,
  514. $response['content']
  515. );
  516. }
  517. /** @test */
  518. public function can_parse_transformer_tag_with_model()
  519. {
  520. $route = $this->createRoute('GET', '/transformerTagWithModel', 'transformerTagWithModel');
  521. $parsed = $this->generator->processRoute($route);
  522. $response = Arr::first($parsed['responses']);
  523. $this->assertTrue(is_array($parsed));
  524. $this->assertArrayHasKey('showresponse', $parsed);
  525. $this->assertTrue($parsed['showresponse']);
  526. $this->assertTrue(is_array($response));
  527. $this->assertEquals(200, $response['status']);
  528. $this->assertSame(
  529. '{"data":{"id":1,"description":"Welcome on this test versions","name":"TestName"}}',
  530. $response['content']
  531. );
  532. }
  533. /** @test */
  534. public function can_parse_transformer_tag_with_status_code()
  535. {
  536. $route = $this->createRoute('GET', '/transformerTagWithStatusCode', 'transformerTagWithStatusCode');
  537. $parsed = $this->generator->processRoute($route);
  538. $response = Arr::first($parsed['responses']);
  539. $this->assertTrue(is_array($parsed));
  540. $this->assertArrayHasKey('showresponse', $parsed);
  541. $this->assertTrue($parsed['showresponse']);
  542. $this->assertTrue(is_array($response));
  543. $this->assertEquals(201, $response['status']);
  544. $this->assertSame(
  545. '{"data":{"id":1,"description":"Welcome on this test versions","name":"TestName"}}',
  546. $response['content']
  547. );
  548. }
  549. /** @test */
  550. public function can_parse_transformer_collection_tag()
  551. {
  552. $route = $this->createRoute('GET', '/transformerCollectionTag', 'transformerCollectionTag');
  553. $parsed = $this->generator->processRoute($route);
  554. $response = Arr::first($parsed['responses']);
  555. $this->assertTrue(is_array($parsed));
  556. $this->assertArrayHasKey('showresponse', $parsed);
  557. $this->assertTrue($parsed['showresponse']);
  558. $this->assertTrue(is_array($response));
  559. $this->assertEquals(200, $response['status']);
  560. $this->assertSame(
  561. $response['content'],
  562. '{"data":[{"id":1,"description":"Welcome on this test versions","name":"TestName"},'.
  563. '{"id":1,"description":"Welcome on this test versions","name":"TestName"}]}'
  564. );
  565. }
  566. /** @test */
  567. public function can_parse_transformer_collection_tag_with_model()
  568. {
  569. $route = $this->createRoute('GET', '/transformerCollectionTagWithModel', 'transformerCollectionTagWithModel');
  570. $parsed = $this->generator->processRoute($route);
  571. $response = Arr::first($parsed['responses']);
  572. $this->assertTrue(is_array($parsed));
  573. $this->assertArrayHasKey('showresponse', $parsed);
  574. $this->assertTrue($parsed['showresponse']);
  575. $this->assertTrue(is_array($response));
  576. $this->assertEquals(200, $response['status']);
  577. $this->assertSame(
  578. $response['content'],
  579. '{"data":[{"id":1,"description":"Welcome on this test versions","name":"TestName"},'.
  580. '{"id":1,"description":"Welcome on this test versions","name":"TestName"}]}'
  581. );
  582. }
  583. /** @test */
  584. public function can_call_route_and_generate_response()
  585. {
  586. $route = $this->createRoute('POST', '/shouldFetchRouteResponse', 'shouldFetchRouteResponse', true);
  587. $rules = [
  588. 'headers' => [
  589. 'Content-Type' => 'application/json',
  590. 'Accept' => 'application/json',
  591. ],
  592. 'response_calls' => [
  593. 'methods' => ['*'],
  594. ],
  595. ];
  596. $parsed = $this->generator->processRoute($route, $rules);
  597. $response = Arr::first($parsed['responses']);
  598. $this->assertTrue(is_array($parsed));
  599. $this->assertArrayHasKey('showresponse', $parsed);
  600. $this->assertTrue($parsed['showresponse']);
  601. $this->assertTrue(is_array($response));
  602. $this->assertEquals(200, $response['status']);
  603. $this->assertArraySubset([
  604. 'id' => 4,
  605. 'name' => 'banana',
  606. 'color' => 'red',
  607. 'weight' => '1 kg',
  608. 'delicious' => true,
  609. ], json_decode($response['content'], true));
  610. }
  611. /** @test */
  612. public function does_not_make_response_call_if_success_response_already_gotten()
  613. {
  614. $route = $this->createRoute('POST', '/withResponseTag', 'withResponseTag', true);
  615. $rules = [
  616. 'headers' => [
  617. 'Content-Type' => 'application/json',
  618. 'Accept' => 'application/json',
  619. ],
  620. 'response_calls' => [
  621. 'methods' => ['*'],
  622. ],
  623. ];
  624. $config = [
  625. 'strategies' => [
  626. 'responses' => [
  627. \Mpociot\ApiDoc\Strategies\Responses\UseResponseTag::class,
  628. \Mpociot\ApiDoc\Strategies\Responses\ResponseCalls::class,
  629. ],
  630. ],
  631. ];
  632. $generator = new Generator(new DocumentationConfig($config));
  633. $parsed = $generator->processRoute($route, $rules);
  634. $this->assertCount(1, $parsed['responses']);
  635. $response = Arr::first($parsed['responses']);
  636. $this->assertTrue(is_array($parsed));
  637. $this->assertArrayHasKey('showresponse', $parsed);
  638. $this->assertTrue($parsed['showresponse']);
  639. $this->assertTrue(is_array($response));
  640. $this->assertEquals(200, $response['status']);
  641. $this->assertArraySubset([
  642. 'id' => 4,
  643. 'name' => 'banana',
  644. 'color' => 'red',
  645. 'weight' => '1 kg',
  646. 'delicious' => true,
  647. 'responseTag' => true,
  648. ], json_decode($response['content'], true));
  649. // This may probably not be the best way to test this, but 🤷‍♀️
  650. $this->assertNull(static::$globalValue);
  651. }
  652. /** @test */
  653. public function can_override_config_during_response_call()
  654. {
  655. $route = $this->createRoute('POST', '/echoesConfig', 'echoesConfig', true);
  656. $rules = [
  657. 'response_calls' => [
  658. 'methods' => ['*'],
  659. ],
  660. ];
  661. $parsed = $this->generator->processRoute($route, $rules);
  662. $response = json_decode(Arr::first($parsed['responses'])['content'], true);
  663. $originalValue = $response['app.env'];
  664. $now = time();
  665. $rules = [
  666. 'response_calls' => [
  667. 'methods' => ['*'],
  668. 'config' => [
  669. 'app.env' => $now,
  670. ],
  671. ],
  672. ];
  673. $parsed = $this->generator->processRoute($route, $rules);
  674. $response = json_decode(Arr::first($parsed['responses'])['content'], true);
  675. $newValue = $response['app.env'];
  676. $this->assertEquals($now, $newValue);
  677. $this->assertNotEquals($originalValue, $newValue);
  678. }
  679. /** @test */
  680. public function can_override_url_path_parameters_with_urlparam_annotation()
  681. {
  682. $route = $this->createRoute('POST', '/echoesUrlParameters/{param}', 'echoesUrlParameters', true);
  683. $rules = [
  684. 'response_calls' => [
  685. 'methods' => ['*'],
  686. ],
  687. ];
  688. $parsed = $this->generator->processRoute($route, $rules);
  689. $response = json_decode(Arr::first($parsed['responses'])['content'], true);
  690. $this->assertEquals(4, $response['param']);
  691. }
  692. /** @test */
  693. public function ignores_or_inserts_optional_url_path_parameters_according_to_annotations()
  694. {
  695. $route = $this->createRoute('POST', '/echoesUrlParameters/{param}/{param2?}/{param3}/{param4?}', 'echoesUrlParameters', true);
  696. $rules = [
  697. 'response_calls' => [
  698. 'methods' => ['*'],
  699. ],
  700. ];
  701. $parsed = $this->generator->processRoute($route, $rules);
  702. $response = json_decode(Arr::first($parsed['responses'])['content'], true);
  703. $this->assertEquals(4, $response['param']);
  704. $this->assertNotNull($response['param2']);
  705. $this->assertEquals(1, $response['param3']);
  706. $this->assertNull($response['param4']);
  707. }
  708. /** @test */
  709. public function can_parse_response_file_tag()
  710. {
  711. // copy file to storage
  712. $filePath = __DIR__.'/../Fixtures/response_test.json';
  713. $fixtureFileJson = file_get_contents($filePath);
  714. copy($filePath, storage_path('response_test.json'));
  715. $route = $this->createRoute('GET', '/responseFileTag', 'responseFileTag');
  716. $parsed = $this->generator->processRoute($route);
  717. $response = Arr::first($parsed['responses']);
  718. $this->assertTrue(is_array($parsed));
  719. $this->assertArrayHasKey('showresponse', $parsed);
  720. $this->assertTrue($parsed['showresponse']);
  721. $this->assertTrue(is_array($response));
  722. $this->assertEquals(200, $response['status']);
  723. $this->assertSame(
  724. $response['content'],
  725. $fixtureFileJson
  726. );
  727. unlink(storage_path('response_test.json'));
  728. }
  729. /** @test */
  730. public function can_add_or_replace_key_value_pair_in_response_file()
  731. {
  732. // copy file to storage
  733. $filePath = __DIR__.'/../Fixtures/response_test.json';
  734. $fixtureFileJson = file_get_contents($filePath);
  735. copy($filePath, storage_path('response_test.json'));
  736. $route = $this->createRoute('GET', '/responseFileTagAndCustomJson', 'responseFileTagAndCustomJson');
  737. $parsed = $this->generator->processRoute($route);
  738. $response = Arr::first($parsed['responses']);
  739. $this->assertTrue(is_array($parsed));
  740. $this->assertArrayHasKey('showresponse', $parsed);
  741. $this->assertTrue($parsed['showresponse']);
  742. $this->assertTrue(is_array($response));
  743. $this->assertEquals(200, $response['status']);
  744. $this->assertNotSame(
  745. $response['content'],
  746. $fixtureFileJson
  747. );
  748. unlink(storage_path('response_test.json'));
  749. }
  750. /** @test */
  751. public function can_parse_multiple_response_file_tags_with_status_codes()
  752. {
  753. // copy file to storage
  754. $successFilePath = __DIR__.'/../Fixtures/response_test.json';
  755. $successFixtureFileJson = file_get_contents($successFilePath);
  756. copy($successFilePath, storage_path('response_test.json'));
  757. $errorFilePath = __DIR__.'/../Fixtures/response_error_test.json';
  758. $errorFixtureFileJson = file_get_contents($errorFilePath);
  759. copy($errorFilePath, storage_path('response_error_test.json'));
  760. $route = $this->createRoute('GET', '/responseFileTag', 'withResponseFileTagAndStatusCode');
  761. $parsed = $this->generator->processRoute($route);
  762. $this->assertTrue(is_array($parsed));
  763. $this->assertArrayHasKey('showresponse', $parsed);
  764. $this->assertTrue($parsed['showresponse']);
  765. $this->assertTrue(is_array($parsed['responses'][0]));
  766. $this->assertEquals(200, $parsed['responses'][0]['status']);
  767. $this->assertSame(
  768. $parsed['responses'][0]['content'],
  769. $successFixtureFileJson
  770. );
  771. $this->assertTrue(is_array($parsed['responses'][1]));
  772. $this->assertEquals(401, $parsed['responses'][1]['status']);
  773. $this->assertSame(
  774. $parsed['responses'][1]['content'],
  775. $errorFixtureFileJson
  776. );
  777. unlink(storage_path('response_test.json'));
  778. unlink(storage_path('response_error_test.json'));
  779. }
  780. /** @test */
  781. public function generates_consistent_examples_when_faker_seed_is_set()
  782. {
  783. $route = $this->createRoute('GET', '/withBodyParameters', 'withBodyParameters');
  784. $paramName = 'room_id';
  785. $results = [];
  786. $results[$this->generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  787. $results[$this->generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  788. $results[$this->generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  789. $results[$this->generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  790. $results[$this->generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  791. // Examples should have different values
  792. $this->assertNotEquals(count($results), 1);
  793. $generator = new Generator(new DocumentationConfig($this->config + ['faker_seed' => 12345]));
  794. $results = [];
  795. $results[$generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  796. $results[$generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  797. $results[$generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  798. $results[$generator->processRoute($route)['cleanBodyParameters'][$paramName]] = true;
  799. // Examples should have same values
  800. $this->assertEquals(count($results), 1);
  801. }
  802. /** @test */
  803. public function uses_configured_settings_when_calling_route()
  804. {
  805. $route = $this->createRoute('PUT', '/echo/{id}', 'shouldFetchRouteResponseWithEchoedSettings', true);
  806. $rules = [
  807. 'headers' => [
  808. 'Content-Type' => 'application/json',
  809. 'Accept' => 'application/json',
  810. 'header' => 'value',
  811. ],
  812. 'response_calls' => [
  813. 'methods' => ['*'],
  814. 'queryParams' => [
  815. 'queryParam' => 'queryValue',
  816. ],
  817. 'bodyParams' => [
  818. 'bodyParam' => 'bodyValue',
  819. ],
  820. ],
  821. ];
  822. $parsed = $this->generator->processRoute($route, $rules);
  823. $response = Arr::first($parsed['responses']);
  824. $this->assertTrue(is_array($parsed));
  825. $this->assertArrayHasKey('showresponse', $parsed);
  826. $this->assertTrue($parsed['showresponse']);
  827. $this->assertTrue(is_array($response));
  828. $this->assertEquals(200, $response['status']);
  829. $responseContent = json_decode($response['content'], true);
  830. $this->assertEquals('queryValue', $responseContent['queryParam']);
  831. $this->assertEquals('bodyValue', $responseContent['bodyParam']);
  832. $this->assertEquals('value', $responseContent['header']);
  833. }
  834. /** @test */
  835. public function can_use_arrays_in_routes_uses()
  836. {
  837. $route = $this->createRouteUsesArray('GET', '/api/array/test', 'withEndpointDescription');
  838. $parsed = $this->generator->processRoute($route);
  839. $this->assertSame('Example title.', $parsed['title']);
  840. $this->assertSame("This will be the long description.\nIt can also be multiple lines long.", $parsed['description']);
  841. }
  842. abstract public function createRoute(string $httpMethod, string $path, string $controllerMethod, $register = false, $class = TestController::class);
  843. abstract public function createRouteUsesArray(string $httpMethod, string $path, string $controllerMethod, $register = false, $class = TestController::class);
  844. public function dataResources()
  845. {
  846. return [
  847. [
  848. null,
  849. '{"data":{"id":1,"description":"Welcome on this test versions","name":"TestName"}}',
  850. ],
  851. [
  852. 'League\Fractal\Serializer\JsonApiSerializer',
  853. '{"data":{"type":null,"id":"1","attributes":{"description":"Welcome on this test versions","name":"TestName"}}}',
  854. ],
  855. ];
  856. }
  857. }