guard('jwt')->login($user); } protected function authHeaders(User $user): array { return ['Authorization' => 'Bearer ' . $this->getAuthToken($user)]; } protected function createTestUser(array $overrides = []): User { $suffix = bin2hex(random_bytes(4)); return User::query()->create(array_merge([ 'username' => 'apikey_int_' . $suffix, 'password' => 'Pass_' . $suffix, 'email' => 'apikey_int_' . $suffix . '@example.com', 'status' => 1, 'api_key_enabled' => true, ], $overrides)); } // ========== API Key CRUD ========== public function test_generate_api_key_success(): void { $user = $this->createTestUser(); $response = $this->post('/api/v1/me/api-keys', [ 'name' => 'IntTest Key', ], $this->authHeaders($user)); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonStructure([ 'data' => ['plain_key', 'api_key' => ['id', 'name', 'key_prefix', 'enabled']], ]); } public function test_generate_api_key_without_api_key_enabled_returns_403(): void { $user = $this->createTestUser(['api_key_enabled' => false]); $response = $this->post('/api/v1/me/api-keys', [ 'name' => 'Blocked Key', ], $this->authHeaders($user)); $response->assertStatus(403); $response->assertJsonPath('code', 403); } public function test_generate_api_key_missing_name_returns_400(): void { $user = $this->createTestUser(); $response = $this->post('/api/v1/me/api-keys', [], $this->authHeaders($user)); $response->assertStatus(400); $response->assertJsonPath('code', 400); } public function test_generate_api_key_without_auth_returns_401(): void { $response = $this->post('/api/v1/me/api-keys', [ 'name' => 'No Auth Key', ]); $response->assertStatus(401); } public function test_list_api_keys(): void { $user = $this->createTestUser(); // 先创建一个 key $this->post('/api/v1/me/api-keys', [ 'name' => 'List Test Key', ], $this->authHeaders($user)); $response = $this->get('/api/v1/me/api-keys', [], $this->authHeaders($user)); $response->assertStatus(200); $response->assertJsonPath('code', 0); } public function test_delete_api_key_success(): void { $user = $this->createTestUser(); // 创建一个 key $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'Delete Test Key', ], $this->authHeaders($user)); $key_id = $create_response->json('data.api_key.id'); $response = $this->delete('/api/v1/me/api-keys/' . $key_id, [], $this->authHeaders($user)); $response->assertStatus(200); $response->assertJsonPath('code', 0); } public function test_delete_other_users_api_key_returns_404(): void { $user_a = $this->createTestUser(); $user_b = $this->createTestUser(); // user_a 创建 key $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'UserA Key', ], $this->authHeaders($user_a)); $key_id = $create_response->json('data.api_key.id'); // user_b 尝试删除 $response = $this->delete('/api/v1/me/api-keys/' . $key_id, [], $this->authHeaders($user_b)); $response->assertStatus(404); $response->assertJsonPath('code', 404); } // ========== API Key 认证 ========== public function test_api_key_auth_access_me_endpoint(): void { $user = $this->createTestUser(); // 通过 JWT 创建 API Key $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'Auth Test Key', ], $this->authHeaders($user)); $plain_key = $create_response->json('data.plain_key'); // 使用 API Key 访问 /me $response = $this->get('/api/v1/me', [], [ 'X-API-Key' => $plain_key, ]); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonPath('data.id', $user->id); } public function test_invalid_api_key_returns_401(): void { $response = $this->get('/api/v1/me', [], [ 'X-API-Key' => 'invalid_key_that_does_not_exist', ]); $response->assertStatus(401); } public function test_disabled_api_key_returns_401(): void { $user = $this->createTestUser(); $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'Disabled Key', ], $this->authHeaders($user)); $plain_key = $create_response->json('data.plain_key'); $key_id = $create_response->json('data.api_key.id'); // 禁用 key $api_key = ApiKey::query()->find($key_id); $api_key->enabled = false; $api_key->save(); // 使用已禁用的 key $response = $this->get('/api/v1/me', [], [ 'X-API-Key' => $plain_key, ]); $response->assertStatus(403); } public function test_jwt_takes_priority_over_api_key(): void { $user_a = $this->createTestUser(); $user_b = $this->createTestUser(); // user_b 创建 API Key $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'Priority Test Key', ], $this->authHeaders($user_b)); $plain_key = $create_response->json('data.plain_key'); // 同时携带 JWT (user_a) 和 API Key (user_b),应使用 JWT $response = $this->get('/api/v1/me', [], [ 'Authorization' => 'Bearer ' . $this->getAuthToken($user_a), 'X-API-Key' => $plain_key, ]); $response->assertStatus(200); $response->assertJsonPath('data.id', $user_a->id); } public function test_api_key_updates_last_used_at(): void { $user = $this->createTestUser(); $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'LastUsed Test Key', ], $this->authHeaders($user)); $plain_key = $create_response->json('data.plain_key'); $key_id = $create_response->json('data.api_key.id'); // 使用 API Key 访问 $this->get('/api/v1/me', [], [ 'X-API-Key' => $plain_key, ]); // 验证 last_used_at 已更新 $api_key = ApiKey::query()->find($key_id); $this->assertNotNull($api_key->last_used_at); } public function test_disabled_user_api_key_returns_403(): void { $user = $this->createTestUser(); $create_response = $this->post('/api/v1/me/api-keys', [ 'name' => 'Disabled User Key', ], $this->authHeaders($user)); $plain_key = $create_response->json('data.plain_key'); // 禁用用户 $user->status = 0; $user->save(); // 使用 API Key 访问 $response = $this->get('/api/v1/me', [], [ 'X-API-Key' => $plain_key, ]); $response->assertStatus(403); } public function test_no_credentials_returns_401(): void { $response = $this->get('/api/v1/me'); $response->assertStatus(401); } }