runInCoroutine(static function (): bool { return Order::query()->exists(); }); } protected function getFirstOrderId(): ?int { return $this->runInCoroutine(static function (): ?int { return Order::query()->value('id'); }); } // ========== Normal 列表接口 ========== public function test_normal_list_returns_paginated_data(): void { $response = $this->get('/api/v1/orders', [], $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/orders', ['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/orders', ['per_page' => 999], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('data.per_page', 100); } public function test_normal_list_excludes_raw_and_hash(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; $this->assertArrayNotHasKey('raw', $first_item); $this->assertArrayNotHasKey('hash', $first_item); } public function test_normal_list_contains_expected_fields(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders', [], $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', 'order_status_id', 'platform_order_id', 'total_amount', 'total_paid', 'total_discount', 'created_date', 'paid_date', 'shipping_date', ]; foreach ($expected_keys as $key) { $this->assertArrayHasKey($key, $first_item, "列表缺少字段: {$key}"); } } // ========== Normal 列表筛选 ========== public function test_normal_list_filter_by_company_id(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $company_id = $this->runInCoroutine(static function (): mixed { return Order::query()->value('company_id'); }); $response = $this->get('/api/v1/orders', ['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_order_status_id(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $status_id = $this->runInCoroutine(static function (): mixed { return Order::query()->value('order_status_id'); }); $response = $this->get('/api/v1/orders', ['order_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['order_status_id']); } } public function test_normal_list_filter_by_created_date_range(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders', [ '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']); } public function test_normal_list_filter_by_paid_date_range(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders', [ 'paid_date_from' => '2020-01-01', 'paid_date_to' => '2099-12-31', ], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); } // ========== Normal 详情接口 ========== public function test_normal_detail_returns_order(): void { $id = $this->getFirstOrderId(); if (!$id) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonPath('data.id', $id); } public function test_normal_detail_contains_order_items(): void { $id = $this->getFirstOrderId(); if (!$id) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayHasKey('order_items', $body['data'], '订单详情应包含 order_items'); $this->assertIsArray($body['data']['order_items']); } public function test_normal_detail_contains_ext(): void { $id = $this->getFirstOrderId(); if (!$id) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayHasKey('ext', $body['data']); } public function test_normal_detail_excludes_raw_and_hash(): void { $id = $this->getFirstOrderId(); if (!$id) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/orders/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayNotHasKey('raw', $body['data']); $this->assertArrayNotHasKey('hash', $body['data']); } public function test_normal_detail_not_found_returns_404(): void { $response = $this->get('/api/v1/orders/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/orders', [], $this->authHeaders()); $response->assertStatus(200); $response->assertJsonPath('code', 0); $response->assertJsonStructure([ 'code', 'message', 'data' => [ 'items', 'total', 'page', 'per_page', ], ]); } public function test_raw_list_contains_hash_but_not_raw(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/raw/orders', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; $this->assertArrayHasKey('hash', $first_item, 'Raw 列表应包含 hash 字段'); $this->assertArrayNotHasKey('raw', $first_item, 'Raw 列表不应包含 raw 字段(太大)'); } public function test_raw_list_excludes_business_fields(): void { if (!$this->hasOrderData()) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/raw/orders', [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $first_item = $body['data']['items'][0] ?? []; // Raw 列表不应包含业务字段 $this->assertArrayNotHasKey('total_amount', $first_item); $this->assertArrayNotHasKey('total_paid', $first_item); $this->assertArrayNotHasKey('total_discount', $first_item); $this->assertArrayNotHasKey('buyer_user_id', $first_item); } // ========== Raw 详情接口 ========== public function test_raw_detail_contains_raw_and_hash(): void { $id = $this->getFirstOrderId(); if (!$id) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/raw/orders/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); $this->assertArrayHasKey('raw', $body['data'], 'Raw 详情应包含 raw 字段'); $this->assertArrayHasKey('hash', $body['data'], 'Raw 详情应包含 hash 字段'); $this->assertArrayHasKey('ext', $body['data'], 'Raw 详情应包含 ext 字段'); } public function test_raw_detail_excludes_business_fields(): void { $id = $this->getFirstOrderId(); if (!$id) { $this->markTestSkipped('没有订单数据'); } $response = $this->get('/api/v1/raw/orders/' . $id, [], $this->authHeaders()); $response->assertStatus(200); $body = json_decode($response->getContent(), true); // Raw 详情不应包含业务字段 $this->assertArrayNotHasKey('total_amount', $body['data']); $this->assertArrayNotHasKey('total_paid', $body['data']); $this->assertArrayNotHasKey('buyer_user_id', $body['data']); } public function test_raw_detail_not_found_returns_404(): void { $response = $this->get('/api/v1/raw/orders/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/orders'); $response->assertStatus(401); } public function test_normal_detail_without_token_returns_401(): void { $response = $this->get('/api/v1/orders/1'); $response->assertStatus(401); } public function test_raw_list_without_token_returns_401(): void { $response = $this->get('/api/v1/raw/orders'); $response->assertStatus(401); } public function test_raw_detail_without_token_returns_401(): void { $response = $this->get('/api/v1/raw/orders/1'); $response->assertStatus(401); } }