diff --git a/src/Payment/Jssdk/Client.php b/src/Payment/Jssdk/Client.php index c543e265e..01fde4bf7 100644 --- a/src/Payment/Jssdk/Client.php +++ b/src/Payment/Jssdk/Client.php @@ -61,11 +61,11 @@ public function bridgeConfig($prepayId, $json = true) * * @param string $prepayId * - * @return array|string + * @return array */ public function sdkConfig($prepayId) { - $config = $this->configForPayment($prepayId, false); + $config = $this->bridgeConfig($prepayId, false); $config['timestamp'] = $config['timeStamp']; unset($config['timeStamp']); diff --git a/tests/Kernel/AccessTokenTest.php b/tests/Kernel/AccessTokenTest.php index 2d7e1cb67..a10a53436 100644 --- a/tests/Kernel/AccessTokenTest.php +++ b/tests/Kernel/AccessTokenTest.php @@ -31,11 +31,11 @@ public function testCache() $this->assertInstanceOf(CacheInterface::class, $token->getCache()); // prepended cache instance - $cache = mock(CacheInterface::class); + $cache = \Mockery::mock(CacheInterface::class); $app['cache'] = function () use ($cache) { return $cache; }; - $token = mock(AccessToken::class.'[setCache]', [$app]); + $token = \Mockery::mock(AccessToken::class.'[setCache]', [$app]); $this->assertInstanceOf(CacheInterface::class, $token->getCache()); } diff --git a/tests/Kernel/BaseClientTest.php b/tests/Kernel/BaseClientTest.php index c647c8433..5621ef18a 100644 --- a/tests/Kernel/BaseClientTest.php +++ b/tests/Kernel/BaseClientTest.php @@ -25,7 +25,7 @@ class BaseClientTest extends TestCase { public function makeClient($methods = [], ServiceContainer $app = null, AccessToken $accessToken = null) { - $methods = implode(',', (array) $methods); + $methods = implode(',', (array)$methods); return \Mockery::mock(BaseClient::class."[{$methods}]", [ $app ?? \Mockery::mock(ServiceContainer::class), @@ -106,7 +106,7 @@ public function testRequest() 'response_type' => 'array', ]); $client = $this->makeClient(['registerHttpMiddlewares', 'performRequest'], $app) - ->shouldAllowMockingProtectedMethods(); + ->shouldAllowMockingProtectedMethods(); // default value $client->expects()->registerHttpMiddlewares()->once(); @@ -151,8 +151,8 @@ public function testHttpClient() public function testRegisterMiddlewares() { $client = $this->makeClient(['retryMiddleware', 'accessTokenMiddleware', 'logMiddleware', 'pushMiddleware']) - ->shouldAllowMockingProtectedMethods() - ->shouldDeferMissing(); + ->shouldAllowMockingProtectedMethods() + ->shouldDeferMissing(); $retryMiddleware = function () { return 'retry'; }; diff --git a/tests/Payment/ApplicationTest.php b/tests/Payment/ApplicationTest.php new file mode 100644 index 000000000..2aae3bd05 --- /dev/null +++ b/tests/Payment/ApplicationTest.php @@ -0,0 +1,30 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Tests\TestCase; +use EasyWeChat\Payment\Client; + +class ApplicationTest extends TestCase +{ + public function testMagicCall() + { + $app = new Application(); + + $this->assertInstanceOf(Client::class, $app->sandboxMode(true)); + + // test calling nonexistent method + $this->expectException(\PHPUnit\Framework\Error\Warning::class); + $app->noncexistentMethod('foo'); + } +} diff --git a/tests/Payment/BaseClientTest.php b/tests/Payment/BaseClientTest.php new file mode 100644 index 000000000..e24eb1699 --- /dev/null +++ b/tests/Payment/BaseClientTest.php @@ -0,0 +1,107 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Payment\BaseClient; +use EasyWeChat\Kernel\Http\Response; +use EasyWeChat\Tests\TestCase; +use EasyWeChat\Kernel\Support; + +class BaseClientTest extends TestCase +{ + public function testPrepends() + { + $app = new Application(); + $client = $this->mockApiClient(BaseClient::class, 'prepends', $app)->makePartial(); + + $this->assertEmpty($client->prepends()); + $this->assertSame([], $client->prepends()); + } + + public function testRequest() + { + $app = new Application(); + $client = $this->mockApiClient(BaseClient::class, ['performRequest', 'resolveResponse', 'prepends', 'getSignKey'], $app)->makePartial(); + + $api = 'http://easywechat.org'; + $params = ['foo' => 'bar']; + $method = 'post'; + $options = ['foo' => 'bar']; + + $mockResponse = new Response(200, [], 'response-content'); + + $client->expects()->performRequest($api, $method, \Mockery::on(function ($options) { + $this->assertSame('bar', $options['foo']); + $this->assertInternalType('string', $options['body']); + + $bodyInOptions = Support\XML::parse($options['body']); + + $this->assertSame($bodyInOptions['foo'], $options['foo']); + $this->assertInternalType('string', $bodyInOptions['nonce_str']); + $this->assertInternalType('string', $bodyInOptions['sign']); + + return true; + }))->times(3)->andReturn($mockResponse); + + + $client->expects()->resolveResponse() + ->with($mockResponse, \Mockery::any()) + ->once() + ->andReturn(['foo' => 'mock-bar']); + + // $returnResponse = false + $this->assertSame(['foo' => 'mock-bar'], $client->request($api, $params, $method, $options, false)); + + // $returnResponse = true + $this->assertInstanceOf(Response::class, $client->request($api, $params, $method, $options, true)); + $this->assertSame('response-content', $client->request($api, $params, $method, $options, true)->getBodyContents()); + } + + public function testRequestRaw() + { + $app = new Application(); + $client = $this->mockApiClient(BaseClient::class, ['request', 'requestRaw'], $app)->makePartial(); + + $api = 'http://easywechat.org'; + $params = ['foo' => 'bar']; + $method = 'post'; + $options = []; + + $client->expects()->request($api, $params, $method, $options, true)->andReturn('mock-result')->once(); + + $this->assertSame('mock-result', $client->requestRaw($api, $params, $method, $options)); + } + + public function testSafeRequest() + { + $app = new Application([ + 'app_id' => 'wx123456', + 'cert_path' => 'foo', + 'key_path' => 'bar', + ]); + $client = $this->mockApiClient(BaseClient::class, ['request', 'safeRequest'], $app)->makePartial(); + + $api = 'http://easywechat.org'; + $params = ['foo' => 'bar']; + $method = 'post'; + + $options = [ + 'cert' => $app['merchant']->get('cert_path'), + 'ssl_key' => $app['merchant']->get('key_path'), + ]; + + $client->expects()->request($api, $params, $method, $options)->andReturn('mock-result')->once(); + + $this->assertSame('mock-result', $client->safeRequest($api, $params, $method)); + } +} diff --git a/tests/Payment/ClientTest.php b/tests/Payment/ClientTest.php new file mode 100644 index 000000000..05a5766fc --- /dev/null +++ b/tests/Payment/ClientTest.php @@ -0,0 +1,302 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Payment\Client; +use EasyWeChat\Payment\Order; +use EasyWeChat\Kernel\Support; +use EasyWeChat\Tests\TestCase; + +class ClientTest extends TestCase +{ + public function testSchema() + { + $app = new Application(); + $mock = $this->mockApiClient(Client::class, 'scheme', $app)->makePartial(); + + $productId = '1'; + + $this->assertNotEmpty($mock->scheme($productId)); + $this->assertStringStartsWith('weixin://wxpay/bizpayurl?', $mock->scheme($productId)); + } + + public function testPay() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['pay', 'wrapApi'], $app)->makePartial(); + + // mock order + $order = \Mockery::mock(Order::class."[all]")->shouldAllowMockingProtectedMethods()->makePartial(); + + $client->expects()->request($client->wrapApi('pay/micropay'), $order->all())->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->pay($order)); + } + + public function testPrepare() + { + $app = new Application([ + 'app_id' => 'wx123456', + 'merchant_id' => 'foo-merchant-id', + ]); + + $client = $this->mockApiClient(Client::class, ['prepare', 'request'], $app)->makePartial(); + + $order = \Mockery::mock(Order::class.'[all, get, set]')->makePartial(); + + $order->set('notify_url', 'https://easywechat.org'); + + // $order->spbill_create_ip is null and trade_type === Order::NATIVE + $order->set('trade_type', Order::NATIVE); + $client->expects()->request($client->wrapApi('pay/unifiedorder'), array_merge($order->all(), ['spbill_create_ip' => Support\get_server_ip()]))->once()->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->prepare($order)); + + // $order->spbill_create_ip is null and trade_type !== Order::NATIVE + $order->set('trade_type', Order::JSAPI); + $client->expects()->request($client->wrapApi('pay/unifiedorder'), array_merge($order->all(), ['spbill_create_ip' => Support\get_client_ip()]))->once()->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->prepare($order)); + + // $order->spbill_create_ip is not null. + $order->set('spbill_create_ip', '192.168.0.1'); + $client->expects()->request($client->wrapApi('pay/unifiedorder'), $order->all())->once()->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->prepare($order)); + } + + public function testQuery() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, 'query', $app)->makePartial(); + + $orderNo = 'foo'; + $type = 'bar'; + + // default type + $client->expects()->request($client->wrapApi('pay/orderquery'), [Client::OUT_TRADE_NO => $orderNo])->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->query($orderNo)); + + // pass a type parameter + $client->expects()->request($client->wrapApi('pay/orderquery'), [$type => $orderNo])->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->query($orderNo, $type)); + } + + public function testQueryByTransactionId() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['query', 'queryByTransactionId'], $app)->makePartial(); + + $transactionId = 'foo'; + + $client->expects()->query($transactionId, Client::TRANSACTION_ID)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryByTransactionId($transactionId)); + } + + public function testClose() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, 'close', $app)->makePartial(); + + $tradeNo = 'foo'; + + $client->expects()->request($client->wrapApi('pay/closeorder'), ['out_trade_no' => $tradeNo])->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->close($tradeNo)); + } + + public function testReverse() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['reverse', 'safeRequest'], $app)->makePartial(); + + $orderNo = 'foo'; + + // default type === 'out_trade_no' + $client->expects()->safeRequest($client->wrapApi('secapi/pay/reverse'), [Client::OUT_TRADE_NO => $orderNo])->andReturn('mock-result-out-trade-no')->once(); + $this->assertSame('mock-result-out-trade-no', $client->reverse($orderNo)); + + // pass a type parameter + $type = Client::TRANSACTION_ID; + $client->expects()->safeRequest($client->wrapApi('secapi/pay/reverse'), [$type => $orderNo])->andReturn('mock-result-with-type')->once(); + $this->assertSame('mock-result-with-type', $client->reverse($orderNo, $type)); + } + + public function testReverseByTransactionId() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['reverse', 'reverseByTransactionId'], $app)->makePartial(); + + $transactionId = 'foo'; + + $client->expects()->reverse($transactionId, Client::TRANSACTION_ID)->andReturn('mock-result')->once(); + + $this->assertSame('mock-result', $client->reverseByTransactionId($transactionId)); + } + + public function testRefund() + { + $app = new Application([ + 'app_id' => 'wx123456', + 'merchant_id' => 'foo-merchant-id', + ]); + $client = $this->mockApiClient(Client::class, ['refund', 'safeRequest'], $app)->makePartial(); + + $orderNo = 'foo'; + $refundNo = 'bar'; + $totalFee = 1; + $optional = ['foo' => 'bar']; + + $params = array_merge([ + Client::TRANSACTION_ID => $orderNo, + 'out_refund_no' => $refundNo, + 'total_fee' => $totalFee, + 'refund_fee' => $totalFee, + 'refund_fee_type' => $app['merchant']->fee_type, + 'refund_account' => 'REFUND_SOURCE_UNSETTLED_FUNDS', + 'op_user_id' => $app['merchant']->merchant_id, + ], $optional); + + $client->expects()->safeRequest($client->wrapApi('secapi/pay/refund'), $params)->andReturn('mock-result')->once(); + + $this->assertSame('mock-result', $client->refund($orderNo, $refundNo, $totalFee, $optional)); + } + + public function testRefundByTransactionId() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['refund', 'refundByTransactionId'], $app)->makePartial(); + + $orderNo = 'foo'; + $refundNo = 'bar'; + $totalFee = 1; + $optional = []; + + $client->expects()->refund($orderNo, $refundNo, $totalFee, $optional)->andReturn('mock-result')->once(); + + $this->assertSame('mock-result', $client->refundByTransactionId($orderNo, $refundNo, $totalFee, $optional)); + } + + public function testQueryRefund() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, 'queryRefund', $app)->makePartial(); + + $orderNo = 'foo'; + $type = 'bar'; + + // default type + $client->expects()->request($client->wrapApi('pay/refundquery'), [Client::OUT_TRADE_NO => $orderNo])->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryRefund($orderNo)); + + // pass a type parameter + $client->expects()->request($client->wrapApi('pay/refundquery'), [$type => $orderNo])->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryRefund($orderNo, $type)); + } + + public function testQueryRefundByRefundNo() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['queryRefund', 'queryRefundByRefundNo'], $app)->makePartial(); + + $refundNo = 'foo'; + + $client->expects()->queryRefund($refundNo, Client::OUT_REFUND_NO)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryRefundByRefundNo($refundNo)); + } + + public function testQueryRefundByTransactionId() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['queryRefund', 'queryRefundByTransactionId'], $app)->makePartial(); + + $transactionId = 'foo'; + + $client->expects()->queryRefund($transactionId, Client::TRANSACTION_ID)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryRefundByTransactionId($transactionId)); + } + + public function testQueryRefundByRefundId() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['queryRefund', 'queryRefundByRefundId'], $app)->makePartial(); + + $refundId = 'foo'; + + $client->expects()->queryRefund($refundId, Client::REFUND_ID)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryRefundByRefundId($refundId)); + } + + public function testDownloadBill() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['downloadBill', 'getBody'], $app)->makePartial(); + + $data = 'foo'; + + $client->shouldReceive('request->getBody') + ->once() + ->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->downloadBill($data)); + } + + public function testReport() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, 'report', $app)->makePartial(); + + $api = 'foo'; + $timeConsuming = 1; + $resultCode = 'bar'; + $returnCode = 'baz'; + $optional = []; + + $params = array_merge([ + 'interface_url' => $api, + 'execute_time_' => $timeConsuming, + 'return_code' => $returnCode, + 'return_msg' => null, + 'result_code' => $resultCode, + 'user_ip' => Support\get_client_ip(), + 'time' => time(), + ], $optional); + + $client->expects()->request($client->wrapApi('payitil/report'), $params)->andReturn('mock-result')->once(); + + $this->assertSame('mock-result', $client->report($api, $timeConsuming, $resultCode, $returnCode, $optional)); + } + + public function testAuthCodeToOpenId() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, 'authCodeToOpenId', $app)->makePartial(); + + $authCode = 'foo'; + + $client->expects()->request('https://api.mch.weixin.qq.com/tools/authcodetoopenid', ['auth_code' => $authCode])->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->authCodeToOpenId($authCode)); + } + + public function testPrepends() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $mock = $this->mockApiClient(Client::class, 'prepends', $app)->makePartial(); + + $this->assertNotEmpty($mock->prepends()); + $this->assertArrayHasKey('appid', $mock->prepends()); + } +} diff --git a/tests/Payment/Coupon/ClientTest.php b/tests/Payment/Coupon/ClientTest.php new file mode 100644 index 000000000..293a8b80b --- /dev/null +++ b/tests/Payment/Coupon/ClientTest.php @@ -0,0 +1,69 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment\Coupon; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Payment\Coupon\Client; +use EasyWeChat\Tests\TestCase; + +class ClientTest extends TestCase +{ + public function testSend() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['send', 'safeRequest'], $app)->makePartial(); + + $params = ['foo' => 'bar']; + + $client->expects()->safeRequest('mmpaymkttransfers/send_coupon', array_merge($params, ['openid_count' => 1]))->once()->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->send($params)); + } + + public function testQueryStock() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['send', 'queryStock'], $app)->makePartial(); + + $params = ['foo' => 'bar']; + + $client->expects()->request('mmpaymkttransfers/query_coupon_stock', $params)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->queryStock($params)); + } + + public function testQuery() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['send', 'query'], $app)->makePartial(); + + $params = ['foo' => 'bar']; + + $client->expects()->request('mmpaymkttransfers/querycouponsinfo', $params)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->query($params)); + } + + public function testPrepends() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + ]); + + $client = $this->mockApiClient(Client::class, 'prepends', $app)->makePartial(); + + $this->assertNotEmpty($client->prepends()); + $this->assertArrayHasKey('mch_id', $client->prepends()); + $this->assertArrayHasKey('appid', $client->prepends()); + $this->assertSame($app['merchant']->merchant_id, $client->prepends()['mch_id']); + $this->assertSame($app['merchant']->app_id, $client->prepends()['appid']); + } +} diff --git a/tests/Payment/Jssdk/ClientTest.php b/tests/Payment/Jssdk/ClientTest.php new file mode 100644 index 000000000..5dddb333d --- /dev/null +++ b/tests/Payment/Jssdk/ClientTest.php @@ -0,0 +1,146 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment\Jssdk; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Tests\TestCase; +use EasyWeChat\Payment\Jssdk\Client; +use Overtrue\Socialite\AccessToken; + +class ClientTest extends TestCase +{ + public function testBridgeConfig() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $client = $this->mockApiClient(Client::class, 'bridgeConfig', $app)->makePartial(); + + $prepayId = 'foo'; + + // return json + $config = json_decode($client->bridgeConfig($prepayId, true), true); + $this->assertArrayHasKey('appId', $config); + $this->assertArrayHasKey('timeStamp', $config); + $this->assertArrayHasKey('nonceStr', $config); + $this->assertArrayHasKey('package', $config); + $this->assertArrayHasKey('signType', $config); + $this->assertArrayHasKey('paySign', $config); + $this->assertSame('123456', $config['appId']); + $this->assertSame("prepay_id=$prepayId", $config['package']); + $this->assertSame('MD5', $config['signType']); + + // return array + $config = $client->bridgeConfig($prepayId, false); + $this->assertArrayHasKey('appId', $config); + $this->assertArrayHasKey('timeStamp', $config); + $this->assertArrayHasKey('nonceStr', $config); + $this->assertArrayHasKey('package', $config); + $this->assertArrayHasKey('signType', $config); + $this->assertArrayHasKey('paySign', $config); + $this->assertSame('123456', $config['appId']); + $this->assertSame("prepay_id=$prepayId", $config['package']); + $this->assertSame('MD5', $config['signType']); + } + + public function testSdkConfig() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $client = $this->mockApiClient(Client::class, 'bridgeConfig, sdkConfig', $app)->makePartial(); + + $prepayId = 'foo'; + + $client->expects()->bridgeConfig($prepayId, false)->andReturn(['foo' => 'bar', 'timeStamp' => '123'])->twice(); + + $bridgeConfig = $client->bridgeConfig($prepayId, false); + $sdkConfig = $client->sdkConfig($prepayId); + + $this->assertArrayHasKey('timestamp', $sdkConfig); + $this->assertArrayNotHasKey('timeStamp', $sdkConfig); + $this->assertSame($sdkConfig['timestamp'], $bridgeConfig['timeStamp']); + } + + public function testAppConfig() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $client = $this->mockApiClient(Client::class, 'appConfig', $app)->makePartial(); + + $prepayId = 'foo'; + + $config = $client->appConfig($prepayId); + $this->assertArrayHasKey('appid', $config); + $this->assertArrayHasKey('partnerid', $config); + $this->assertArrayHasKey('prepayid', $config); + $this->assertArrayHasKey('noncestr', $config); + $this->assertArrayHasKey('timestamp', $config); + $this->assertArrayHasKey('package', $config); + $this->assertArrayHasKey('sign', $config); + + $this->assertSame($app['merchant']->app_id, $config['appid']); + $this->assertSame($app['merchant']->merchant_id, $config['partnerid']); + $this->assertSame($prepayId, $config['prepayid']); + $this->assertSame('Sign=WXPay', $config['package']); + } + + public function testShareAddressConfig() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $client = $this->mockApiClient(Client::class, 'shareAddressConfig', $app)->makePartial(); + + $accessTokenParams = ['access_token' => 'foo']; + + $mockAccessToken = \Mockery::mock(AccessToken::class.'[getToken]', [$accessTokenParams])->shouldAllowMockingProtectedMethods()->makePartial(); + $mockAccessToken->expects()->getToken()->andReturn('foo_access_token')->twice(); + + // return json + $config = json_decode($client->shareAddressConfig($mockAccessToken, true), true); + $this->assertArrayHasKey('appId', $config); + $this->assertArrayHasKey('scope', $config); + $this->assertArrayHasKey('timeStamp', $config); + $this->assertArrayHasKey('nonceStr', $config); + $this->assertArrayHasKey('signType', $config); + $this->assertArrayHasKey('addrSign', $config); + $this->assertSame('123456', $config['appId']); + $this->assertSame('jsapi_address', $config['scope']); + $this->assertSame('SHA1', $config['signType']); + + // return array + $config = $client->shareAddressConfig($mockAccessToken, false); + $this->assertArrayHasKey('appId', $config); + $this->assertArrayHasKey('scope', $config); + $this->assertArrayHasKey('timeStamp', $config); + $this->assertArrayHasKey('nonceStr', $config); + $this->assertArrayHasKey('signType', $config); + $this->assertArrayHasKey('addrSign', $config); + $this->assertSame('123456', $config['appId']); + $this->assertSame('jsapi_address', $config['scope']); + $this->assertSame('SHA1', $config['signType']); + } +} diff --git a/tests/Payment/MerchantTest.php b/tests/Payment/MerchantTest.php new file mode 100644 index 000000000..80488add9 --- /dev/null +++ b/tests/Payment/MerchantTest.php @@ -0,0 +1,42 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment; + +use EasyWeChat\Payment\Merchant; +use EasyWeChat\Tests\TestCase; + +class MerchantTest extends TestCase +{ + public function testErrorParams() + { + try { + new Merchant(''); + $this->fail('No exception thrown.'); + } catch (\Throwable $e) { + $this->assertInstanceOf(\TypeError::class, $e); + } + + try { + new Merchant(1); + $this->fail('No exception thrown.'); + } catch (\Throwable $e) { + $this->assertInstanceOf(\TypeError::class, $e); + } + } + + public function testAttributes() + { + $merchant = new Merchant(['foo' => 'bar']); + + $this->assertSame('bar', $merchant->foo); + } +} diff --git a/tests/Payment/NotifyTest.php b/tests/Payment/NotifyTest.php new file mode 100644 index 000000000..4ee646609 --- /dev/null +++ b/tests/Payment/NotifyTest.php @@ -0,0 +1,110 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment; + +use EasyWeChat\Payment\Merchant; +use EasyWeChat\Payment\Notify; +use EasyWeChat\Tests\TestCase; +use EasyWeChat\Kernel\Support\Collection; +use EasyWeChat\Kernel\Support\XML; +use Symfony\Component\HttpFoundation\Request; + +class NotifyTest extends TestCase +{ + public function testIsValid() + { + $merchant = new Merchant([ + 'merchant_id' => '123456', + 'key' => 'foo', + ]); + + $notify = \Mockery::mock(Notify::class.'[getNotify, isValid]', [$merchant])->makePartial(); + + $mockResult = new Collection([ + 'foo' => 'bar', + 'bax' => 123, + 'sign' => 'barsign', + ]); + + $notify->expects()->getNotify()->andReturn($mockResult)->twice(); + + $this->assertFalse($notify->isValid()); + } + + public function testGetNotify() + { + $merchant = new Merchant([ + 'merchant_id' => '123456', + 'key' => 'foo', + ]); + + // test notify already has non-empty value. + $notify = new DummnyClassForNotiryTest($merchant); + $notify->setNotify(new Collection(['foo' => 'bar'])); + + $notifyResult = $notify->getNotify(); + + $this->assertNotEmpty($notifyResult); + $this->assertInstanceOf(Collection::class, $notifyResult); + $this->assertSame('bar', $notifyResult->foo); + + // teset normal. + $xmlString = XML::build([ + 'foo' => 'bar', + ]); + $request = new Request([], [], [], [], [], [], $xmlString); + + $notify = new Notify($merchant, $request); + + $notifyResult = $notify->getNotify(); + + $this->assertNotEmpty($notifyResult); + $this->assertInstanceOf(Collection::class, $notifyResult); + $this->assertSame('bar', $notifyResult->foo); + + // test invalid request XML. (can't be parsed) + $xmlString = 'foo'; + $request = new Request([], [], [], [], [], [], $xmlString); + + $notify = new Notify($merchant, $request); + + try { + $notify->getNotify(); + $this->fail('No eaception is thrown.'); + } catch (\Throwable $e) { + $this->assertStringStartsWith('Invalid request XML:', $e->getMessage()); + $this->assertSame(400, $e->getCode()); + } + + // test invalid request XML. (empty for example) + $xmlString = XML::build([]); + $request = new Request([], [], [], [], [], [], $xmlString); + + $notify = new Notify($merchant, $request); + + try { + $notify->getNotify(); + $this->fail('No eaception is thrown.'); + } catch (\Throwable $e) { + $this->assertSame('Invalid request XML.', $e->getMessage()); + $this->assertSame(400, $e->getCode()); + } + } +} + +class DummnyClassForNotiryTest extends Notify +{ + public function setNotify(Collection $notify) + { + $this->notify = $notify; + } +} \ No newline at end of file diff --git a/tests/Payment/OrderTest.php b/tests/Payment/OrderTest.php new file mode 100644 index 000000000..f5806abf0 --- /dev/null +++ b/tests/Payment/OrderTest.php @@ -0,0 +1,42 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment; + +use EasyWeChat\Payment\Order; +use EasyWeChat\Tests\TestCase; + +class OrderTest extends TestCase +{ + public function testErrorParams() + { + try { + new Order(''); + $this->fail('No exception thrown.'); + } catch (\Throwable $e) { + $this->assertInstanceOf(\TypeError::class, $e); + } + + try { + new Order(1); + $this->fail('No exception thrown.'); + } catch (\Throwable $e) { + $this->assertInstanceOf(\TypeError::class, $e); + } + } + + public function testAttributes() + { + $order = new Order(['foo' => 'bar']); + + $this->assertSame('bar', $order->foo); + } +} diff --git a/tests/Payment/Redpack/ClientTest.php b/tests/Payment/Redpack/ClientTest.php new file mode 100644 index 000000000..67b67cc9e --- /dev/null +++ b/tests/Payment/Redpack/ClientTest.php @@ -0,0 +1,136 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment\Redpack; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Payment\Redpack\Client; +use EasyWeChat\Kernel\Support; +use EasyWeChat\Tests\TestCase; + +class ClientTest extends TestCase +{ + public function testPrepare() + { + $app = new Application([ + 'app_id' => 'wx123456', + ]); + $client = $this->mockApiClient(Client::class, ['prepare', 'safeRequest'], $app)->makePartial(); + + $params = [ + 'foo' => 'bar', + ]; + + $paramsForSafeRequest = array_merge($params, [ + 'wxappid' => $app['merchant']->app_id, + 'auth_mchid' => '1000052601', + 'auth_appid' => 'wxbf42bd79c4391863', + 'amt_type' => 'ALL_RAND', + ]); + + $client->expects()->safeRequest('mmpaymkttransfers/hbpreorder', $paramsForSafeRequest)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->prepare($params)); + } + + public function testQuery() + { + $app = new Application([ + 'app_id' => 'wx123456', + ]); + $client = $this->mockApiClient(Client::class, ['query', 'safeRequest'], $app)->makePartial(); + + $mchBillNo = '123456'; + + $params = [ + 'appid' => $app['merchant']->app_id, + 'mch_billno' => $mchBillNo, + 'bill_type' => 'MCHT', + ]; + + $client->expects()->safeRequest('mmpaymkttransfers/gethbinfo', $params)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->query($mchBillNo)); + + } + + public function testSend() + { + $app = new Application([ + 'app_id' => 'wx123456', + ]); + $client = $this->mockApiClient(Client::class, ['send', 'safeRequest'], $app)->makePartial(); + + $params = [ + 'client_id' => '162.168.0.1', + ]; + + $paramsForSafeRequest = array_merge($params, [ + 'wxappid' => $app['merchant']->app_id, + ]); + + // type === 'NORMAL' + $type = Client::TYPE_NORMAL; + $endpoint = 'mmpaymkttransfers/sendredpack'; + + $client->expects()->safeRequest($endpoint, $paramsForSafeRequest)->andReturn('mock-result-normal')->once(); + + $this->assertSame('mock-result-normal', $client->send($params, $type)); + + // type === 'GROUP' + $type = Client::TYPE_GROUP; + $endpoint = 'mmpaymkttransfers/sendgroupredpack'; + unset($paramsForSafeRequest['client_ip']); + + $client->expects()->safeRequest($endpoint, $paramsForSafeRequest)->andReturn('mock-result-group')->once(); + + $this->assertSame('mock-result-group', $client->send($params, $type)); + } + + public function testSendNormal() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['send', 'sendNormal'], $app)->makePartial(); + + $params = []; + + $paramsDefault = [ + 'total_num' => 1, + 'client_ip' => $params['client_ip'] ?? Support\get_server_ip(), + ]; + + $client->expects()->send(array_merge($params, $paramsDefault), Client::TYPE_NORMAL)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->sendNormal($params)); + } + + public function testSendGroup() + { + $app = new Application(); + $client = $this->mockApiClient(Client::class, ['send', 'sendGroup'], $app)->makePartial(); + + $params = []; + + $client->expects()->send(array_merge($params, ['amt_type' => 'ALL_RAND']), Client::TYPE_GROUP)->andReturn('mock-result')->once(); + $this->assertSame('mock-result', $client->sendGroup($params)); + } + + public function testPrepends() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + ]); + + $client = $this->mockApiClient(Client::class, 'prepends', $app)->makePartial(); + + $this->assertNotEmpty($client->prepends()); + $this->assertArrayHasKey('mch_id', $client->prepends()); + $this->assertSame('foo-merchant-id', $client->prepends()['mch_id']); + } +} diff --git a/tests/Payment/Traits/HandleNotiryTest.php b/tests/Payment/Traits/HandleNotiryTest.php new file mode 100644 index 000000000..77e808b24 --- /dev/null +++ b/tests/Payment/Traits/HandleNotiryTest.php @@ -0,0 +1,176 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment\Traits; + +use EasyWeChat\Kernel\Exceptions\Exception; +use EasyWeChat\Kernel\Http\Response; +use EasyWeChat\Kernel\Support\Collection; +use EasyWeChat\Payment\Application; +use EasyWeChat\Payment\Notify; +use EasyWeChat\Payment\Traits\HandleNotify; +use EasyWeChat\Tests\TestCase; + +class HandleNotiryTest extends TestCase +{ + /** + * Make Application. + * + * @param array $config + */ + private function makeApp($config = []) + { + return new Application(array_merge([ + 'merchant' => [ + 'id' => 'foo', + 'merchant_id' => 'bar', + 'sub_appid' => 'foo_sub_appid', + 'sub_mch_id' => 'foo_sub_mch_id', + ], + ], $config)); + } + + /** + * A callback for handleNotify(). + * + * @param $notify + * @param $successful + * + * @return mixed + */ + public function handleNotifyCallback($notify, $successful) + { + return $successful; + } + + /** + * A callback for handleScanNotify(). + * + * @param $produceId + * @param $openid + * @param $notify + * + * @return string + * @throws \Exception + */ + public function handleScanNotifyCallback($produceId, $openid, $notify) + { + if ($produceId) { + return 'foo'; + } + + throw new Exception('No product_id given.'); + } + + public function testHandleNotify() + { + $app = $this->makeApp(); + + $mock = \Mockery::mock(DummnyClassForHandleNotifyTest::class.'[getNotify, handleNotify]', [$app])->shouldAllowMockingProtectedMethods()->makePartial(); + + $mockNotify = \Mockery::mock(Notify::class, [$app['merchant']]); + + // mock Notify. + $mockNotify->shouldReceive('isValid') + ->times(3) + ->andReturn(false, true, true); + + $mockNotify->shouldReceive('getNotify') + ->twice() + ->andReturn( + new Collection(['result_code' => 'SUCCESS']), + new Collection(['result_code' => 'FAILURE']) + ); + + $mock->shouldReceive('getNotify') + ->times(3) + ->andReturn($mockNotify); + + // $notify->isValid() === false + try { + $mock->handleNotify([$this, 'handleNotifyCallback']); + $this->fail('No expection was thrown.'); + } catch (\Exception $e) { + $this->assertSame('Invalid request payloads.', $e->getMessage()); + $this->assertSame(400, $e->getCode()); + } + + // result_code === 'SUCCESS' + $this->assertInstanceOf(Response::class, $mock->handleNotify([$this, 'handleNotifyCallback'])); + + // result_code === 'SUCCESS' + $this->assertInstanceOf(Response::class, $mock->handleNotify([$this, 'handleNotifyCallback'])); + } + + public function testHandleScanNotify() + { + $app = $this->makeApp(); + + $mock = \Mockery::mock(DummnyClassForHandleNotifyTest::class.'[getNotify, handleScanNotify]', [$app])->shouldAllowMockingProtectedMethods()->makePartial(); + + $mockNotify = \Mockery::mock(Notify::class, [$app['merchant']]); + + // mock Notify. + $mockNotify->shouldReceive('isValid') + ->times(3) + ->andReturn(false, true, true); + + $mockNotify->shouldReceive('getNotify') + ->twice() + ->andReturn( + new Collection(['product_id' => 'foo', 'openid' => 'wx12345']), + new Collection(['product_id' => null, 'oepnid' => null]) + ); + + $mock->shouldReceive('getNotify') + ->times(3) + ->andReturn($mockNotify); + + // $notify->isValid() === false + try { + $mock->handleScanNotify([$this, 'handleScanNotifyCallback']); + $this->fail('No expection was thrown.'); + } catch (\Exception $e) { + $this->assertSame('Invalid request payloads.', $e->getMessage()); + $this->assertSame(400, $e->getCode()); + } + + // product_id and openid is valid. + $this->assertInstanceOf(Response::class, $mock->handleScanNotify([$this, 'handleScanNotifyCallback'])); + + // product_id or oepndid is not valid. + $this->assertInstanceOf(Response::class, $mock->handleScanNotify([$this, 'handleScanNotifyCallback'])); + } + + public function testGetNotify() + { + $app = $this->makeApp(); + + $mock = \Mockery::mock(DummnyClassForHandleNotifyTest::class.'[getNotify]', [$app])->shouldAllowMockingProtectedMethods()->makePartial(); + + $this->assertInstanceOf(Notify::class, $mock->getNotify()); + } +} + +class DummnyClassForHandleNotifyTest +{ + use HandleNotify; + + public $app; + + /** + * DummnyClassForWorksInSandboxTest constructor. + */ + public function __construct(Application $app) + { + $this->app = $app; + } +} diff --git a/tests/Payment/Traits/WorkInSandboxTest.php b/tests/Payment/Traits/WorkInSandboxTest.php new file mode 100644 index 000000000..80071b1bf --- /dev/null +++ b/tests/Payment/Traits/WorkInSandboxTest.php @@ -0,0 +1,181 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment\Traits; + +use EasyWeChat\Kernel\Exceptions\InvalidArgumentException; +use EasyWeChat\Kernel\Support\XML; +use EasyWeChat\Payment\Traits\WorksInSandbox; +use EasyWeChat\Tests\TestCase; +use EasyWeChat\Payment\Application; + +class WorkInSandboxTest extends TestCase +{ + public function testSandboxMode() + { + $app = new Application(); + $mock = \Mockery::mock(DummnyClassForWorksInSandboxTest::class."[sadboxMode]", [$app]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $this->assertInstanceOf(DummnyClassForWorksInSandboxTest::class, $mock->sandboxMode()); + $this->assertInstanceOf(DummnyClassForWorksInSandboxTest::class, $mock->sandboxMode(true)); + + $this->assertFalse($mock->sandboxMode()->inSandbox); + $this->assertTrue($mock->sandboxMode(true)->inSandbox); + $this->assertFalse($mock->sandboxMode(false)->inSandbox); + } + + public function testWrapApi() + { + $app = new Application(); + $mock = \Mockery::mock(DummnyClassForWorksInSandboxTest::class."[sandboxMode, wrapApi]", [$app]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $this->assertSame('foo', $mock->wrapApi('foo')); + $this->assertSame('foo', $mock->sandboxMode()->wrapApi('foo')); + $this->assertSame('sandboxnew/foo', $mock->sandboxMode(true)->wrapApi('foo')); + } + + public function testGetSignKey() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $mock = \Mockery::mock(DummnyClassForWorksInSandboxTest::class.'[getSignKey, getSandboxSignKey]', [$app]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $mock->expects()->getSandboxSignKey()->andReturn('mock-sign-key')->once(); + + // inSandbox === false + $this->assertSame($mock->app['merchant']->key, $mock->sandboxMode(false)->getSignKey('foo')); + + // inSandbox === true + $this->assertSame('mock-sign-key', $mock->sandboxMode(true)->getSignKey('foo')); + + // $api === $this->signKeyEndpoint + $this->assertSame($mock->app['merchant']->key, $mock->sandboxMode(false)->getSignKey('sandboxnew/pay/getsignkey')); + } + + public function testGetSandboxSignKey() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $mock = \Mockery::mock(DummnyClassForWorksInSandboxTest::class.'[getSandboxSignKey, getSignKeyFromServer, getCache]', [$app]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $mock->expects()->getSignKeyFromServer()->andReturn('mock-signkey')->once(); + + // clear cache. + $mock->getCache()->clear(); + + $this->assertSame('mock-signkey', $mock->getSandboxSignKey()); + + // $this->signKey has a value and no cached value. + $mock->signKey = 'foo_sign_key'; + $this->assertSame('foo_sign_key', $mock->getSandboxSignKey()); + + // $this->signKey === null but has cached value. + $mock->signKey = null; + $mock->getCache()->set($mock->getCacheKey(), 'sign-key-in-cache'); + $this->assertSame('sign-key-in-cache', $mock->getSandboxSignKey()); + } + + public function testGetSignKeyFromServer() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $mock = \Mockery::mock(DummnyClassForWorksInSandboxTest::class.'[requestRaw, getSignKeyFromServer]', [$app]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + // return_code === SUCCESS + $successXmlString = XML::build([ + 'return_code' => 'SUCCESS', + 'return_msg' => 'SUCCESS', + 'sandbox_signkey' => 'bar_signkey', + ]); + + // return_code === FAILURE + $failureXmlString = XML::build([ + 'return_code' => 'FAILURE', + 'return_msg' => 'failure msg', + ]); + + $mock->shouldReceive('requestRaw->getBody') + ->twice() + ->andReturn($successXmlString, $failureXmlString); + + $this->assertSame('bar_signkey', $mock->getSignKeyFromServer()); + + try { + $mock->getSignKeyFromServer(); + + } catch (\Exception $e) { + $this->assertInstanceOf(InvalidArgumentException::class, $e); + $this->assertSame('failure msg', $e->getMessage()); + } + } + + public function testGetCacheKey() + { + $app = new Application([ + 'app_id' => '123456', + 'merchant_id' => 'foo-merchant-id', + 'key' => 'key123456', + ]); + + $mock = \Mockery::mock(DummnyClassForWorksInSandboxTest::class.'[getCacheKey]', [$app]) + ->shouldAllowMockingProtectedMethods() + ->makePartial(); + + $this->assertStringStartsWith('easywechat.payment.sandbox.', $mock->getCacheKey()); + } +} + +class DummnyClassForWorksInSandboxTest +{ + use WorksInSandbox; + + public $app; + + /** + * DummnyClassForWorksInSandboxTest constructor. + */ + public function __construct(Application $app) + { + $this->app = $app; + } + + public function __get($propertyName) + { + return $this->$propertyName ?? null; + } + + public function __set($propertyName, $value) + { + $this->$propertyName = $value; + } +} diff --git a/tests/Payment/Transfer/ClientTest.php b/tests/Payment/Transfer/ClientTest.php new file mode 100644 index 000000000..a1d423e55 --- /dev/null +++ b/tests/Payment/Transfer/ClientTest.php @@ -0,0 +1,66 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace EasyWeChat\Tests\Payment\Transfer; + +use EasyWeChat\Payment\Application; +use EasyWeChat\Tests\TestCase; +use EasyWeChat\Payment\Transfer\Client; + +class ClientTest extends TestCase +{ + public function testQuery() + { + $app = new Application([ + 'app_id' => 'wx123456', + 'merchant_id' => 'foo-merchant-id', + ]); + $client = $this->mockApiClient(Client::class, ['query', 'safeRequest'], $app)->makePartial(); + + $mchBillNo = 'foo'; + + $params = [ + 'appid' => $app['merchant']->app_id, + 'mch_id' => $app['merchant']->merchant_id, + 'partner_trade_no' => $mchBillNo, + ]; + + $client->expects()->safeRequest('mmpaymkttransfers/gettransferinfo', $params) + ->once() + ->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->query($mchBillNo)); + } + + public function testSend() + { + $app = new Application([ + 'app_id' => 'wx123456', + 'merchant_id' => 'foo-merchant-id', + ]); + $client = $this->mockApiClient(Client::class, ['send', 'safeRequest'], $app)->makePartial(); + + $params = [ + 'foo' => 'bar', + ]; + + $paramsForSafeRequest = array_merge($params, [ + 'mchid' => $app['merchant']->merchant_id, + 'mch_appid' => $app['merchant']->app_id, + ]); + + $client->expects()->safeRequest('mmpaymkttransfers/promotion/transfers', $paramsForSafeRequest) + ->once() + ->andReturn('mock-result'); + + $this->assertSame('mock-result', $client->send($params)); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3a1282dbd..6d17cdc5b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -13,5 +13,3 @@ define('STUBS_ROOT', __DIR__.'/stubs'); include __DIR__.'/../vendor/autoload.php'; - -Mockery::globalHelpers();