|
|
@@ -0,0 +1,128 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Tests\Integration;
|
|
|
+
|
|
|
+use App\Services\MenuService;
|
|
|
+use Illuminate\Config\Repository;
|
|
|
+use Illuminate\Database\Capsule\Manager;
|
|
|
+use Illuminate\Database\Eloquent\Model;
|
|
|
+use Illuminate\Database\Schema\Blueprint;
|
|
|
+use Illuminate\Foundation\Application;
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
+use Illuminate\Support\Facades\Facade;
|
|
|
+use PHPUnit\Framework\TestCase;
|
|
|
+
|
|
|
+class MenuOrderTest extends TestCase
|
|
|
+{
|
|
|
+ protected function setUp(): void
|
|
|
+ {
|
|
|
+ Facade::clearResolvedInstances();
|
|
|
+ Model::clearBootedModels();
|
|
|
+ $app = new Application(dirname(__DIR__, 2));
|
|
|
+ $app->instance('config', new Repository(['app' => ['url' => 'https://admin.example.test']]));
|
|
|
+ Facade::setFacadeApplication($app);
|
|
|
+ $db = new Manager($app);
|
|
|
+ $db->addConnection(['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => 'bot_']);
|
|
|
+ $db->setAsGlobal();
|
|
|
+ $db->bootEloquent();
|
|
|
+ $app->instance('db', $db->getDatabaseManager());
|
|
|
+ $db->schema()->create('menus', function (Blueprint $t) {
|
|
|
+ $t->id();
|
|
|
+ $t->integer('parent_id')->default(0);
|
|
|
+ $t->string('title');
|
|
|
+ $t->integer('sort')->default(0);
|
|
|
+ $t->integer('status')->default(1);
|
|
|
+ $t->integer('type')->default(1);
|
|
|
+ $t->string('uri')->default('');
|
|
|
+ });
|
|
|
+ $db->schema()->create('roles', function (Blueprint $t) {
|
|
|
+ $t->id();
|
|
|
+ $t->string('name');
|
|
|
+ });
|
|
|
+ foreach (['role_user' => 'user_id', 'role_menu' => 'menu_id'] as $table => $field) {
|
|
|
+ $db->schema()->create($table, function (Blueprint $t) use ($field) {
|
|
|
+ $t->id();
|
|
|
+ $t->integer('role_id');
|
|
|
+ $t->integer($field);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function tearDown(): void
|
|
|
+ {
|
|
|
+ DB::disconnect();
|
|
|
+ Facade::clearResolvedInstances();
|
|
|
+ Model::clearBootedModels();
|
|
|
+ parent::tearDown();
|
|
|
+ }
|
|
|
+
|
|
|
+ private function menu(int $id, int $sort, int $parent = 0, int $status = 1, int $type = 1): void
|
|
|
+ {
|
|
|
+ DB::table('menus')->insert([
|
|
|
+ 'id' => $id, 'title' => 'Menu ' . $id, 'sort' => $sort,
|
|
|
+ 'parent_id' => $parent, 'status' => $status, 'type' => $type,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function role(int $id, array $menus, array $users): void
|
|
|
+ {
|
|
|
+ DB::table('roles')->insert(['id' => $id, 'name' => 'Role ' . $id]);
|
|
|
+ foreach ($menus as $menuId) {
|
|
|
+ DB::table('role_menu')->insert(['role_id' => $id, 'menu_id' => $menuId]);
|
|
|
+ }
|
|
|
+ foreach ($users as $userId) {
|
|
|
+ DB::table('role_user')->insert(['role_id' => $id, 'user_id' => $userId]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private function treeIds($tree): array
|
|
|
+ {
|
|
|
+ $result = [];
|
|
|
+ foreach ($tree as $node) {
|
|
|
+ $result[] = ['id' => (int) $node['id'], 'children' => $this->treeIds($node['children'])];
|
|
|
+ }
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_single_and_multiple_roles_follow_admin_order_for_roots_children_and_equal_sort(): void
|
|
|
+ {
|
|
|
+ foreach ([[10, 30], [20, 10], [30, 10]] as [$id, $sort]) {
|
|
|
+ $this->menu($id, $sort);
|
|
|
+ }
|
|
|
+ foreach ([[50, 30], [60, 5], [70, 5], [80, 20]] as [$id, $sort]) {
|
|
|
+ $this->menu($id, $sort, 20);
|
|
|
+ }
|
|
|
+ $this->role(1, [10, 30, 50, 70], [20]);
|
|
|
+ $this->role(2, [20, 30, 60, 70, 80], [20]);
|
|
|
+ $this->role(3, [70, 50, 30, 10, 80, 60, 20], [21]);
|
|
|
+
|
|
|
+ $expected = $this->treeIds(MenuService::getUserMenu(1));
|
|
|
+ $this->assertSame([20, 30, 10], array_column($expected, 'id'));
|
|
|
+ $this->assertSame([60, 70, 80, 50], array_column($expected[0]['children'], 'id'));
|
|
|
+ $this->assertSame($expected, $this->treeIds(MenuService::getUserMenu(20)));
|
|
|
+ $this->assertSame($expected, $this->treeIds(MenuService::getUserMenu(21)));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_sorting_preserves_role_permissions_existing_hidden_menus_and_menu_types(): void
|
|
|
+ {
|
|
|
+ $this->menu(10, 30);
|
|
|
+ $this->menu(20, 10);
|
|
|
+ $this->menu(30, 20, status: 2);
|
|
|
+ $this->menu(40, 20, type: 2);
|
|
|
+ $this->menu(50, 10, type: 2);
|
|
|
+ $this->menu(60, 0, type: 2);
|
|
|
+ $this->role(1, [10, 30, 40, 50], [20]);
|
|
|
+ $this->role(2, [20], [21]);
|
|
|
+
|
|
|
+ $this->assertSame([30, 10], array_column($this->treeIds(MenuService::getUserMenu(20)), 'id'));
|
|
|
+ $this->assertSame([20], array_column($this->treeIds(MenuService::getUserMenu(21)), 'id'));
|
|
|
+ $buttons = MenuService::getUserMenu(20, 2);
|
|
|
+ $this->assertSame([50, 40], array_column($buttons, 'id'));
|
|
|
+ $this->assertSame([], MenuService::getUserMenu(99));
|
|
|
+ $this->assertSame([], MenuService::getUserMenu(99, 2));
|
|
|
+
|
|
|
+ // Admin still uses its existing sort-only query and visible-menu filter.
|
|
|
+ $this->assertSame([20, 10], array_column($this->treeIds(MenuService::getUserMenu(1)), 'id'));
|
|
|
+ $this->assertSame([60, 50, 40], MenuService::getUserMenu(1, 2)->pluck('id')->all());
|
|
|
+ }
|
|
|
+}
|