fetchAdminRole(); $user = User::query() ->where('status', 1) ->where('role_id', $admin_role->id) ->first(); if (!$user) { $this->markTestSkipped('没有可用的 administrator 用户,无法测试'); } $auth = make(AuthManager::class); return $auth->guard('jwt')->login($user); } protected function fetchAdminRole(): Role { return Role::query()->where('name', 'administrator')->firstOrFail(); } protected function adminHeaders(): array { return ['Authorization' => 'Bearer ' . $this->getAdminAuthToken()]; } protected function createTestUser(string $suffix, array $overrides = []): User { return User::query()->create(array_merge([ 'username' => 'admin_apikey_test_' . $suffix, 'password' => 'Pass_' . $suffix, 'email' => 'admin_apikey_test_' . $suffix . '@example.com', 'status' => 1, 'api_key_enabled' => true, ], $overrides)); } protected function getNonAdminToken(): array { $user = $this->createTestUser('nonadmin_' . uniqid()); $auth = make(AuthManager::class); $token = $auth->guard('jwt')->login($user); return ['Authorization' => 'Bearer ' . $token]; } public function test_admin_can_list_all_api_keys(): void { $user = $this->createTestUser('list_' . uniqid()); $result = ApiKey::generate($user->id, 'Test Key List'); $response = $this->get('/api/v1/admin/api-keys', [], $this->adminHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $body = json_decode($response->getBody()->getContents(), true); $this->assertArrayHasKey('items', $body['data']); $this->assertArrayHasKey('total', $body['data']); $this->assertArrayHasKey('page', $body['data']); $this->assertArrayHasKey('per_page', $body['data']); // 验证 user 关联信息 $items = $body['data']['items']; $found = false; foreach ($items as $item) { if ($item['id'] === $result['api_key']->id) { $found = true; $this->assertArrayHasKey('user', $item); $this->assertEquals($user->id, $item['user']['id']); $this->assertEquals($user->username, $item['user']['username']); $this->assertArrayHasKey('api_key_enabled', $item['user']); } } $this->assertTrue($found, '应在列表中找到刚创建的 Key'); $user->forceDelete(); } public function test_admin_list_does_not_expose_key_hash(): void { $user = $this->createTestUser('hash_' . uniqid()); ApiKey::generate($user->id, 'Hash Check Key'); $response = $this->get('/api/v1/admin/api-keys', ['user_id' => $user->id], $this->adminHeaders()); $response->assertStatus(200); $body = json_decode($response->getBody()->getContents(), true); foreach ($body['data']['items'] as $item) { $this->assertArrayNotHasKey('key_hash', $item, '响应不应包含 key_hash'); } $user->forceDelete(); } public function test_admin_list_filter_by_user_id(): void { $user = $this->createTestUser('filter_uid_' . uniqid()); ApiKey::generate($user->id, 'Filter User Key'); $response = $this->get('/api/v1/admin/api-keys', ['user_id' => $user->id], $this->adminHeaders()); $response->assertStatus(200); $body = json_decode($response->getBody()->getContents(), true); foreach ($body['data']['items'] as $item) { $this->assertEquals($user->id, $item['user_id']); } $user->forceDelete(); } public function test_admin_list_filter_by_enabled(): void { $user = $this->createTestUser('filter_en_' . uniqid()); $result = ApiKey::generate($user->id, 'Enabled Filter Key'); $result['api_key']->enabled = false; $result['api_key']->save(); $response = $this->get('/api/v1/admin/api-keys', ['user_id' => $user->id, 'enabled' => 0], $this->adminHeaders()); $response->assertStatus(200); $body = json_decode($response->getBody()->getContents(), true); $this->assertCount(1, $body['data']['items']); $this->assertFalse($body['data']['items'][0]['enabled']); $user->forceDelete(); } public function test_admin_list_pagination(): void { $response = $this->get('/api/v1/admin/api-keys', ['page' => 1, 'per_page' => 2], $this->adminHeaders()); $response->assertStatus(200); $body = json_decode($response->getBody()->getContents(), true); $this->assertEquals(1, $body['data']['page']); $this->assertEquals(2, $body['data']['per_page']); $this->assertLessThanOrEqual(2, count($body['data']['items'])); } public function test_admin_can_toggle_any_key(): void { $user = $this->createTestUser('toggle_' . uniqid()); $result = ApiKey::generate($user->id, 'Toggle Key'); $key_id = $result['api_key']->id; // 禁用 $response = $this->patch('/api/v1/admin/api-keys/' . $key_id . '/toggle', ['enabled' => false], $this->adminHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $body = json_decode($response->getBody()->getContents(), true); $this->assertFalse($body['data']['enabled']); // 重新启用 $response = $this->patch('/api/v1/admin/api-keys/' . $key_id . '/toggle', ['enabled' => true], $this->adminHeaders()); $response->assertStatus(200); $body = json_decode($response->getBody()->getContents(), true); $this->assertTrue($body['data']['enabled']); $user->forceDelete(); } public function test_admin_toggle_does_not_expose_key_hash(): void { $user = $this->createTestUser('toggle_hash_' . uniqid()); $result = ApiKey::generate($user->id, 'Toggle Hash Key'); $response = $this->patch('/api/v1/admin/api-keys/' . $result['api_key']->id . '/toggle', ['enabled' => false], $this->adminHeaders()); $response->assertStatus(200); $body = json_decode($response->getBody()->getContents(), true); $this->assertArrayNotHasKey('key_hash', $body['data'], '响应不应包含 key_hash'); $user->forceDelete(); } public function test_admin_can_delete_any_key(): void { $user = $this->createTestUser('delete_' . uniqid()); $result = ApiKey::generate($user->id, 'Delete Key'); $key_id = $result['api_key']->id; $response = $this->delete('/api/v1/admin/api-keys/' . $key_id, [], $this->adminHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $this->assertNull(ApiKey::query()->find($key_id)); $user->forceDelete(); } public function test_non_admin_cannot_access_admin_api_keys(): void { $headers = $this->getNonAdminToken(); $response = $this->get('/api/v1/admin/api-keys', [], $headers); $response->assertStatus(403); } public function test_toggle_nonexistent_key_returns_404(): void { $response = $this->patch('/api/v1/admin/api-keys/999999/toggle', ['enabled' => false], $this->adminHeaders()); $response->assertStatus(404); } }