runInCoroutine(static function (): bool { return RefundItem::query()->exists(); }); } protected function getFirstRefundItemId(): ?int { return $this->runInCoroutine(static function (): ?int { return RefundItem::query()->value('id'); }); } // ========== Normal 列表接口 ========== public function test_normal_list_returns_paginated_data(): void { $response = $this->get('/api/v1/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonStructure([ 'code', 'message', 'data' => [ 'items', 'total', 'page', 'per_page', ], ]); } public function test_normal_list_respects_per_page(): void { $response = $this->get('/api/v1/refund-items', ['per_page' => 5], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('data.per_page', 5); } public function test_normal_list_per_page_max_100(): void { $response = $this->get('/api/v1/refund-items', ['per_page' => 999], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('data.per_page', 100); } public function test_normal_list_excludes_raw(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; $this->assertArrayNotHasKey('raw', $first_item); } public function test_normal_list_contains_expected_fields(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0]; $expected_keys = [ 'id', 'company_id', 'platform_id', 'store_id', 'refund_id', 'platform_refund_id', 'platform_order_id', 'platform_sub_order_id', 'platform_product_id', 'refund_status_id', 'refund_type_id', 'quantity', 'refund_amount', 'currency', 'created_date', ]; foreach ($expected_keys as $key) { $this->assertArrayHasKey($key, $first_item, "列表缺少字段: {$key}"); } } public function test_normal_list_excludes_detail_only_fields(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; $this->assertArrayNotHasKey('ext', $first_item); $this->assertArrayNotHasKey('reason', $first_item); $this->assertArrayNotHasKey('buyer_user_id', $first_item); $this->assertArrayNotHasKey('order_created_date', $first_item); } // ========== Normal 列表筛选 ========== public function test_normal_list_filter_by_company_id(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $company_id = $this->runInCoroutine(static function (): mixed { return RefundItem::query()->value('company_id'); }); $response = $this->get('/api/v1/refund-items', ['company_id' => $company_id], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); foreach ($body['data']['items'] as $item) { $this->assertSame($company_id, $item['company_id']); } } public function test_normal_list_filter_by_refund_id(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $refund_id = $this->runInCoroutine(static function (): mixed { return RefundItem::query()->value('refund_id'); }); $response = $this->get('/api/v1/refund-items', ['refund_id' => $refund_id], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); foreach ($body['data']['items'] as $item) { $this->assertSame($refund_id, $item['refund_id']); } } public function test_normal_list_filter_by_refund_status_id(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $status_id = $this->runInCoroutine(static function (): mixed { return RefundItem::query()->value('refund_status_id'); }); $response = $this->get('/api/v1/refund-items', ['refund_status_id' => $status_id], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); foreach ($body['data']['items'] as $item) { $this->assertSame($status_id, $item['refund_status_id']); } } public function test_normal_list_filter_by_refund_type_id(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $type_id = $this->runInCoroutine(static function (): mixed { return RefundItem::query()->value('refund_type_id'); }); $response = $this->get('/api/v1/refund-items', ['refund_type_id' => $type_id], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); foreach ($body['data']['items'] as $item) { $this->assertSame($type_id, $item['refund_type_id']); } } public function test_normal_list_filter_by_platform_refund_id(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $platform_refund_id = $this->runInCoroutine(static function (): mixed { return RefundItem::query()->value('platform_refund_id'); }); $response = $this->get('/api/v1/refund-items', ['platform_refund_id' => $platform_refund_id], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); foreach ($body['data']['items'] as $item) { $this->assertSame($platform_refund_id, $item['platform_refund_id']); } } public function test_normal_list_filter_by_platform_order_id(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $platform_order_id = $this->runInCoroutine(static function (): mixed { return RefundItem::query()->value('platform_order_id'); }); $response = $this->get('/api/v1/refund-items', ['platform_order_id' => $platform_order_id], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); foreach ($body['data']['items'] as $item) { $this->assertSame($platform_order_id, $item['platform_order_id']); } } public function test_normal_list_filter_by_created_date_range(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items', [ 'created_date_from' => '2020-01-01', 'created_date_to' => '2099-12-31', ], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertGreaterThanOrEqual(0, $body['data']['total']); } // ========== Normal 详情接口 ========== public function test_normal_detail_returns_refund_item(): void { $id = $this->getFirstRefundItemId(); if (!$id) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonPath('data.id', $id); } public function test_normal_detail_contains_ext(): void { $id = $this->getFirstRefundItemId(); if (!$id) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayHasKey('ext', $body['data']); } public function test_normal_detail_excludes_raw(): void { $id = $this->getFirstRefundItemId(); if (!$id) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayNotHasKey('raw', $body['data']); } public function test_normal_detail_contains_expected_fields(): void { $id = $this->getFirstRefundItemId(); if (!$id) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/refund-items/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $expected_keys = [ 'id', 'company_id', 'platform_id', 'store_id', 'refund_id', 'platform_parent_refund_id', 'platform_refund_id', 'refund_status_id', 'refund_type_id', 'platform_order_id', 'platform_sub_order_id', 'platform_product_id', 'reason', 'currency', 'buyer_user_id', 'quantity', 'refund_amount', 'order_created_date', 'order_paid_date', 'created_date', 'updated_date', 'completed_date', 'ext', 'created_at', 'updated_at', ]; foreach ($expected_keys as $key) { $this->assertArrayHasKey($key, $body['data'], "详情缺少字段: {$key}"); } } public function test_normal_detail_not_found_returns_404(): void { $response = $this->get('/api/v1/refund-items/999999999', [], $this->authHeaders()); $response->assertStatus(404); $response->assertJsonPath('code', 404); } // ========== Raw 列表接口 ========== public function test_raw_list_returns_paginated_data(): void { $response = $this->get('/api/v1/raw/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonStructure([ 'code', 'message', 'data' => [ 'items', 'total', 'page', 'per_page', ], ]); } public function test_raw_list_does_not_contain_raw(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/raw/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; $this->assertArrayNotHasKey('raw', $first_item, 'Raw 列表不应包含 raw 字段(太大)'); } public function test_raw_list_excludes_business_fields(): void { if (!$this->hasRefundItemData()) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/raw/refund-items', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; $this->assertArrayNotHasKey('refund_amount', $first_item); $this->assertArrayNotHasKey('quantity', $first_item); $this->assertArrayNotHasKey('buyer_user_id', $first_item); $this->assertArrayNotHasKey('reason', $first_item); } // ========== Raw 详情接口 ========== public function test_raw_detail_contains_raw_and_ext(): void { $id = $this->getFirstRefundItemId(); if (!$id) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/raw/refund-items/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayHasKey('raw', $body['data'], 'Raw 详情应包含 raw 字段'); $this->assertArrayHasKey('ext', $body['data'], 'Raw 详情应包含 ext 字段'); } public function test_raw_detail_excludes_business_fields(): void { $id = $this->getFirstRefundItemId(); if (!$id) { $this->markTestSkipped('没有退款项数据'); } $response = $this->get('/api/v1/raw/refund-items/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayNotHasKey('refund_amount', $body['data']); $this->assertArrayNotHasKey('quantity', $body['data']); $this->assertArrayNotHasKey('buyer_user_id', $body['data']); $this->assertArrayNotHasKey('reason', $body['data']); } public function test_raw_detail_not_found_returns_404(): void { $response = $this->get('/api/v1/raw/refund-items/999999999', [], $this->authHeaders()); $response->assertStatus(404); $response->assertJsonPath('code', 404); } // ========== 认证拦截 ========== public function test_normal_list_without_token_returns_401(): void { $response = $this->get('/api/v1/refund-items'); $response->assertStatus(401); } public function test_normal_detail_without_token_returns_401(): void { $response = $this->get('/api/v1/refund-items/1'); $response->assertStatus(401); } public function test_raw_list_without_token_returns_401(): void { $response = $this->get('/api/v1/raw/refund-items'); $response->assertStatus(401); } public function test_raw_detail_without_token_returns_401(): void { $response = $this->get('/api/v1/raw/refund-items/1'); $response->assertStatus(401); } }