RouteMatcherTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. <?php
  2. namespace Mpociot\ApiDoc\Tests\Unit;
  3. use Illuminate\Support\Str;
  4. use Dingo\Api\Routing\Router;
  5. use Orchestra\Testbench\TestCase;
  6. use Mpociot\ApiDoc\Tools\RouteMatcher;
  7. use Illuminate\Support\Facades\Route as RouteFacade;
  8. class RouteMatcherTest extends TestCase
  9. {
  10. /**
  11. * @var RouteMatcher
  12. */
  13. private $matcher;
  14. protected function setUp(): void
  15. {
  16. parent::setUp();
  17. $this->matcher = new RouteMatcher();
  18. }
  19. protected function getPackageProviders($app)
  20. {
  21. return [
  22. \Dingo\Api\Provider\LaravelServiceProvider::class,
  23. ];
  24. }
  25. public function testRespectsDomainsRuleForLaravelRouter()
  26. {
  27. $this->registerLaravelRoutes();
  28. $routeRules[0]['match']['prefixes'] = ['*'];
  29. $routeRules[0]['match']['domains'] = ['*'];
  30. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  31. $this->assertCount(12, $routes);
  32. $routeRules[0]['match']['domains'] = ['domain1.*', 'domain2.*'];
  33. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  34. $this->assertCount(12, $routes);
  35. $routeRules[0]['match']['domains'] = ['domain1.*'];
  36. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  37. $this->assertCount(6, $routes);
  38. foreach ($routes as $route) {
  39. $this->assertContains('domain1', $route['route']->getDomain());
  40. }
  41. $routeRules[0]['match']['domains'] = ['domain2.*'];
  42. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  43. $this->assertCount(6, $routes);
  44. foreach ($routes as $route) {
  45. $this->assertContains('domain2', $route['route']->getDomain());
  46. }
  47. }
  48. public function testRespectsDomainsRuleForDingoRouter()
  49. {
  50. $this->registerDingoRoutes();
  51. $routeRules[0]['match']['versions'] = ['v1'];
  52. $routeRules[0]['match']['prefixes'] = ['*'];
  53. $routeRules[0]['match']['domains'] = ['*'];
  54. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  55. $this->assertCount(12, $routes);
  56. $routeRules[0]['match']['domains'] = ['domain1.*', 'domain2.*'];
  57. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  58. $this->assertCount(12, $routes);
  59. $routeRules[0]['match']['domains'] = ['domain1.*'];
  60. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  61. $this->assertCount(6, $routes);
  62. foreach ($routes as $route) {
  63. $this->assertContains('domain1', $route['route']->getDomain());
  64. }
  65. $routeRules[0]['match']['domains'] = ['domain2.*'];
  66. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  67. $this->assertCount(6, $routes);
  68. foreach ($routes as $route) {
  69. $this->assertContains('domain2', $route['route']->getDomain());
  70. }
  71. }
  72. public function testRespectsPrefixesRuleForLaravelRouter()
  73. {
  74. $this->registerLaravelRoutes();
  75. $routeRules[0]['match']['domains'] = ['*'];
  76. $routeRules[0]['match']['prefixes'] = ['*'];
  77. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  78. $this->assertCount(12, $routes);
  79. $routeRules[0]['match']['prefixes'] = ['prefix1/*', 'prefix2/*'];
  80. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  81. $this->assertCount(8, $routes);
  82. $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
  83. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  84. $this->assertCount(4, $routes);
  85. foreach ($routes as $route) {
  86. $this->assertTrue(Str::is('prefix1/*', $route['route']->uri()));
  87. }
  88. $routeRules[0]['match']['prefixes'] = ['prefix2/*'];
  89. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  90. $this->assertCount(4, $routes);
  91. foreach ($routes as $route) {
  92. $this->assertTrue(Str::is('prefix2/*', $route['route']->uri()));
  93. }
  94. }
  95. public function testRespectsPrefixesRuleForDingoRouter()
  96. {
  97. $this->registerDingoRoutes();
  98. $routeRules[0]['match']['versions'] = ['v1'];
  99. $routeRules[0]['match']['domains'] = ['*'];
  100. $routeRules[0]['match']['prefixes'] = ['*'];
  101. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  102. $this->assertCount(12, $routes);
  103. $routeRules[0]['match']['prefixes'] = ['prefix1/*', 'prefix2/*'];
  104. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  105. $this->assertCount(8, $routes);
  106. $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
  107. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  108. $this->assertCount(4, $routes);
  109. foreach ($routes as $route) {
  110. $this->assertTrue(Str::is('prefix1/*', $route['route']->uri()));
  111. }
  112. $routeRules[0]['match']['prefixes'] = ['prefix2/*'];
  113. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  114. $this->assertCount(4, $routes);
  115. foreach ($routes as $route) {
  116. $this->assertTrue(Str::is('prefix2/*', $route['route']->uri()));
  117. }
  118. }
  119. public function testRespectsVersionsRuleForDingoRouter()
  120. {
  121. $this->registerDingoRoutes();
  122. $routeRules[0]['match']['versions'] = ['v2'];
  123. $routeRules[0]['match']['domains'] = ['*'];
  124. $routeRules[0]['match']['prefixes'] = ['*'];
  125. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  126. $this->assertCount(6, $routes);
  127. foreach ($routes as $route) {
  128. $this->assertNotEmpty(array_intersect($route['route']->versions(), ['v2']));
  129. }
  130. $routeRules[0]['match']['versions'] = ['v1', 'v2'];
  131. $routeRules[0]['match']['domains'] = ['*'];
  132. $routeRules[0]['match']['prefixes'] = ['*'];
  133. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  134. $this->assertCount(18, $routes);
  135. }
  136. public function testWillIncludeRouteIfListedExplicitlyForLaravelRouter()
  137. {
  138. $this->registerLaravelRoutes();
  139. $mustInclude = 'domain1-1';
  140. $routeRules[0]['include'] = [$mustInclude];
  141. $routeRules[0]['match']['domains'] = ['domain1.*'];
  142. $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
  143. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  144. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustInclude) {
  145. return $route['route']->getName() === $mustInclude;
  146. });
  147. $this->assertCount(1, $oddRuleOut);
  148. }
  149. public function testWillIncludeRouteIfListedExplicitlyForDingoRouter()
  150. {
  151. $this->registerDingoRoutes();
  152. $mustInclude = 'v2.domain2';
  153. $routeRules = [
  154. [
  155. 'match' => [
  156. 'domains' => ['domain1.*'],
  157. 'prefixes' => ['prefix1/*'],
  158. 'versions' => ['v1'],
  159. ],
  160. 'include' => [$mustInclude],
  161. ],
  162. ];
  163. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  164. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustInclude) {
  165. return $route['route']->getName() === $mustInclude;
  166. });
  167. $this->assertCount(1, $oddRuleOut);
  168. }
  169. public function testWillIncludeRouteIfMatchForAnIncludePatternForLaravelRouter()
  170. {
  171. $this->registerLaravelRoutes();
  172. $mustInclude = ['domain1-1', 'domain1-2'];
  173. $includePattern = 'domain1-*';
  174. $routeRules[0]['include'] = [$includePattern];
  175. $routeRules[0]['match']['domains'] = ['domain1.*'];
  176. $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
  177. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  178. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustInclude) {
  179. return in_array($route['route']->getName(), $mustInclude);
  180. });
  181. $this->assertCount(count($mustInclude), $oddRuleOut);
  182. }
  183. public function testWillIncludeRouteIfMatchForAnIncludePatternForDingoRouter()
  184. {
  185. $this->registerDingoRoutes();
  186. $mustInclude = ['v2.domain1', 'v2.domain2'];
  187. $includePattern = 'v2.domain*';
  188. $routeRules = [
  189. [
  190. 'match' => [
  191. 'domains' => ['domain1.*'],
  192. 'prefixes' => ['prefix1/*'],
  193. 'versions' => ['v1'],
  194. ],
  195. 'include' => [$includePattern],
  196. ],
  197. ];
  198. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  199. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustInclude) {
  200. return in_array($route['route']->getName(), $mustInclude);
  201. });
  202. $this->assertCount(count($mustInclude), $oddRuleOut);
  203. }
  204. public function testWillExcludeRouteIfListedExplicitlyForLaravelRouter()
  205. {
  206. $this->registerLaravelRoutes();
  207. $mustNotInclude = 'prefix1.domain1-1';
  208. $routeRules[0]['exclude'] = [$mustNotInclude];
  209. $routeRules[0]['match']['domains'] = ['domain1.*'];
  210. $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
  211. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  212. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustNotInclude) {
  213. return $route['route']->getName() === $mustNotInclude;
  214. });
  215. $this->assertCount(0, $oddRuleOut);
  216. }
  217. public function testWillExcludeRouteIfListedExplicitlyForDingoRouter()
  218. {
  219. $this->registerDingoRoutes();
  220. $mustNotInclude = 'v2.domain2';
  221. $routeRules = [
  222. [
  223. 'match' => [
  224. 'domains' => ['domain2.*'],
  225. 'prefixes' => ['*'],
  226. 'versions' => ['v2'],
  227. ],
  228. 'exclude' => [$mustNotInclude],
  229. ],
  230. ];
  231. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  232. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustNotInclude) {
  233. return $route['route']->getName() === $mustNotInclude;
  234. });
  235. $this->assertCount(0, $oddRuleOut);
  236. }
  237. public function testWillExcludeRouteIfMatchForAnExcludePatternForLaravelRouter()
  238. {
  239. $this->registerLaravelRoutes();
  240. $mustNotInclude = ['prefix1.domain1-1', 'prefix1.domain1-2'];
  241. $excludePattern = 'prefix1.domain1-*';
  242. $routeRules[0]['exclude'] = [$excludePattern];
  243. $routeRules[0]['match']['domains'] = ['domain1.*'];
  244. $routeRules[0]['match']['prefixes'] = ['prefix1/*'];
  245. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  246. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustNotInclude) {
  247. return in_array($route['route']->getName(), $mustNotInclude);
  248. });
  249. $this->assertCount(0, $oddRuleOut);
  250. }
  251. public function testWillExcludeRouteIfMatchForAnExcludePatterForDingoRouter()
  252. {
  253. $this->registerDingoRoutes();
  254. $mustNotInclude = ['v2.prefix1.domain2', 'v2.prefix2.domain2'];
  255. $excludePattern = 'v2.*.domain2';
  256. $routeRules = [
  257. [
  258. 'match' => [
  259. 'domains' => ['domain2.*'],
  260. 'prefixes' => ['*'],
  261. 'versions' => ['v2'],
  262. ],
  263. 'exclude' => [$excludePattern],
  264. ],
  265. ];
  266. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  267. $oddRuleOut = collect($routes)->filter(function ($route) use ($mustNotInclude) {
  268. return in_array($route['route']->getName(), $mustNotInclude);
  269. });
  270. $this->assertCount(0, $oddRuleOut);
  271. }
  272. public function testMergesRoutesFromDifferentRuleGroupsForLaravelRouter()
  273. {
  274. $this->registerLaravelRoutes();
  275. $routeRules = [
  276. [
  277. 'match' => [
  278. 'domains' => ['domain1.*'],
  279. 'prefixes' => ['prefix1/*'],
  280. ],
  281. ],
  282. [
  283. 'match' => [
  284. 'domains' => ['domain2.*'],
  285. 'prefixes' => ['prefix2*'],
  286. ],
  287. ],
  288. ];
  289. $routes = $this->matcher->getRoutesToBeDocumented($routeRules);
  290. $this->assertCount(4, $routes);
  291. $routes = collect($routes);
  292. $firstRuleGroup = $routes->filter(function ($route) {
  293. return Str::is('prefix1/*', $route['route']->uri())
  294. && Str::is('domain1.*', $route['route']->getDomain());
  295. });
  296. $this->assertCount(2, $firstRuleGroup);
  297. $secondRuleGroup = $routes->filter(function ($route) {
  298. return Str::is('prefix2/*', $route['route']->uri())
  299. && Str::is('domain2.*', $route['route']->getDomain());
  300. });
  301. $this->assertCount(2, $secondRuleGroup);
  302. }
  303. public function testMergesRoutesFromDifferentRuleGroupsForDingoRouter()
  304. {
  305. $this->registerDingoRoutes();
  306. $routeRules = [
  307. [
  308. 'match' => [
  309. 'domains' => ['*'],
  310. 'prefixes' => ['*'],
  311. 'versions' => ['v1'],
  312. ],
  313. ],
  314. [
  315. 'match' => [
  316. 'domains' => ['*'],
  317. 'prefixes' => ['*'],
  318. 'versions' => ['v2'],
  319. ],
  320. ],
  321. ];
  322. $routes = $this->matcher->getDingoRoutesToBeDocumented($routeRules);
  323. $this->assertCount(18, $routes);
  324. $routes = collect($routes);
  325. $firstRuleGroup = $routes->filter(function ($route) {
  326. return ! empty(array_intersect($route['route']->versions(), ['v1']));
  327. });
  328. $this->assertCount(12, $firstRuleGroup);
  329. $secondRuleGroup = $routes->filter(function ($route) {
  330. return ! empty(array_intersect($route['route']->versions(), ['v2']));
  331. });
  332. $this->assertCount(6, $secondRuleGroup);
  333. }
  334. private function registerLaravelRoutes()
  335. {
  336. RouteFacade::group(['domain' => 'domain1.app.test'], function () {
  337. RouteFacade::post('/domain1-1', function () {
  338. return 'hi';
  339. })->name('domain1-1');
  340. RouteFacade::get('domain1-2', function () {
  341. return 'hi';
  342. })->name('domain1-2');
  343. RouteFacade::get('/prefix1/domain1-1', function () {
  344. return 'hi';
  345. })->name('prefix1.domain1-1');
  346. RouteFacade::get('prefix1/domain1-2', function () {
  347. return 'hi';
  348. })->name('prefix1.domain1-2');
  349. RouteFacade::get('/prefix2/domain1-1', function () {
  350. return 'hi';
  351. })->name('prefix2.domain1-1');
  352. RouteFacade::get('prefix2/domain1-2', function () {
  353. return 'hi';
  354. })->name('prefix2.domain1-2');
  355. });
  356. RouteFacade::group(['domain' => 'domain2.app.test'], function () {
  357. RouteFacade::post('/domain2-1', function () {
  358. return 'hi';
  359. })->name('domain2-1');
  360. RouteFacade::get('domain2-2', function () {
  361. return 'hi';
  362. })->name('domain2-2');
  363. RouteFacade::get('/prefix1/domain2-1', function () {
  364. return 'hi';
  365. })->name('prefix1.domain2-1');
  366. RouteFacade::get('prefix1/domain2-2', function () {
  367. return 'hi';
  368. })->name('prefix1.domain2-2');
  369. RouteFacade::get('/prefix2/domain2-1', function () {
  370. return 'hi';
  371. })->name('prefix2.domain2-1');
  372. RouteFacade::get('prefix2/domain2-2', function () {
  373. return 'hi';
  374. })->name('prefix2.domain2-2');
  375. });
  376. }
  377. private function registerDingoRoutes()
  378. {
  379. $api = app('api.router');
  380. $api->version('v1', function (Router $api) {
  381. $api->group(['domain' => 'domain1.app.test'], function (Router $api) {
  382. $api->post('/domain1-1', function () {
  383. return 'hi';
  384. })->name('v1.domain1-1');
  385. $api->get('domain1-2', function () {
  386. return 'hi';
  387. })->name('v1.domain1-2');
  388. $api->get('/prefix1/domain1-1', function () {
  389. return 'hi';
  390. })->name('v1.prefix1.domain1-1');
  391. $api->get('prefix1/domain1-2', function () {
  392. return 'hi';
  393. })->name('v1.prefix1.domain1-2');
  394. $api->get('/prefix2/domain1-1', function () {
  395. return 'hi';
  396. })->name('v1.prefix2.domain1-1');
  397. $api->get('prefix2/domain1-2', function () {
  398. return 'hi';
  399. })->name('v1.prefix2.domain1-2');
  400. });
  401. $api->group(['domain' => 'domain2.app.test'], function (Router $api) {
  402. $api->post('/domain2-1', function () {
  403. return 'hi';
  404. })->name('v1.domain2-1');
  405. $api->get('domain2-2', function () {
  406. return 'hi';
  407. })->name('v1.domain2-2');
  408. $api->get('/prefix1/domain2-1', function () {
  409. return 'hi';
  410. })->name('v1.prefix1.domain2-1');
  411. $api->get('prefix1/domain2-2', function () {
  412. return 'hi';
  413. })->name('v1.prefix1.domain2-2');
  414. $api->get('/prefix2/domain2-1', function () {
  415. return 'hi';
  416. })->name('v1.prefix2.domain2-1');
  417. $api->get('prefix2/domain2-2', function () {
  418. return 'hi';
  419. })->name('v1.prefix2.domain2-2');
  420. });
  421. });
  422. $api->version('v2', function (Router $api) {
  423. $api->group(['domain' => 'domain1.app.test'], function (Router $api) {
  424. $api->post('/domain1', function () {
  425. return 'hi';
  426. })->name('v2.domain1');
  427. $api->get('/prefix1/domain1', function () {
  428. return 'hi';
  429. })->name('v2.prefix1.domain1');
  430. $api->get('/prefix2/domain1', function () {
  431. return 'hi';
  432. })->name('v2.prefix2.domain1');
  433. });
  434. $api->group(['domain' => 'domain2.app.test'], function (Router $api) {
  435. $api->post('/domain2', function () {
  436. return 'hi';
  437. })->name('v2.domain2');
  438. $api->get('/prefix1/domain2', function () {
  439. return 'hi';
  440. })->name('v2.prefix1.domain2');
  441. $api->get('/prefix2/domain2', function () {
  442. return 'hi';
  443. })->name('v2.prefix2.domain2');
  444. });
  445. });
  446. }
  447. }