151 lines
5.9 KiB
PHP
151 lines
5.9 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit\EcommerceFinance;
|
||
|
||
use Illuminate\Http\Client\Request;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Modules\EcommerceFinance\Services\JushuitanOpenApiClient;
|
||
use RuntimeException;
|
||
use Tests\TestCase;
|
||
|
||
class JushuitanOpenApiClientTest extends TestCase
|
||
{
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
Carbon::setTestNow('2026-09-14 06:00:00');
|
||
config()->set('services.jushuitan', [
|
||
'base_url' => 'https://jushuitan.example.test',
|
||
'app_key' => 'test-app-key',
|
||
'app_secret' => 'test-app-secret',
|
||
'access_token' => 'test-access-token',
|
||
'timeout' => 20,
|
||
'product_operation_history_path' => '/open/webapi/itemapi/itemsku/queryitemskuopshistory',
|
||
]);
|
||
Http::preventStrayRequests();
|
||
}
|
||
|
||
protected function tearDown(): void
|
||
{
|
||
Carbon::setTestNow();
|
||
parent::tearDown();
|
||
}
|
||
|
||
public function test_it_posts_signed_form_and_keeps_only_cost_price_operations(): void
|
||
{
|
||
Http::fake([
|
||
'https://jushuitan.example.test/*' => Http::response([
|
||
'code' => 0,
|
||
'data' => [
|
||
'page' => ['current_page' => 1, 'page_size' => 2000, 'count' => 2, 'pages' => 1],
|
||
'list' => [[
|
||
'autoid' => 101,
|
||
'operation_type' => '商品与库存管理导入',
|
||
'operation_description' => '采购价:72.2222->65',
|
||
'created' => '2026-06-06 14:32:02.993',
|
||
'creator_name' => '简尚',
|
||
'sku_id' => '15961G',
|
||
'i_id' => 'ZWJ0111',
|
||
'name' => '测试商品',
|
||
'properties_value' => '胡桃色',
|
||
'sku_type' => 'normal',
|
||
], [
|
||
'autoid' => 102,
|
||
'operation_type' => '编辑',
|
||
'operation_description' => '商品名称:旧名称->新名称',
|
||
'created' => '2026-06-06 14:33:00',
|
||
'creator_name' => '测试人',
|
||
'sku_id' => '15961G',
|
||
]],
|
||
],
|
||
]),
|
||
]);
|
||
|
||
$result = (new JushuitanOpenApiClient())->getCostPriceOperationHistory(
|
||
Carbon::parse('2026-06-01 00:00:00'),
|
||
Carbon::parse('2026-06-30 23:59:59'),
|
||
1,
|
||
2000,
|
||
);
|
||
|
||
$this->assertSame(2, $result['source_rows']);
|
||
$this->assertCount(1, $result['records']);
|
||
$this->assertSame('101', $result['records'][0]['operation_id']);
|
||
$this->assertSame(72.2222, $result['records'][0]['before_price']);
|
||
$this->assertSame(65.0, $result['records'][0]['after_price']);
|
||
$this->assertEqualsWithDelta(-7.2222, $result['records'][0]['diff_amount'], 0.00001);
|
||
$this->assertSame('简尚', $result['records'][0]['operator_name']);
|
||
|
||
Http::assertSent(function (Request $request): bool {
|
||
$data = $request->data();
|
||
$sign = $data['sign'] ?? null;
|
||
unset($data['sign']);
|
||
ksort($data, SORT_STRING);
|
||
$plain = 'test-app-secret';
|
||
foreach ($data as $key => $value) {
|
||
$plain .= $key . (string) $value;
|
||
}
|
||
$biz = json_decode((string) ($data['biz'] ?? ''), true);
|
||
|
||
return $request->method() === 'POST'
|
||
&& $request->url() === 'https://jushuitan.example.test/open/webapi/itemapi/itemsku/queryitemskuopshistory'
|
||
&& str_starts_with((string) $request->header('Content-Type')[0], 'application/x-www-form-urlencoded')
|
||
&& $sign === md5($plain)
|
||
&& $biz === [
|
||
'current_page' => 1,
|
||
'page_size' => 2000,
|
||
'start_time' => '2026-06-01 00:00:00',
|
||
'end_time' => '2026-06-30 23:59:59',
|
||
];
|
||
});
|
||
}
|
||
|
||
public function test_it_preserves_unparsed_cost_operation_content_without_inventing_prices(): void
|
||
{
|
||
Http::fake([
|
||
'*' => Http::response([
|
||
'code' => 0,
|
||
'data' => [
|
||
'page' => ['current_page' => 1, 'count' => 1, 'pages' => 1],
|
||
'list' => [[
|
||
'autoid' => 103,
|
||
'operation_description' => '采购价已由批量任务更新',
|
||
'created' => '2026-06-07 08:00:00',
|
||
'sku_id' => 'SKU-UNPARSED',
|
||
]],
|
||
],
|
||
]),
|
||
]);
|
||
|
||
$result = (new JushuitanOpenApiClient())->getCostPriceOperationHistory(
|
||
Carbon::parse('2026-06-01'),
|
||
Carbon::parse('2026-06-30'),
|
||
1,
|
||
100,
|
||
);
|
||
|
||
$this->assertCount(1, $result['records']);
|
||
$this->assertNull($result['records'][0]['before_price']);
|
||
$this->assertNull($result['records'][0]['after_price']);
|
||
$this->assertSame('采购价已由批量任务更新', $result['records'][0]['operation_description']);
|
||
}
|
||
|
||
public function test_it_rejects_missing_credentials_and_upstream_error_codes(): void
|
||
{
|
||
config()->set('services.jushuitan.access_token', null);
|
||
try {
|
||
(new JushuitanOpenApiClient())->getCostPriceOperationHistory(now(), now(), 1, 100);
|
||
$this->fail('Expected missing credential exception.');
|
||
} catch (RuntimeException $exception) {
|
||
$this->assertSame('jushuitan_api_credentials_missing', $exception->getMessage());
|
||
}
|
||
|
||
config()->set('services.jushuitan.access_token', 'test-access-token');
|
||
Http::fake(['*' => Http::response(['code' => 10001, 'msg' => 'denied'])]);
|
||
|
||
$this->expectExceptionMessage('jushuitan_api_code_10001');
|
||
(new JushuitanOpenApiClient())->getCostPriceOperationHistory(now(), now(), 1, 100);
|
||
}
|
||
}
|