0) { $callback(); return; } $exception = null; \Swoole\Coroutine\run(static function () use ($callback, &$exception): void { try { $callback(); } catch (\Throwable $e) { $exception = $e; } }); if ($exception) { throw $exception; } } // --- RouteGroup 模型测试 --- public function test_route_group_fillable(): void { $group = new RouteGroup(); $this->assertEqualsCanonicalizing( ['name', 'label', 'description', 'sort_order'], $group->getFillable() ); } public function test_route_group_table_name(): void { $group = new RouteGroup(); $this->assertSame('route_groups', $group->getTable()); } public function test_route_group_has_no_timestamps(): void { $group = new RouteGroup(); $this->assertFalse($group->timestamps); } // --- Route 模型测试 --- public function test_route_fillable(): void { $route = new Route(); $this->assertEqualsCanonicalizing( ['group_id', 'method', 'path', 'name', 'label'], $route->getFillable() ); } public function test_route_table_name(): void { $route = new Route(); $this->assertSame('routes', $route->getTable()); } public function test_route_has_no_timestamps(): void { $route = new Route(); $this->assertFalse($route->timestamps); } // --- RoleRouteOverride 模型测试 --- public function test_role_route_override_fillable(): void { $override = new RoleRouteOverride(); $this->assertEqualsCanonicalizing( ['role_id', 'route_id', 'allowed'], $override->getFillable() ); } public function test_role_route_override_table_name(): void { $override = new RoleRouteOverride(); $this->assertSame('role_route_overrides', $override->getTable()); } public function test_role_route_override_casts_allowed_to_boolean(): void { $override = new RoleRouteOverride(); $casts = $override->getCasts(); $this->assertSame('boolean', $casts['allowed']); } // --- 关联测试(需要数据库) --- public function test_route_group_has_many_routes(): void { $this->runInCoroutine(function (): void { Db::beginTransaction(); try { $group = RouteGroup::query()->create([ 'name' => 'test_group_' . uniqid(), 'label' => '测试分组', ]); Route::query()->create([ 'group_id' => $group->id, 'method' => 'GET', 'path' => '/api/v1/test-' . uniqid(), ]); Route::query()->create([ 'group_id' => $group->id, 'method' => 'POST', 'path' => '/api/v1/test-' . uniqid(), ]); $routes = $group->routes; $this->assertCount(2, $routes); } finally { Db::rollBack(); } }); } public function test_route_belongs_to_group(): void { $this->runInCoroutine(function (): void { Db::beginTransaction(); try { $group = RouteGroup::query()->create([ 'name' => 'test_group_' . uniqid(), 'label' => '测试分组', ]); $route = Route::query()->create([ 'group_id' => $group->id, 'method' => 'GET', 'path' => '/api/v1/test-' . uniqid(), ]); $this->assertNotNull($route->group); $this->assertSame($group->id, $route->group->id); } finally { Db::rollBack(); } }); } public function test_role_has_many_route_groups(): void { $this->runInCoroutine(function (): void { Db::beginTransaction(); try { $role = Role::query()->where('name', 'developer')->first(); $group1 = RouteGroup::query()->create([ 'name' => 'test_group_a_' . uniqid(), 'label' => '测试分组 A', ]); $group2 = RouteGroup::query()->create([ 'name' => 'test_group_b_' . uniqid(), 'label' => '测试分组 B', ]); $role->routeGroups()->attach([$group1->id, $group2->id]); // 重新加载关联 $role->load('routeGroups'); $this->assertCount(2, $role->routeGroups); $group_ids = $role->routeGroups->pluck('id')->toArray(); $this->assertContains($group1->id, $group_ids); $this->assertContains($group2->id, $group_ids); } finally { Db::rollBack(); } }); } public function test_role_route_override_query(): void { $this->runInCoroutine(function (): void { Db::beginTransaction(); try { $role = Role::query()->where('name', 'accessor')->first(); $route = Route::query()->create([ 'method' => 'DELETE', 'path' => '/api/v1/test-override-' . uniqid(), ]); RoleRouteOverride::query()->create([ 'role_id' => $role->id, 'route_id' => $route->id, 'allowed' => false, ]); // 查询角色的覆盖规则 $overrides = $role->routeOverrides; $this->assertGreaterThanOrEqual(1, $overrides->count()); $override = $overrides->where('route_id', $route->id)->first(); $this->assertNotNull($override); $this->assertFalse($override->allowed); // 查询路由的覆盖规则 $route_overrides = $route->overrides; $this->assertCount(1, $route_overrides); $this->assertSame($role->id, $route_overrides->first()->role_id); } finally { Db::rollBack(); } }); } public function test_route_method_path_unique_constraint(): void { $this->runInCoroutine(function (): void { Db::beginTransaction(); try { $unique_path = '/api/v1/unique-test-' . uniqid(); Route::query()->create([ 'method' => 'GET', 'path' => $unique_path, ]); $this->expectException(\Throwable::class); Route::query()->create([ 'method' => 'GET', 'path' => $unique_path, ]); } finally { Db::rollBack(); } }); } public function test_route_group_roles_inverse_relation(): void { $this->runInCoroutine(function (): void { Db::beginTransaction(); try { $role = Role::query()->where('name', 'developer')->first(); $group = RouteGroup::query()->create([ 'name' => 'test_inverse_' . uniqid(), 'label' => '反向关联测试', ]); $role->routeGroups()->attach($group->id); // 从 RouteGroup 查询关联的 Role $group->load('roles'); $this->assertCount(1, $group->roles); $this->assertSame($role->id, $group->roles->first()->id); } finally { Db::rollBack(); } }); } public function test_routes_synced_from_command(): void { $this->runInCoroutine(function (): void { // route:sync 已执行,验证 routes 表有数据 $count = Route::query()->count(); $this->assertGreaterThan(0, $count); // 验证已知路由存在 $route = Route::query() ->where('method', 'GET') ->where('path', '/api/v1/users') ->first(); $this->assertNotNull($route); $this->assertNotNull($route->name); }); } }