runInCoroutine(static function (): User { return User::query()->create([ 'username' => 'route_sync_test_nonadmin_' . uniqid(), 'password' => 'TestPass123', 'email' => 'route_sync_nonadmin_' . uniqid() . '@example.com', 'status' => 1, ]); }); } return $this->runInCoroutine(function (): array { $auth = make(AuthManager::class); $token = $auth->guard('jwt')->login(self::$nonAdminUser); return ['Authorization' => 'Bearer ' . $token]; }); } public static function tearDownAfterClass(): void { if (self::$nonAdminUser !== null) { $user_id = self::$nonAdminUser->id; $cleanup = static function () use ($user_id): void { User::query()->where('id', $user_id)->delete(); }; if (\Swoole\Coroutine::getCid() > 0) { $cleanup(); } else { \Swoole\Coroutine\run($cleanup); } self::$nonAdminUser = null; } parent::tearDownAfterClass(); } // ========== 管理员成功 ========== public function test_admin_can_sync_routes(): void { $response = $this->post('/api/v1/routes/sync', [], $this->authHeaders()); $response->assertOk(); $body = $response->json(); $this->assertSame(0, $body['code']); $this->assertSame('同步成功', $body['message']); $this->assertIsArray($body['data']); $this->assertArrayHasKey('synced', $body['data']); $this->assertArrayHasKey('total', $body['data']); $this->assertIsInt($body['data']['synced']); $this->assertIsInt($body['data']['total']); $this->assertGreaterThan(0, $body['data']['synced']); $this->assertSame($body['data']['synced'], $body['data']['total']); } // ========== 非管理员 403 ========== public function test_non_admin_receives_403(): void { $response = $this->post('/api/v1/routes/sync', [], $this->getNonAdminHeaders()); $response->assertStatus(403); } // ========== 未认证 401 ========== public function test_unauthenticated_returns_401(): void { $response = $this->post('/api/v1/routes/sync'); $response->assertStatus(401); } // ========== 重复调用幂等 ========== public function test_repeated_sync_is_idempotent(): void { $response1 = $this->post('/api/v1/routes/sync', [], $this->authHeaders()); $response1->assertOk(); $data1 = $response1->json('data'); $response2 = $this->post('/api/v1/routes/sync', [], $this->authHeaders()); $response2->assertOk(); $data2 = $response2->json('data'); $this->assertSame($data1['synced'], $data2['synced']); $this->assertSame($data1['total'], $data2['total']); } }