CaiWuXiTong/tests/Unit/EcommerceFinance/JushuitanOpenApiClientTest.php
limeng 8f150080c5 修正聚水潭成本价历史凭据边界与未同步状态 (#186)
保留完整成本价历史方案,区分经典接口凭据与新开放平台凭据;首次同步未完成时显示待同步,不误报 0 条。

验证:33 passed,260 assertions。
2026-09-15 11:39:09 +08:00

178 lines
7.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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/api/open/query.aspx',
'partner_id' => 'test-partner-id',
'partner_key' => 'test-partner-key',
'token' => 'test-token',
'timeout' => 20,
'product_operation_history_method' => 'worklog.query',
]);
Http::preventStrayRequests();
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
public function test_it_posts_signed_json_and_keeps_only_cost_price_operations(): void
{
Http::fake([
'https://jushuitan.example.test/*' => Http::response([
'code' => 0,
'page_size' => 2,
'page_index' => 1,
'has_next' => false,
'datas' => [[
'autoid' => 101,
'type' => '商品与库存管理导入',
'remark' => '采购价: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,
'type' => '编辑',
'remark' => '商品名称:旧名称->新名称',
'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-08 00:00:00'),
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 {
parse_str((string) parse_url($request->url(), PHP_URL_QUERY), $query);
$timestamp = (string) Carbon::now()->timestamp;
return $request->method() === 'POST'
&& str_starts_with($request->url(), 'https://jushuitan.example.test/api/open/query.aspx?')
&& str_starts_with((string) $request->header('Content-Type')[0], 'application/json')
&& $query === [
'method' => 'worklog.query',
'partnerid' => 'test-partner-id',
'token' => 'test-token',
'ts' => $timestamp,
'sign' => md5('worklog.querytest-partner-idtokentest-tokents' . $timestamp . 'test-partner-key'),
]
&& $request->data() === [
'page_index' => 1,
'page_size' => 50,
'modified_begin' => '2026-06-01 00:00:00',
'modified_end' => '2026-06-08 00:00:00',
];
});
}
public function test_it_preserves_unparsed_cost_operation_content_without_inventing_prices(): void
{
Http::fake([
'*' => Http::response([
'code' => 0,
'page_index' => 1,
'has_next' => false,
'datas' => [[
'autoid' => 103,
'remark' => '采购价已由批量任务更新',
'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.token', null);
try {
(new JushuitanOpenApiClient())->getCostPriceOperationHistory(now(), now(), 1, 100);
$this->fail('Expected missing credential exception.');
} catch (RuntimeException $exception) {
$this->assertSame('jushuitan_classic_api_credentials_missing', $exception->getMessage());
}
config()->set('services.jushuitan.token', 'test-token');
Http::fake(['*' => Http::response(['code' => 10001, 'msg' => 'denied'])]);
$this->expectExceptionMessage('jushuitan_api_code_10001');
(new JushuitanOpenApiClient())->getCostPriceOperationHistory(now(), now(), 1, 100);
}
public function test_it_reports_an_invalid_classic_partner_id_without_exposing_upstream_text(): void
{
Http::fake(['*' => Http::response([
'code' => 100,
'msg' => '验证失败! partnerid 无效',
])]);
$this->expectExceptionMessage('jushuitan_classic_api_partner_id_invalid');
(new JushuitanOpenApiClient())->getCostPriceOperationHistory(now(), now(), 1, 50);
}
public function test_new_platform_app_credentials_do_not_replace_classic_credentials(): void
{
config()->set('services.jushuitan.partner_id', null);
config()->set('services.jushuitan.partner_key', null);
config()->set('services.jushuitan.token', null);
config()->set('services.jushuitan.app_key', 'new-platform-app-key');
config()->set('services.jushuitan.app_secret', 'new-platform-app-secret');
config()->set('services.jushuitan.access_token', 'new-platform-access-token');
try {
(new JushuitanOpenApiClient())->getCostPriceOperationHistory(now(), now(), 1, 50);
$this->fail('Expected missing classic credential exception.');
} catch (RuntimeException $exception) {
$this->assertSame('jushuitan_classic_api_credentials_missing', $exception->getMessage());
}
Http::assertNothingSent();
}
}