true]); $response = $this->getJson('/api/v1/does-not-exist'); $response->assertNotFound() ->assertExactJson([ 'success' => false, 'message' => '资源不存在', 'errors' => [], ]); } public function test_validation_errors_use_the_failure_envelope(): void { Route::post('/api/v1/_validation_probe', function (Request $request) { $request->validate(['name' => ['required', 'string']]); return ApiResponse::success(); }); $response = $this->postJson('/api/v1/_validation_probe', []); $response->assertStatus(422) ->assertJsonPath('success', false) ->assertJsonPath('message', '参数错误') ->assertJsonStructure(['errors' => ['name']]); } public function test_method_not_allowed_uses_the_failure_envelope(): void { $this->postJson('/api/v1/health') ->assertStatus(405) ->assertJsonPath('success', false) ->assertJsonPath('message', '方法不允许'); } public function test_paginated_payload_uses_meta(): void { $paginator = new LengthAwarePaginator( items: [['sku_code' => 'A-1']], total: 21, perPage: 20, currentPage: 1, ); $payload = ApiResponse::paginated($paginator)->getData(true); $this->assertSame([ 'success' => true, 'message' => 'OK', 'data' => [ ['sku_code' => 'A-1'], ], 'meta' => [ 'current_page' => 1, 'per_page' => 20, 'total' => 21, 'last_page' => 2, ], ], $payload); } public function test_per_page_is_capped(): void { $this->assertSame(20, ApiResponse::perPage(null)); $this->assertSame(20, ApiResponse::perPage(0)); $this->assertSame(50, ApiResponse::perPage('50')); $this->assertSame(100, ApiResponse::perPage(500)); } }