UseResponseAttributesTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. <?php
  2. namespace Knuckles\Scribe\Tests\Strategies\Responses;
  3. use Illuminate\Database\Schema\Blueprint;
  4. use Illuminate\Foundation\Application;
  5. use Illuminate\Routing\Route;
  6. use Illuminate\Support\Facades\Schema;
  7. use Knuckles\Camel\Extraction\ExtractedEndpointData;
  8. use Knuckles\Scribe\Attributes\Response;
  9. use Knuckles\Scribe\Attributes\ResponseFromApiResource;
  10. use Knuckles\Scribe\Attributes\ResponseFromFile;
  11. use Knuckles\Scribe\Attributes\ResponseFromTransformer;
  12. use Knuckles\Scribe\Extracting\Strategies\Responses\UseResponseAttributes;
  13. use Knuckles\Scribe\ScribeServiceProvider;
  14. use Knuckles\Scribe\Tests\BaseLaravelTest;
  15. use Knuckles\Scribe\Tests\Fixtures\TestModel;
  16. use Knuckles\Scribe\Tests\Fixtures\TestPet;
  17. use Knuckles\Scribe\Tests\Fixtures\TestTransformer;
  18. use Knuckles\Scribe\Tests\Fixtures\TestUser;
  19. use Knuckles\Scribe\Tests\Fixtures\TestUserApiResource;
  20. use Knuckles\Scribe\Tools\DocumentationConfig;
  21. use Knuckles\Scribe\Tools\Utils;
  22. use League\Fractal\Pagination\IlluminatePaginatorAdapter;
  23. use ReflectionClass;
  24. class UseResponseAttributesTest extends BaseLaravelTest
  25. {
  26. protected function getPackageProviders($app)
  27. {
  28. $providers = parent::getPackageProviders($app);
  29. if (class_exists(\Illuminate\Database\Eloquent\LegacyFactoryServiceProvider::class)) {
  30. $providers[] = \Illuminate\Database\Eloquent\LegacyFactoryServiceProvider ::class;
  31. }
  32. return $providers;
  33. }
  34. public function setUp(): void
  35. {
  36. parent::setUp();
  37. $this->setConfig(['database_connections_to_transact' => []]);
  38. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  39. $factory->define(TestUser::class, function () {
  40. return [
  41. 'id' => 4,
  42. 'first_name' => 'Tested',
  43. 'last_name' => 'Again',
  44. 'email' => 'a@b.com',
  45. ];
  46. });
  47. $factory->state(TestUser::class, 'state1', ["state1" => true]);
  48. $factory->state(TestUser::class, 'random-state', ["random-state" => true]);
  49. $factory->define(TestPet::class, function () {
  50. return [
  51. 'id' => 1,
  52. 'name' => 'Mephistopheles',
  53. 'species' => 'dog',
  54. ];
  55. });
  56. }
  57. /** @test */
  58. public function can_parse_plain_response_attributes()
  59. {
  60. $results = $this->fetch($this->endpoint("plainResponseAttributes"));
  61. $this->assertArraySubset([
  62. [
  63. 'status' => 200,
  64. 'content' => json_encode(["all" => "good"]),
  65. "description" => "Success"
  66. ],
  67. [
  68. 'status' => 201,
  69. 'content' => json_encode(["all" => "good"]),
  70. ],
  71. [
  72. 'status' => 404,
  73. 'content' => null,
  74. ]
  75. ], $results);
  76. }
  77. /** @test */
  78. public function can_parse_responsefile_attributes()
  79. {
  80. $results = $this->fetch($this->endpoint("responseFileAttributes"));
  81. $this->assertArraySubset([
  82. [
  83. 'status' => 401,
  84. 'content' => json_encode(["message" => "Unauthorized", "merge" => "this"]),
  85. ],
  86. ], $results);
  87. }
  88. /** @test */
  89. public function can_parse_apiresource_attributes()
  90. {
  91. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  92. $factory->afterMaking(TestUser::class, function (TestUser $user, $faker) {
  93. if ($user->id === 4) {
  94. $child = Utils::getModelFactory(TestUser::class)->make(['id' => 5, 'parent_id' => 4]);
  95. $user->setRelation('children', collect([$child]));
  96. }
  97. });
  98. $results = $this->fetch($this->endpoint("apiResourceAttributes"));
  99. $this->assertArraySubset([
  100. [
  101. 'status' => 200,
  102. 'content' => json_encode([
  103. 'data' => [
  104. [
  105. 'id' => 4,
  106. 'name' => 'Tested Again',
  107. 'email' => 'a@b.com',
  108. 'children' => [
  109. [
  110. 'id' => 5,
  111. 'name' => 'Tested Again',
  112. 'email' => 'a@b.com',
  113. ],
  114. ],
  115. 'state1' => true,
  116. 'random-state' => true,
  117. ],
  118. ],
  119. 'links' => [
  120. "first" => '/?page=1',
  121. "last" => null,
  122. "prev" => null,
  123. "next" => '/?page=2',
  124. ],
  125. "meta" => [
  126. "current_page" => 1,
  127. "from" => 1,
  128. "path" => '/',
  129. "per_page" => 1,
  130. "to" => 1,
  131. ],
  132. "a" => "b",
  133. ]),
  134. ],
  135. ], $results);
  136. }
  137. /** @test */
  138. public function can_parse_apiresource_attributes_with_no_model_specified()
  139. {
  140. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  141. $factory->afterMaking(TestUser::class, function (TestUser $user, $faker) {
  142. if ($user->id === 4) {
  143. $child = Utils::getModelFactory(TestUser::class)->make(['id' => 5, 'parent_id' => 4]);
  144. $user->setRelation('children', collect([$child]));
  145. }
  146. });
  147. $results = $this->fetch($this->endpoint("apiResourceAttributesWithNoModel"));
  148. $this->assertArraySubset([
  149. [
  150. 'status' => 200,
  151. 'content' => json_encode([
  152. 'data' => [
  153. [
  154. 'id' => 4,
  155. 'name' => 'Tested Again',
  156. 'email' => 'a@b.com',
  157. 'children' => [
  158. [
  159. 'id' => 5,
  160. 'name' => 'Tested Again',
  161. 'email' => 'a@b.com',
  162. ],
  163. ],
  164. 'state1' => true,
  165. 'random-state' => true,
  166. ],
  167. ],
  168. 'links' => [
  169. "first" => '/?page=1',
  170. "last" => null,
  171. "prev" => null,
  172. "next" => '/?page=2',
  173. ],
  174. "meta" => [
  175. "current_page" => 1,
  176. "from" => 1,
  177. "path" => '/',
  178. "per_page" => 1,
  179. "to" => 1,
  180. ],
  181. "a" => "b",
  182. ]),
  183. ],
  184. ], $results);
  185. }
  186. /** @test */
  187. public function can_parse_transformer_attributes()
  188. {
  189. $results = $this->fetch($this->endpoint("transformerAttributes"));
  190. $this->assertArraySubset([
  191. [
  192. 'status' => 200,
  193. 'content' => json_encode([
  194. "data" => [
  195. [
  196. "id" => 1,
  197. "description" => "Welcome on this test versions",
  198. "name" => "TestName",
  199. ],
  200. ],
  201. 'meta' => [
  202. "pagination" => [
  203. "total" => 2,
  204. "count" => 1,
  205. "per_page" => 1,
  206. "current_page" => 1,
  207. "total_pages" => 2,
  208. "links" => ["next" => "/?page=2"],
  209. ],
  210. ],
  211. ]),
  212. ],
  213. ], $results);
  214. }
  215. /** @test */
  216. public function can_parse_apiresource_attributes_with_cursor_pagination()
  217. {
  218. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  219. $factory->afterMaking(TestUser::class, function (TestUser $user, $faker) {
  220. if ($user->id === 4) {
  221. $child = Utils::getModelFactory(TestUser::class)->make(['id' => 5, 'parent_id' => 4]);
  222. $user->setRelation('children', collect([$child]));
  223. }
  224. });
  225. $results = $this->fetch($this->endpoint("apiResourceAttributesWithCursorPaginate"));
  226. $nextCursor = base64_encode(json_encode(['_pointsToNextItems' => true]));
  227. $this->assertArraySubset([
  228. [
  229. 'status' => 200,
  230. 'content' => json_encode([
  231. 'data' => [
  232. [
  233. 'id' => 4,
  234. 'name' => 'Tested Again',
  235. 'email' => 'a@b.com',
  236. 'children' => [
  237. [
  238. 'id' => 5,
  239. 'name' => 'Tested Again',
  240. 'email' => 'a@b.com',
  241. ],
  242. ],
  243. ],
  244. ],
  245. 'links' => [
  246. "first" => null,
  247. "last" => null,
  248. "prev" => null,
  249. "next" => "/?cursor={$nextCursor}",
  250. ],
  251. "meta" => [
  252. "path" => '/',
  253. 'per_page' => 1,
  254. 'next_cursor' => $nextCursor,
  255. 'prev_cursor' => null,
  256. ]
  257. ]),
  258. ],
  259. ], $results);
  260. }
  261. /** @test */
  262. public function can_parse_apiresource_attributes_and_load_children_using_factory_create()
  263. {
  264. Schema::create('test_users', function (Blueprint $table) {
  265. $table->id();
  266. $table->string('first_name');
  267. $table->string('last_name');
  268. $table->string('email');
  269. $table->integer('parent_id')->nullable();
  270. });
  271. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  272. $factory->afterCreating(TestUser::class, function (TestUser $user, $faker) {
  273. if ($user->id === 4) {
  274. Utils::getModelFactory(TestUser::class)->create(['id' => 5, 'parent_id' => 4]);
  275. }
  276. });
  277. $documentationConfig = ['examples' => ['models_source' => ['factoryCreate']]];
  278. $results = $this->fetch($this->endpoint("apiResourceAttributesIncludeChildren"), $documentationConfig);
  279. $this->assertArraySubset([
  280. [
  281. 'status' => 200,
  282. 'content' => json_encode([
  283. "data" => [
  284. "id" => 4,
  285. "name" => "Tested Again",
  286. "email" => "a@b.com",
  287. "children" => [
  288. [
  289. "id" => 5,
  290. "name" => "Tested Again",
  291. "email" => "a@b.com",
  292. ]
  293. ],
  294. ],
  295. ]),
  296. ],
  297. ], $results);
  298. }
  299. /** @test */
  300. public function can_parse_apiresource_attributes_and_load_children_and_children_count_using_factory_create()
  301. {
  302. Schema::create('test_users', function (Blueprint $table) {
  303. $table->id();
  304. $table->string('first_name');
  305. $table->string('last_name');
  306. $table->string('email');
  307. $table->integer('parent_id')->nullable();
  308. });
  309. $factory = app(\Illuminate\Database\Eloquent\Factory::class);
  310. $factory->afterCreating(TestUser::class, function (TestUser $user, $faker) {
  311. if ($user->id === 4) {
  312. Utils::getModelFactory(TestUser::class)->create(['id' => 5, 'parent_id' => 4]);
  313. }
  314. });
  315. $documentationConfig = ['examples' => ['models_source' => ['factoryCreate']]];
  316. $results = $this->fetch($this->endpoint("apiResourceAttributesIncludeChildrenAndChildrenCount"), $documentationConfig);
  317. $this->assertArraySubset([
  318. [
  319. 'status' => 200,
  320. 'content' => json_encode([
  321. "data" => [
  322. "id" => 4,
  323. "name" => "Tested Again",
  324. "email" => "a@b.com",
  325. "children" => [
  326. [
  327. "id" => 5,
  328. "name" => "Tested Again",
  329. "email" => "a@b.com",
  330. ]
  331. ],
  332. 'children_count' => 1,
  333. ],
  334. ]),
  335. ],
  336. ], $results);
  337. }
  338. protected function fetch($endpoint, array $documentationConfig = []): array
  339. {
  340. $strategy = new UseResponseAttributes(new DocumentationConfig([]));
  341. return $strategy($endpoint, []);
  342. }
  343. protected function endpoint(string $method): ExtractedEndpointData
  344. {
  345. $endpoint = new class extends ExtractedEndpointData {
  346. public function __construct(array $parameters = []) {}
  347. };
  348. $endpoint->controller = new ReflectionClass(ResponseAttributesTestController::class);
  349. $endpoint->method = $endpoint->controller->getMethod($method);
  350. $endpoint->route = new Route(['POST'], "/somethingRandom", ['uses' => [ResponseAttributesTestController::class, $method]]);
  351. return $endpoint;
  352. }
  353. }
  354. class ResponseAttributesTestController
  355. {
  356. #[Response(["all" => "good"], 200, "Success")]
  357. #[Response('{"all":"good"}', 201)]
  358. #[Response(status: 404)]
  359. public function plainResponseAttributes()
  360. {
  361. }
  362. #[ResponseFromFile("tests/Fixtures/response_error_test.json", 401, ["merge" => "this"])]
  363. public function responseFileAttributes()
  364. {
  365. }
  366. #[ResponseFromApiResource(TestUserApiResource::class, TestUser::class, collection: true,
  367. factoryStates: ["state1", "random-state"], simplePaginate: 1, additional: ["a" => "b"])]
  368. public function apiResourceAttributes()
  369. {
  370. }
  371. #[ResponseFromApiResource(TestUserApiResource::class, collection: true,
  372. factoryStates: ["state1", "random-state"], simplePaginate: 1, additional: ["a" => "b"])]
  373. public function apiResourceAttributesWithNoModel()
  374. {
  375. }
  376. #[ResponseFromTransformer(TestTransformer::class, TestModel::class, collection: true,
  377. paginate: [IlluminatePaginatorAdapter::class, 1])]
  378. public function transformerAttributes()
  379. {
  380. }
  381. #[ResponseFromApiResource(TestUserApiResource::class, collection: true, cursorPaginate: 1)]
  382. public function apiResourceAttributesWithCursorPaginate()
  383. {
  384. }
  385. #[ResponseFromApiResource(TestUserApiResource::class, with: ['children'], withCount: ['children'])]
  386. public function apiResourceAttributesIncludeChildrenAndChildrenCount()
  387. {
  388. }
  389. #[ResponseFromApiResource(TestUserApiResource::class, with: ['children'])]
  390. public function apiResourceAttributesIncludeChildren()
  391. {
  392. }
  393. }