Files
ECShopX/tests/ShuyunOpenPlatform/ShuyunOpenPlatformOrderAcceptanceMockTest.php
wanghai 058673559c 4.7.0
2026-06-26 19:20:24 +08:00

275 lines
11 KiB
PHP
Raw Permalink 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
declare(strict_types=1);
namespace Tests\ShuyunOpenPlatform;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Response;
use ShuyunOpenPlatformBundle\Entities\CompanyShuyunOpenPlatformConfig;
use ShuyunOpenPlatformBundle\Repositories\CompanyShuyunOpenPlatformConfigRepository;
use ShuyunOpenPlatformBundle\Services\ShuyunOpenPlatformGatewayClientFactory;
use ShuyunOpenPlatformBundle\Services\ShuyunOpenPlatformOrderGatewayActions;
use ShuyunOpenPlatformBundle\Services\ShuyunOpenPlatformRefundSyncService;
use ShuyunOpenPlatformBundle\Services\ShuyunOpenPlatformShopSyncService;
use ShuyunOpenPlatformBundle\Services\ShuyunOpenPlatformTradeSyncService;
use ShuyunOpenPlatformBundle\Services\ShuyunOpenPlatformTrafficAuditWriter;
/**
* 计划 **Acceptance & Test Cases** 中与网关 HTTP 相关的条目 — Guzzle MockHandler 录制请求,不连外网。
*
* @see `.tasks/plans/shuyun-open-platform-order.md` A-ORD-FWD-02Z、FWD-03、REV-04、REV-05、GATE-02、PLATbody 无 platform
*/
class ShuyunOpenPlatformOrderAcceptanceMockTest extends \TestCase
{
/** A-ORD-FWD-02Z零元单仍 POST trade.sync金额字段允许为 0D-ZERO-01 */
public function testAcceptanceFwd02ZeroYuanTradeSyncPayload(): void
{
$container = [];
$svc = $this->tradeServiceWithHistory($container, [
new Response(200, [], '{"code":10000,"data":{},"msg":""}'),
]);
$order = $this->minimalTradeOrder();
$order['payment'] = 0.0;
$order['post_fee'] = 0.0;
$order['trade_discount_fee'] = 0.0;
$this->assertTrue($svc->syncValidatedTradeOrders(1, 'myplat', [$order]));
$body = (string) $container[0]['request']->getBody();
$decoded = json_decode($body, true);
$this->assertIsArray($decoded);
$this->assertArrayHasKey(0, $decoded);
$this->assertSame(0.0, (float) ($decoded[0]['payment'] ?? -1));
}
/** A-ORD-FWD-0351 条 → 2 次 POST50+1 */
public function testAcceptanceFwd03FiftyOneTradeOrdersTwoPosts(): void
{
$container = [];
$svc = $this->tradeServiceWithHistory($container, [
new Response(200, [], '{"code":10000,"data":{},"msg":""}'),
new Response(200, [], '{"code":10000,"data":{},"msg":""}'),
]);
$base = $this->minimalTradeOrder();
$orders = [];
for ($i = 0; $i < 51; ++$i) {
$row = $base;
$row['order_id'] = (string) (900000 + $i);
$orders[] = $row;
}
$this->assertTrue($svc->syncValidatedTradeOrders(1, 'wxplat', $orders));
$this->assertCount(2, $container);
$this->assertSame(ShuyunOpenPlatformOrderGatewayActions::GATEWAY_ACTION_TRADE_SYNC, $container[0]['request']->getHeaderLine('Gateway-Action-Method'));
$this->assertSame(ShuyunOpenPlatformOrderGatewayActions::GATEWAY_ACTION_TRADE_SYNC, $container[1]['request']->getHeaderLine('Gateway-Action-Method'));
}
/** 正向 body 根为订单数组,元素内不传 `platform`(仅 Header */
public function testAcceptanceTradeBodyJsonRootIsArrayWithoutPlatformKey(): void
{
$container = [];
$svc = $this->tradeServiceWithHistory($container, [
new Response(200, [], '{"code":10000,"data":{},"msg":""}'),
]);
$this->assertTrue($svc->syncValidatedTradeOrders(1, 'offline', [$this->minimalTradeOrder()]));
$decoded = json_decode((string) $container[0]['request']->getBody(), true);
$this->assertIsArray($decoded);
$this->assertArrayHasKey(0, $decoded);
$this->assertArrayNotHasKey('platform', $decoded[0]);
}
/** A-ORD-GATE-02网关业务码非 10000 → sync 返回 falseERROR 由 Service 内记录) */
public function testAcceptanceGate02BusinessNonSuccessReturnsFalse(): void
{
$container = [];
$svc = $this->tradeServiceWithHistory($container, [
new Response(200, [], '{"code":11008,"msg":"access invalid","data":null}'),
]);
$this->assertFalse($svc->syncValidatedTradeOrders(1, 'offline', [$this->minimalTradeOrder()]));
$this->assertCount(1, $container);
}
/** A-ORD-REV-04同一 refund_id 先审核中再成功,两次 sync末次 body 含 SY_REFUND_SUCC */
public function testAcceptanceRev04RefundResyncSecondPayloadSuccess(): void
{
$container = [];
$svc = $this->refundServiceWithHistory($container, [
new Response(200, [], '{"code":10000,"data":"success","msg":""}'),
new Response(200, [], '{"code":10000,"data":"success","msg":""}'),
]);
$base = $this->minimalRefundPayload();
$checking = $base;
$checking['refund_status'] = 'SY_CHECKING';
$success = $base;
$success['refund_status'] = 'SY_REFUND_SUCC';
$this->assertTrue($svc->syncValidatedRefunds(1, 'offline', [$checking]));
$this->assertTrue($svc->syncValidatedRefunds(1, 'offline', [$success]));
$this->assertCount(2, $container);
$body1 = (string) $container[0]['request']->getBody();
$body2 = (string) $container[1]['request']->getBody();
$this->assertStringContainsString('SY_CHECKING', $body1);
$this->assertStringContainsString('SY_REFUND_SUCC', $body2);
$this->assertStringContainsString('"refund_id":"acc-mock-1"', $body2);
}
/** A-ORD-REV-0551 条退款 → 2 次 POST */
public function testAcceptanceRev05FiftyOneRefundsTwoPosts(): void
{
$container = [];
$svc = $this->refundServiceWithHistory($container, [
new Response(200, [], '{"code":10000,"data":"success","msg":""}'),
new Response(200, [], '{"code":10000,"data":"success","msg":""}'),
]);
$base = $this->minimalRefundPayload();
$refunds = [];
for ($i = 0; $i < 51; ++$i) {
$r = $base;
$r['refund_id'] = 'r'.(700000 + $i);
$refunds[] = $r;
}
$this->assertTrue($svc->syncValidatedRefunds(1, 'offline', $refunds));
$this->assertCount(2, $container);
$this->assertSame(ShuyunOpenPlatformOrderGatewayActions::GATEWAY_ACTION_REFUND_SYNC, $container[0]['request']->getHeaderLine('Gateway-Action-Method'));
}
/** 逆向 refund body 数组元素内不传 `platform` */
public function testAcceptanceRefundBodyElementWithoutPlatformKey(): void
{
$container = [];
$svc = $this->refundServiceWithHistory($container, [
new Response(200, [], '{"code":10000,"data":"success","msg":""}'),
]);
$this->assertTrue($svc->syncValidatedRefunds(1, 'offline', [$this->minimalRefundPayload()]));
$decoded = json_decode((string) $container[0]['request']->getBody(), true);
$this->assertIsArray($decoded);
$this->assertArrayNotHasKey('platform', $decoded[0]);
}
/**
* @param list<Response> $responses
*/
private function tradeServiceWithHistory(array &$container, array $responses): ShuyunOpenPlatformTradeSyncService
{
config(['shuyun_open_platform.base_uri' => 'http://open-api.test']);
config(['shuyun_open_platform.timeout' => 5.0]);
$cfgRepo = $this->createMock(CompanyShuyunOpenPlatformConfigRepository::class);
$cfgRepo->method('findOneByCompanyId')->willReturn($this->eligibleConfig());
$shopEligibility = $this->createMock(ShuyunOpenPlatformShopSyncService::class);
$shopEligibility->method('isEligible')->willReturn(true);
$stack = HandlerStack::create(new MockHandler($responses));
$stack->push(Middleware::history($container));
$client = new Client(['handler' => $stack, 'base_uri' => 'http://open-api.test/']);
return new ShuyunOpenPlatformTradeSyncService($cfgRepo, $shopEligibility, $client, new ShuyunOpenPlatformGatewayClientFactory(null));
}
/**
* @param list<Response> $responses
*/
private function refundServiceWithHistory(array &$container, array $responses): ShuyunOpenPlatformRefundSyncService
{
config(['shuyun_open_platform.base_uri' => 'http://open-api.test']);
config(['shuyun_open_platform.timeout' => 5.0]);
$cfgRepo = $this->createMock(CompanyShuyunOpenPlatformConfigRepository::class);
$cfgRepo->method('findOneByCompanyId')->willReturn($this->eligibleConfig());
$shopEligibility = $this->createMock(ShuyunOpenPlatformShopSyncService::class);
$shopEligibility->method('isEligible')->willReturn(true);
$stack = HandlerStack::create(new MockHandler($responses));
$stack->push(Middleware::history($container));
$client = new Client(['handler' => $stack, 'base_uri' => 'http://open-api.test/']);
return new ShuyunOpenPlatformRefundSyncService($cfgRepo, $shopEligibility, $client, new ShuyunOpenPlatformGatewayClientFactory(null));
}
/**
* @return array<string, mixed>
*/
private function minimalTradeOrder(): array
{
return [
'shop_id' => '2202520',
'plat_account' => '10001',
'order_id' => '9081283821',
'order_status' => 'WAIT_SELLER_SEND_GOODS',
'trade_type' => 'FIXED',
'is_presale' => '0',
'trade_source' => '11',
'payment' => 121.0,
'post_fee' => 10.0,
'adjust_fee' => 0.7,
'product_num' => 1,
'created' => '2017-12-08 21:33:03',
'modified' => '2017-12-08 21:33:03',
'delivery_type' => 'SY_EXPRESS',
'orders' => [
[
'order_item_id' => '1223',
'product_id' => '908872367645891',
'sku_id' => '231562388901',
'product_name' => '新款T恤',
'price' => 12.22,
'product_num' => 1,
'discount_fee' => 0.0,
'adjust_fee' => 0.0,
'pay_time' => '2018-10-08 09:00:00',
],
],
];
}
/**
* @return array<string, mixed>
*/
private function minimalRefundPayload(): array
{
return [
'refund_id' => 'acc-mock-1',
'order_id' => '22112333',
'order_item_id' => '231433221223',
'shop_id' => '2202520',
'product_id' => '234221111',
'sku_id' => '324443333',
'refund_fee' => 0.9,
'refund_status' => 'SY_CHECKING',
'good_return' => 'SY_ONLY_FEE',
'refund_reason' => 'acceptance-mock',
'created' => '2018-06-08 12:00:34',
'modified' => '2018-06-09 12:00:34',
'refund_phase' => 2,
];
}
private function eligibleConfig(): CompanyShuyunOpenPlatformConfig
{
$e = new CompanyShuyunOpenPlatformConfig();
$e->setCompanyId(1);
$e->setAuthValue('av');
$e->setAppId('aid');
$e->setAppSecret('sec');
$e->setAccessToken('tok');
$e->setIsEnabled(1);
return $e;
}
}