diff --git a/.travis.yml b/.travis.yml index 16f7669..8b3b4d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,14 +19,20 @@ matrix: stages: - analyze - test + - integration jobs: exclude: - php: 7.2 include: - - if: branch = master + - stage: integration php: 7.0 - env: GROUP=integration + env: GROUP=integration TYPE=shop + secure: "gtOzOUYqUk1ucFKg0DtVdqzxov1nR/MiMS8vwsTc/ARSW0O4g38zYvAn61ZW1W81L41R2uqLChbaHls6SI/G3zEhPBgrfPTTr/bRCKZ+lfDfmtwRWUK0WB81/OUx7SGwIVULAaaZ3d8ti0RzsHz5NclaqlOM25XQjVzWfhPezTTfYX/DjCx5eNGMqDka74CsVj+pFk42DAW3wTH1GTTUWYTCk+U4qPEo+T29KAMmDS781YB283s9AmekUNIIo7Xf1j2GzyVdx1X7MlbBeWN+5Q+9g6wH458PmsM3LY+IzcNNcTiAS32H2WdKSMjokqjrZbSfBpXzQaX5SQRO+8RO3MgsKboGGODinxJX8PC3FUOZKOWUBtGLnqjoBjdsBJlvP2/CHqzHXWhPOQAmc0H3y/BpUnVd3mEowrQrEHG6SJtu7AVDri9j3elX5qEQjCt0HkvLAWfT5VqS1VF5VVIOSzgT6AwRAaa7+VQwZpFDP2DXnNLPmP3Ic9cyT+2IgwARNqSfEcroYRETKbNIyVasYTPR1RqazukA8iCw+QM04kJydL0A52TCGEV6x09DqQZLLsevD9oaR/LcHjKSnVXXAmtp3SMded4/CyMNHgMA2JAsTg2IHT0ZdO4A76mJYAtCbI3RdDc6Bbh8QsOwY0FCMCxc7Z6TomHHvJF4uXh9jPA=" + - stage: integration + php: 7.0 + env: GROUP=integration TYPE=delivery + secure: "Ph/Uj8bLsl0LrDmC77leACtwveVHacK959zQSCRJIDovUauX0t24eijAfF5qd0ki+g1qQjcgUa2vkak/A35bbMgnSLOdLUx+aZmC8rt3wi7pJoQa6Uc/beoUzrNvLF1Z7AHcJWhauPyM9cm/OGRl67Td2zNb10/CURjqWhsM2HDclkY2aIVMcWlj54euim6SEBCyDM5RIyiAsrATi++t2HvXzmPhfxanZ/girowL3ThlRc9fJGy2n9I1ItGzgkFUPFAdYu/UHcriQEaYTZYaqIsZL6WlVbgd989NPGt72eKObJLJX/CWVvm9pkc8nNC6EkyGjPaCUMuKX+XEnvRhJhYyDKgQY8c7EPANf7JE7TV5meFChvGiW21EKPw3bcrYCUJMGFRnUlU5jaN7nOzACgIKBrVvGobKHf+fV/H7p7nqkOT12+AI2mCSovcIbNY28TE1wtDWBAc3IazQzxcqGP+T0fbBiXGz/4KnyJzanLntVDrvH4aFK5nIxzXimd30HtKLmJRTHXdwSqdDBorRcWVfr+wdASXDUl+pTvYGpsiKyeY2TewLRcQ4gTACmSK8oSlZsCv2EpCDTIYyKewVm2VrYgNCIa85IzNhAM1LtgyY8UfFeswOCj720m/Zfy6qA20L+NQIMoE39k483GlWPRBni9FtyudKezhtxYxVbII=" - stage: analyze php: 7.2 install: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f316ed0..0f3b678 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,17 +12,19 @@ make -j # Интеграционные тесты -Для запуска интеграционных тестов нужно задать тестовые ключи, с которыми производится доступ: +Для запуска интеграционных тестов нужно задать тестовые ключи ([есть в документации](https://www.cdek.ru/clients/integrator.html)), с которыми производится доступ: ```bash export CDEK_ACCOUNT=..... export CDEK_PASSWORD=..... ``` -Также можно поменять путь до API, например, на вариант без https: +Тесты должны проходить как с ключами для ИМ, так и с ключами для доставки. + +Также можно задать путь до API, например, на вариант с https: ```bash -export CDEK_BASE_URL=http://integration.cdek.ru +export CDEK_BASE_URL=https://integration.edu.cdek.ru ``` Затем можно запускать тесты в режиме отладки: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d68a855..2c150e2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,7 @@ - + tests @@ -15,4 +17,7 @@ src/ + + + diff --git a/tests/Integration/CalculationRequestTest.php b/tests/Integration/CalculationRequestTest.php index 90bf426..bd4c65d 100644 --- a/tests/Integration/CalculationRequestTest.php +++ b/tests/Integration/CalculationRequestTest.php @@ -38,6 +38,8 @@ */ class CalculationRequestTest extends TestCase { + const UNAUTHORIZED_ERROR = 2; + public function test_success() { $request = new CalculationRequest(); @@ -81,6 +83,10 @@ public function test_authorized_success() $response = $this->getClient()->sendCalculationRequest($request); foreach ($response->getErrors() as $error) { + if ((int) $error->getErrorCode() === self::UNAUTHORIZED_ERROR) { + $this->skipIfTestEndpointIsUsed(); + } + $this->fail("{$error->getErrorCode()}: {$error->getMessage()}"); } diff --git a/tests/Integration/DeliveryRequestTest.php b/tests/Integration/DeliveryRequestTest.php index 1ba7822..67f11a1 100644 --- a/tests/Integration/DeliveryRequestTest.php +++ b/tests/Integration/DeliveryRequestTest.php @@ -104,7 +104,7 @@ public function test_delete_success() /** * @depends test_delete_success */ - public function test_failing_request_with_demo_keys() + public function test_successful_request_for_shop(): string { $order = new Order([ 'Number' => 'TEST-123456', @@ -174,15 +174,24 @@ public function test_failing_request_with_demo_keys() foreach ($response->getMessages() as $message) { $this->assertNotEmpty($message->getErrorCode(), $message->getMessage()); $this->assertSame('ERR_INVALID_TARIFF_WITH_CLIENT', $message->getErrorCode()); - break; + + // "Для выбранного тарифа клиент-плательщик должен быть задан и иметь ИМ-договор" + // Это означает что с нашими реквизитами нельзя создать заказ для ИМ + return ''; } + + $this->fail(); + + return ''; } /** - * @depends test_delete_success + * @depends test_successful_request_for_shop */ - public function test_successful_request() + public function test_successful_request_for_delivery(string $dispatchNumber): string { + // Попробуем создать заказ на доставку + $order = new Order([ 'ClientSide' => Order::CLIENT_SIDE_SENDER, 'Number' => 'TEST-123456', @@ -262,16 +271,37 @@ public function test_successful_request() } /** - * @depends test_failing_request_with_demo_keys + * @depends test_successful_request_for_shop + * @depends test_successful_request_for_delivery + */ + public function test_successful_request_any(string $dispatchNumberShop, string $dispatchNumberDelivery) + { + $dispatchNumber = $dispatchNumberShop !== '' ? $dispatchNumberShop : $dispatchNumberDelivery; + + $this->assertNotEmpty($dispatchNumber); + + return $dispatchNumber; + } + + /** + * @depends test_successful_request_any */ public function test_update_request(string $dispatchNumber) { + if ($dispatchNumber === '') { + $this->markTestSkipped('Используются реквизиты для доставки'); + } + $request = UpdateRequest::create([ 'Number' => self::TEST_NUMBER, ])->addOrder(Order::create([ 'DispatchNumber' => $dispatchNumber, 'Number' => 'TEST-123456', - ])->addPackage(Package::create([ + ])->setAddress(Address::create([ + 'Street' => 'Морозильная улица', + 'House' => '2', + 'Flat' => '101', + ]))->addPackage(Package::create([ 'Number' => 'TEST-123456', 'BarCode' => 'TEST-123456', 'Weight' => 600, // Общий вес (в граммах) @@ -291,18 +321,9 @@ public function test_update_request(string $dispatchNumber) $this->assertInstanceOf(UpdateResponse::class, $response); - $this->skipIfKnownAPIErrorCode($response); - - foreach ($response->getMessages() as $message) { - if ($message->getErrorCode() === 'ERR_INVALID_TARIFF_WITH_CLIENT') { - $this->markTestSkipped($message->getMessage()); - } - - // Случай когда БД СДЭК отстаёт - if ($message->getErrorCode() === 'ERR_ORDER_NOTFIND') { - $this->markTestSkipped($message->getMessage()); - } - } + $this->skipIfKnownAPIErrorCode($response, [ + 'ERR_ORDER_NOTFIND', // Случай когда БД СДЭК отстаёт + ]); foreach ($response->getMessages() as $message) { $this->assertEmpty($message->getErrorCode(), $message->getMessage()); @@ -317,7 +338,7 @@ public function test_update_request(string $dispatchNumber) } /** - * @depends test_successful_request + * @depends test_successful_request_any */ public function test_print_receipts_request(string $dispatchNumber) { @@ -326,12 +347,12 @@ public function test_print_receipts_request(string $dispatchNumber) $response = $this->getClient()->sendPrintReceiptsRequest($request); + $this->skipIfKnownAPIErrorCode($response, [ + 'ERR_API', + ]); + if ($response->hasErrors()) { foreach ($response->getMessages() as $message) { - if ($message->getErrorCode() === 'ERR_API') { - $this->markTestSkipped($message->getMessage()); - } - // Новые заказы попадают в БД СДЭК с задержкой, потому квитанцию не всегда получается сразу получить if ($message->getErrorCode() === 'ERR_INVALID_DISPATCHNUMBER') { $this->assertContains($dispatchNumber, $message->getMessage()); @@ -351,7 +372,7 @@ public function test_print_receipts_request(string $dispatchNumber) } /** - * @depends test_successful_request + * @depends test_successful_request_any * @psalm-suppress PossiblyNullReference */ public function test_status_report(string $dispatchNumber) @@ -378,9 +399,18 @@ public function test_status_report(string $dispatchNumber) $order = $response->getOrders()[0]; $this->assertInstanceOf(Order::class, $order); - $this->assertSame('TESTING123', $order->getActNumber()); + $this->assertSame($dispatchNumber, $order->getDispatchNumber()); $this->assertSame('Создан', $order->getStatus()->getDescription()); + if ($order->getActNumber() !== '' && !$this->isTestEndpointUsed()) { + $this->assertSame('TESTING123', $order->getActNumber()); + } + + if ($order->getNumber() === 'null' && $this->isTestEndpointUsed()) { + // Тестовое API иногда возвращает такое - тестирование продолжаем как можно + return Order::withDispatchNumber($order->getDispatchNumber()); + } + return Order::withNumberAndDate($order->getNumber(), $order->getStatus()->getDate()); } @@ -436,7 +466,7 @@ public function test_info_report(Order $order) } /** - * @depends test_successful_request + * @depends test_successful_request_any */ public function test_call_courier(string $dispatchNumber) { diff --git a/tests/Integration/RegionsRequestTest.php b/tests/Integration/RegionsRequestTest.php index 1043bdf..38e253d 100644 --- a/tests/Integration/RegionsRequestTest.php +++ b/tests/Integration/RegionsRequestTest.php @@ -42,7 +42,7 @@ class RegionsRequestTest extends TestCase public function test_example() { $request = new RegionsRequest(); - $request->setCountryCode(1); + $request->setCountryCode('RU'); $request->setPage(0)->setSize(1); $response = $this->getClient()->sendRegionsRequest($request); @@ -53,6 +53,11 @@ public function test_example() $this->assertCount(1, $response->getItems()); $region = $response->getItems()[0]; + + if ($region->getCountryCode() === 0) { + $this->markTestSkipped("Unknown country: {$region->getCountryName()}"); + } + $this->assertSame('18aff43f-58b8-4608-ade7-92fdab7fc39f', $region->getUuid()); $this->assertSame('Тверская', $region->getName()); $this->assertSame('обл', $region->getPrefix()); diff --git a/tests/Integration/TestCase.php b/tests/Integration/TestCase.php index e8d6c25..b5b8c5a 100644 --- a/tests/Integration/TestCase.php +++ b/tests/Integration/TestCase.php @@ -34,9 +34,14 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase { + const TEST_HOST = 'integration.edu.cdek.ru'; + /** @var \CdekSDK\CdekClient */ private $client; + /** @var bool */ + private $isTesting = false; + /** * @psalm-suppress PossiblyFalseArgument * @psalm-suppress MixedArgument @@ -51,10 +56,18 @@ protected function setUp() $this->markTestSkipped('Integration testing disabled (CDEK_PASSWORD missing).'); } - $http = false === getenv('CDEK_BASE_URL') ? null : new GuzzleClient([ - 'base_uri' => getenv('CDEK_BASE_URL'), - 'verify' => !getenv('CI'), // Igonore SSL errors on the likes of Travis CI - ]); + if (false === getenv('CDEK_BASE_URL')) { + $http = null; + } else { + if (strpos(getenv('CDEK_BASE_URL'), self::TEST_HOST)) { + $this->isTesting = true; + } + + $http = new GuzzleClient([ + 'base_uri' => getenv('CDEK_BASE_URL'), + 'verify' => !getenv('CI'), // Igonore SSL errors on the likes of Travis CI + ]); + } $this->client = new CdekClient(getenv('CDEK_ACCOUNT'), getenv('CDEK_PASSWORD'), $http); @@ -63,22 +76,38 @@ protected function setUp() } } - protected function getClient(): CdekClient + final protected function getClient(): CdekClient { return $this->client; } - protected function skipIfKnownAPIErrorCode(Response $response) + final protected function skipIfKnownAPIErrorCode(Response $response, array $transientErrorCodes = []) { - if ($response->hasErrors()) { - foreach ($response->getMessages() as $message) { - if ($message->getErrorCode() === '502') { - $this->markTestSkipped("CDEK responded with an HTTP error code: {$message->getMessage()}"); - } - if ($message->getErrorCode() === 'ERROR_INTERNAL') { - $this->markTestSkipped("CDEK failed with an internal error: {$message->getMessage()}"); - } + if (!$response->hasErrors()) { + return; + } + + $transientErrorCodes[] = 'ERROR_INTERNAL'; + + foreach ($response->getMessages() as $message) { + if ($message->getErrorCode() === '502') { + $this->markTestSkipped("CDEK responded with an HTTP error code: {$message->getMessage()}"); } + if (in_array($message->getErrorCode(), $transientErrorCodes)) { + $this->markTestSkipped("CDEK failed with a known transient error code {$message->getErrorCode()}: {$message->getMessage()}"); + } + } + } + + final protected function isTestEndpointUsed(): bool + { + return $this->isTesting; + } + + final protected function skipIfTestEndpointIsUsed(string $message = '') + { + if ($this->isTestEndpointUsed()) { + $this->markTestSkipped($message); } } }