runInCoroutine(static function (): int { $record = ApiRequestLog::query()->create([ 'user_id' => 1, 'method' => 'POST', 'path' => '/api/v1/test/integration', 'status_code' => 200, 'ip' => '127.0.0.1', 'user_agent' => 'PHPUnit/Integration-Test', 'request_body' => ['username' => 'test', 'action' => 'create'], 'response_code' => 0, 'duration_ms' => 42, ]); return $record->id; }); self::$testRecordId = $id; return $id; } protected function tearDown(): void { parent::tearDown(); } public static function tearDownAfterClass(): void { if (self::$testRecordId !== null) { $id = self::$testRecordId; if (\Swoole\Coroutine::getCid() > 0) { ApiRequestLog::query()->where('id', $id)->delete(); } else { \Swoole\Coroutine\run(static function () use ($id): void { ApiRequestLog::query()->where('id', $id)->delete(); }); } self::$testRecordId = null; } parent::tearDownAfterClass(); } // ========== 列表接口 ========== public function test_list_returns_paginated_data(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonStructure([ 'code', 'message', 'data' => [ 'items', 'total', 'page', 'per_page', ], ]); } public function test_list_respects_per_page(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', ['per_page' => 5], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('data.per_page', 5); } public function test_list_excludes_request_body(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', [], $this->authHeaders()); $response->assertStatus(200); $items = $response->json('data.items'); if (!empty($items)) { $first = $items[0]; $this->assertArrayNotHasKey('request_body', $first); $this->assertArrayNotHasKey('user_agent', $first); $this->assertArrayHasKey('method', $first); $this->assertArrayHasKey('path', $first); } } // ========== 筛选 ========== public function test_list_filter_by_method(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', ['method' => 'POST'], $this->authHeaders()); $response->assertStatus(200); $items = $response->json('data.items'); foreach ($items as $item) { $this->assertSame('POST', $item['method']); } } public function test_list_filter_by_status_code(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', ['status_code' => 200], $this->authHeaders()); $response->assertStatus(200); $items = $response->json('data.items'); foreach ($items as $item) { $this->assertSame(200, $item['status_code']); } } public function test_list_filter_by_path_like(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', ['path' => 'integration'], $this->authHeaders()); $response->assertStatus(200); $items = $response->json('data.items'); foreach ($items as $item) { $this->assertStringContainsString('integration', strtolower($item['path'])); } } public function test_list_filter_by_created_at_range(): void { $this->ensureTestData(); $response = $this->get('/api/v1/logs/requests', [ 'created_at_from' => '2026-03-01', 'created_at_to' => '2026-03-31', ], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); } // ========== 详情接口 ========== public function test_detail_contains_full_fields(): void { $id = $this->ensureTestData(); $response = $this->get("/api/v1/logs/requests/{$id}", [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $data = $response->json('data'); $this->assertArrayHasKey('request_body', $data); $this->assertArrayHasKey('user_agent', $data); $this->assertArrayHasKey('method', $data); $this->assertArrayHasKey('path', $data); $this->assertArrayHasKey('ip', $data); $this->assertArrayHasKey('duration_ms', $data); $this->assertSame('POST', $data['method']); } public function test_detail_not_found_returns_404(): void { $response = $this->get('/api/v1/logs/requests/999999', [], $this->authHeaders()); $response->assertStatus(404); $this->assertSame(404, $response->json('code')); } // ========== 认证检查 ========== public function test_list_without_token_returns_401(): void { $response = $this->get('/api/v1/logs/requests'); $response->assertStatus(401); } }