'toolbox.local']); } public function test_validation_errors_are_returned_to_ip_clients(): void { $this ->postJson('http://192.168.1.20/api/production-diagnosis/diagnose', []) ->assertUnprocessable() ->assertJson([ 'success' => false, 'message' => '请求参数验证失败', ]); } public function test_successful_diagnosis_response_is_unchanged(): void { $result = [ 'type' => 'case', 'type_label' => '病例', 'code' => 'C123', 'found' => true, 'entity' => ['patient_name' => '测试患者'], 'checks' => [], 'can_production' => true, ]; $service = Mockery::mock(ProductionDiagnosisService::class); $service->shouldReceive('diagnose') ->once() ->with('case', 'C123') ->andReturn($result); $this->app->instance(ProductionDiagnosisService::class, $service); $this ->postJson('http://192.168.1.20/api/production-diagnosis/diagnose', [ 'type' => 'case', 'code' => ' C123 ', ]) ->assertOk() ->assertExactJson([ 'success' => true, 'data' => $result, ]); } public function test_internal_exception_details_are_not_returned(): void { $service = Mockery::mock(ProductionDiagnosisService::class); $service->shouldReceive('diagnose') ->once() ->andThrow(new RuntimeException('SQLSTATE[HY000] secret database detail')); $this->app->instance(ProductionDiagnosisService::class, $service); $response = $this->postJson('http://192.168.1.20/api/production-diagnosis/diagnose', [ 'type' => 'case', 'code' => 'C123', ]); $response ->assertInternalServerError() ->assertExactJson([ 'success' => false, 'message' => '诊断服务暂不可用,请稍后重试', ]); $response->assertDontSee('SQLSTATE'); $response->assertDontSee('secret database detail'); } public function test_ip_client_is_rate_limited_after_thirty_requests_per_minute(): void { for ($attempt = 1; $attempt <= 30; $attempt++) { $this ->withServerVariables(['REMOTE_ADDR' => '192.168.1.50']) ->postJson('http://192.168.1.20/api/production-diagnosis/diagnose', []) ->assertUnprocessable(); } $this ->withServerVariables(['REMOTE_ADDR' => '192.168.1.50']) ->postJson('http://192.168.1.20/api/production-diagnosis/diagnose', []) ->assertTooManyRequests(); } }