diff --git a/src/Bot/HttpClient.php b/src/Bot/HttpClient.php index a9ce032..491da2e 100644 --- a/src/Bot/HttpClient.php +++ b/src/Bot/HttpClient.php @@ -114,16 +114,22 @@ class HttpClient */ public function makeRequest($path, $method, $request = null, $serializeTo = Serializer::S_JSON) { + $getParameters = ''; + $this->validateMethod($method); if (!is_null($request)) { $this->validateRequest($request); } + if ($method == self::METHOD_GET) { + $getParameters = Url::buildGetParameters(Serializer::serialize($request, Serializer::S_ARRAY)); + } + $parameters = is_null($request) ? null : Serializer::serialize($request, $serializeTo); $request = new Request( $method, - \sprintf("%s%s", $this->basePath, $path), + \sprintf("%s%s%s", $this->basePath, $path, $getParameters), [ 'Content-Type' => 'application/json', 'X-Bot-Token' => $this->token diff --git a/src/Bot/Model/Response/ListResponse.php b/src/Bot/Model/Response/ListResponse.php index 6fabeea..488f03b 100644 --- a/src/Bot/Model/Response/ListResponse.php +++ b/src/Bot/Model/Response/ListResponse.php @@ -94,6 +94,16 @@ class ListResponse implements \Iterator, \ArrayAccess { return $this->errors; } + /** + * `count()` alternative for ListResponse + * + * @return int + */ + public function count(): int + { + return count($this->items ?: []); + } + /** * Set offset * diff --git a/src/Url.php b/src/Url.php index c5423f2..2d82cc9 100644 --- a/src/Url.php +++ b/src/Url.php @@ -47,4 +47,28 @@ class Url return $url; } + + /** + * Convert request data to GET parameters + * + * @param array $params + * + * @return string + */ + public static function buildGetParameters(array $params) + { + $result = ''; + + foreach ($params as $param => $value) { + if (!is_array($value)) { + $result .= '&' . $param . '=' . $value; + } else { + foreach ($value as $subvalue) { + $result .= '&' . $param . '=' . $subvalue; + } + } + } + + return count($result) > 0 ? '?' . substr($result, 1) : ''; + } } diff --git a/tests/Bot/Test/TestCase.php b/tests/Bot/Test/TestCase.php index 027ae8f..1ede82c 100644 --- a/tests/Bot/Test/TestCase.php +++ b/tests/Bot/Test/TestCase.php @@ -16,6 +16,7 @@ namespace RetailCrm\Mg\Bot\Test; use PHPUnit\Framework\TestCase as BaseCase; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; +use GuzzleHttp\Psr7\Response; use RetailCrm\Mg\Bot\Client; /** @@ -57,4 +58,71 @@ class TestCase extends BaseCase empty($response) ? null : HandlerStack::create($mock) ); } + + /** + * Returns mocked GuzzleHttp response + * + * @param string|null $body + * @param int $statusCode + * + * @return Response + */ + public function getResponse(string $body = null, int $statusCode = 200) + { + return new Response( + $statusCode, + array_filter( + [ + 'Server' => 'openresty/1.13.6.2', + 'Date' => gmdate("D, d M Y H:m:s \G\M\T"), + 'Content-Type' => 'application/json; charset=utf-8', + 'Content-Length' => is_null($body) ? null : strlen($body), + 'Connection' => 'keep-alive', + 'Access-Control-Allow-Credentials' => 'true', + 'Access-Control-Allow-Headers' => 'X-Requested-With, Content-Type, X-Api-Key, X-Client-Token', + 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS' + ] + ), + $body + ); + } + + /** + * Generate and return mocked response. + * Response data should be stored in Resources directory as json file. + * Only file name (without extension or any other data) should be provided, + * e.g. `getJsonResponse('bots', 200)` + * + * @param string $jsonFile + * @param int $statusCode + * + * @return Response|null + */ + public function getJsonResponse(string $jsonFile, int $statusCode = 200) + { + $fileName = realpath(join(DIRECTORY_SEPARATOR, [__DIR__, '..', '..', 'Resources', "${jsonFile}.json"])); + + if (file_exists($fileName)) { + $json = file_get_contents($fileName); + json_decode($json, true); + + if (json_last_error() == JSON_ERROR_NONE) { + return $this->getResponse($json, $statusCode); + } + } else { + return null; + } + } + + /** + * Generate and return empty response + * + * @param int $statusCode + * + * @return Response|null + */ + public function getEmptyResponse(int $statusCode = 200) + { + return $this->getResponse(null, $statusCode); + } } diff --git a/tests/Bot/Tests/ClientListTest.php b/tests/Bot/Tests/ClientListTest.php index f498e7c..f31604d 100644 --- a/tests/Bot/Tests/ClientListTest.php +++ b/tests/Bot/Tests/ClientListTest.php @@ -14,7 +14,11 @@ namespace RetailCrm\Mg\Bot\Tests; use RetailCrm\Mg\Bot\Model\Constants; +use RetailCrm\Mg\Bot\Model\Entity\Channel; +use RetailCrm\Mg\Bot\Model\Entity\Chat\Chat; +use RetailCrm\Mg\Bot\Model\Entity\Chat\ChatMember; use RetailCrm\Mg\Bot\Model\Entity\Dialog; +use RetailCrm\Mg\Bot\Model\Entity\Message\Message; use RetailCrm\Mg\Bot\Model\Request; use RetailCrm\Mg\Bot\Model\Response; use RetailCrm\Mg\Bot\Test\TestCase; @@ -38,15 +42,21 @@ class ClientListTest extends TestCase */ public function testChannels() { - $client = self::getApiClient(); + $client = self::getApiClient( + null, + null, + false, + $this->getJsonResponse('channels') + ); $request = new Request\ChannelsRequest(); $request->setActive(true); - $request->setTypes([Constants::CHANNEL_TYPE_FACEBOOK, Constants::CHANNEL_TYPE_INSTAGRAM]); + $request->setTypes([Constants::CHANNEL_TYPE_TELEGRAM, Constants::CHANNEL_TYPE_INSTAGRAM]); $response = $client->channels($request); - print_r($response->get(0)); + static::assertEquals(4, $response->count(), "Incorrect channels count"); + static::assertTrue($response[0] instanceof Channel\Channel, "Incorrect channel instance"); } /** @@ -55,14 +65,20 @@ class ClientListTest extends TestCase */ public function testChats() { - $client = self::getApiClient(); + $client = self::getApiClient( + null, + null, + false, + $this->getJsonResponse('chats') + ); $request = new Request\ChatsRequest(); - $request->setChannelType(Constants::CHANNEL_TYPE_FACEBOOK); + $request->setChannelType(Constants::CHANNEL_TYPE_TELEGRAM); $response = $client->chats($request); - print_r($response); + static::assertEquals(2, $response->count(), "Incorrect chats count"); + static::assertTrue($response[0] instanceof Chat, "Incorrect chat instance"); } /** @@ -71,13 +87,18 @@ class ClientListTest extends TestCase */ public function testMembers() { - $client = self::getApiClient(); + $client = self::getApiClient( + null, + null, + false, + $this->getJsonResponse('members') + ); $request = new Request\MembersRequest(); - $response = $client->members($request); - print_r($response); + static::assertEquals(4, $response->count(), "Incorrect members count"); + static::assertTrue($response[0] instanceof ChatMember, "Incorrect member instance"); } /** @@ -86,7 +107,12 @@ class ClientListTest extends TestCase */ public function testMessages() { - $client = self::getApiClient(); + $client = self::getApiClient( + null, + null, + false, + $this->getJsonResponse('messages') + ); $request = new Request\MessagesRequest(); $request->setChannelType(Constants::CHANNEL_TYPE_INSTAGRAM); @@ -94,7 +120,8 @@ class ClientListTest extends TestCase $response = $client->messages($request); - print_r($response); + static::assertEquals(2, $response->count(), "Incorrect message count"); + static::assertTrue($response[0] instanceof Message, "Incorrect message instance"); } /** @@ -103,13 +130,17 @@ class ClientListTest extends TestCase */ public function testCommands() { - $client = self::getApiClient(); + $client = self::getApiClient( + null, + null, + false, + $this->getResponse('[]') + ); $request = new Request\CommandsRequest(); - $response = $client->commands($request); - print_r($response); + self::assertEquals(0, $response->count(), "Invalid commands count"); } /** @@ -118,7 +149,12 @@ class ClientListTest extends TestCase */ public function testBots() { - $client = self::getApiClient(); + $client = self::getApiClient( + null, + null, + false, + $this->getJsonResponse('bots') + ); $request = new Request\BotsRequest(); $request->setActive(1); @@ -126,7 +162,7 @@ class ClientListTest extends TestCase $data = $client->bots($request); - print_r($data[0]); + static::assertEquals(3, $data->count()); } /** diff --git a/tests/Resources/bots.json b/tests/Resources/bots.json new file mode 100644 index 0000000..728f667 --- /dev/null +++ b/tests/Resources/bots.json @@ -0,0 +1,46 @@ +[ + { + "id": 9, + "name": "Помощник", + "events": null, + "client_id": "identifier_1", + "avatar_url": "https://s3-s1.retailcrm.tech/eu-central-1/retailcrm-billing/images/5b927ad342366-bot-logo.svg", + "roles": null, + "created_at": "2018-09-13T07:38:02.988438Z", + "updated_at": "2019-06-13T12:56:36.346811Z", + "deactivated_at": null, + "is_active": true, + "is_self": true, + "is_system": false + }, + { + "id": 7, + "name": "Helper", + "events": null, + "client_id": "identifier_2", + "avatar_url": "https://test.te/static/logo.svg", + "roles": null, + "created_at": "2018-09-07T07:27:41.87424Z", + "updated_at": "2018-10-25T07:43:03.077956Z", + "deactivated_at": "2019-10-25T07:43:03.077956Z", + "is_active": false, + "is_self": false, + "is_system": false + }, + { + "id": 6, + "name": "Mira chat bot", + "events": null, + "client_id": "identifier_3", + "avatar_url": "https://s3.retailcrm.pro/eu-central-1/retailcrm-billing/images/5b97bfd983d2b-bot-logo-2.svg", + "roles": [ + "responsible" + ], + "created_at": "2018-09-06T13:40:02.956515Z", + "updated_at": "2018-09-12T06:15:32.858632Z", + "deactivated_at": null, + "is_active": true, + "is_self": false, + "is_system": false + } +] \ No newline at end of file diff --git a/tests/Resources/channels.json b/tests/Resources/channels.json new file mode 100644 index 0000000..c47ec97 --- /dev/null +++ b/tests/Resources/channels.json @@ -0,0 +1,178 @@ +[ + { + "id": 72, + "type": "telegram", + "name": "@testbot", + "settings": { + "status": { + "delivered": "send" + }, + "text": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_chars_count": 4096 + }, + "product": { + "creating": "receive", + "editing": "receive" + }, + "order": { + "creating": "receive", + "editing": "receive" + }, + "image": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 10 + }, + "file": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 1 + } + }, + "created_at": "2019-06-11T12:46:48.72241Z", + "updated_at": "2019-06-11T12:46:48.72241Z", + "activated_at": "2019-06-11T12:46:48.722086Z", + "deactivated_at": null, + "is_active": true + }, + { + "id": 71, + "type": "telegram", + "name": "@testbot", + "settings": { + "status": { + "delivered": "send" + }, + "text": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_chars_count": 4096 + }, + "product": { + "creating": "receive", + "editing": "receive" + }, + "order": { + "creating": "receive", + "editing": "receive" + }, + "image": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 10 + }, + "file": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 1 + } + }, + "created_at": "2019-06-11T12:41:44.660495Z", + "updated_at": "2019-06-11T12:46:42.094353Z", + "activated_at": "2019-06-11T12:41:44.660204Z", + "deactivated_at": "2019-06-11T12:46:42.094135Z", + "is_active": true + }, + { + "id": 70, + "type": "telegram", + "name": "@testbot", + "settings": { + "status": { + "delivered": "send" + }, + "text": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_chars_count": 4096 + }, + "product": { + "creating": "receive", + "editing": "receive" + }, + "order": { + "creating": "receive", + "editing": "receive" + }, + "image": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 10 + }, + "file": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 1 + } + }, + "created_at": "2019-06-11T12:38:24.322413Z", + "updated_at": "2019-06-11T12:41:42.335889Z", + "activated_at": "2019-06-11T12:38:24.322125Z", + "deactivated_at": "2019-06-11T12:41:42.335678Z", + "is_active": true + }, + { + "id": 69, + "type": "telegram", + "name": "@testbot", + "settings": { + "status": { + "delivered": "send" + }, + "text": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_chars_count": 4096 + }, + "product": { + "creating": "receive", + "editing": "receive" + }, + "order": { + "creating": "receive", + "editing": "receive" + }, + "image": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 10 + }, + "file": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 1 + } + }, + "created_at": "2019-06-11T12:33:08.676214Z", + "updated_at": "2019-06-11T12:38:19.615651Z", + "activated_at": "2019-06-11T12:33:08.676043Z", + "deactivated_at": "2019-06-11T12:38:19.615335Z", + "is_active": true + } +] \ No newline at end of file diff --git a/tests/Resources/chats.json b/tests/Resources/chats.json new file mode 100644 index 0000000..1bd6785 --- /dev/null +++ b/tests/Resources/chats.json @@ -0,0 +1,116 @@ +[ + { + "id": 30, + "avatar": "", + "name": "", + "channel": { + "id": 72, + "avatar": "", + "transport_id": 4, + "type": "fbmessenger", + "settings": { + "status": { + "delivered": "send" + }, + "text": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_chars_count": 4096 + }, + "product": { + "creating": "receive", + "editing": "receive" + }, + "order": { + "creating": "receive", + "editing": "receive" + }, + "image": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 10 + }, + "file": { + "creating": "both", + "editing": "both", + "quoting": "both", + "deleting": "receive", + "max_items_count": 1 + } + }, + "name": "testbot", + "is_active": true + }, + "members": null, + "customer": { + "id": 39, + "type": "customer", + "avatar": "", + "name": "Иван", + "username": "Иван", + "first_name": "Иван" + }, + "author_id": 0, + "last_message": null, + "last_activity": "2019-06-11T17:36:20+03:00", + "created_at": "2019-06-11T12:49:26.938879Z", + "updated_at": "2019-06-14T14:40:28.7111Z" + }, + { + "id": 29, + "avatar": "", + "name": "", + "channel": { + "id": 68, + "avatar": "", + "transport_id": 5, + "type": "fbmessenger", + "settings": { + "status": { + "delivered": "send", + "read": "send" + }, + "text": { + "creating": "both", + "quoting": "receive", + "max_chars_count": 2000 + }, + "product": { + "creating": "receive" + }, + "order": { + "creating": "receive" + }, + "image": { + "creating": "both", + "max_items_count": 10 + }, + "file": { + "creating": "both", + "max_items_count": 10 + } + }, + "name": "testbot", + "is_active": false + }, + "members": null, + "customer": { + "id": 38, + "type": "customer", + "avatar": "", + "name": "Пётр Петрович", + "username": "Пётр", + "first_name": "Пётр", + "last_name": "Петрович" + }, + "author_id": 0, + "last_message": null, + "last_activity": "2019-06-13T11:07:14+03:00", + "created_at": "2019-06-11T07:34:16.082957Z", + "updated_at": "2019-06-16T08:10:28.657972Z" + } +] \ No newline at end of file diff --git a/tests/Resources/members.json b/tests/Resources/members.json new file mode 100644 index 0000000..e3f8672 --- /dev/null +++ b/tests/Resources/members.json @@ -0,0 +1,38 @@ +[ + { + "id": 50, + "created_at": "2019-06-11T12:49:34.718596Z", + "updated_at": "2019-06-11T14:36:22.086823Z", + "is_author": false, + "state": "active", + "chat_id": 30, + "user_id": 12 + }, + { + "id": 49, + "created_at": "2019-06-11T12:49:26.967903Z", + "updated_at": "2019-06-11T12:49:34.723983Z", + "is_author": false, + "state": "kicked", + "chat_id": 30, + "user_id": 4 + }, + { + "id": 48, + "created_at": "2019-06-11T08:40:14.139024Z", + "updated_at": "2019-06-13T08:10:03.576754Z", + "is_author": false, + "state": "active", + "chat_id": 29, + "user_id": 12 + }, + { + "id": 47, + "created_at": "2019-06-11T07:34:16.117109Z", + "updated_at": "2019-06-11T08:40:14.145243Z", + "is_author": false, + "state": "kicked", + "chat_id": 29, + "user_id": 4 + } +] \ No newline at end of file diff --git a/tests/Resources/messages.json b/tests/Resources/messages.json new file mode 100644 index 0000000..dcd9b55 --- /dev/null +++ b/tests/Resources/messages.json @@ -0,0 +1,48 @@ +[ + { + "id": 3376, + "time": "2019-05-20T17:17:28+03:00", + "type": "text", + "scope": "public", + "chat_id": 26, + "is_read": false, + "is_edit": false, + "status": "seen", + "from": { + "id": 35, + "type": "customer", + "avatar": "", + "name": "Иванов Иван", + "username": "username", + "first_name": "Иванов Иван" + }, + "content": "Text", + "quote": null, + "channel_id": 61, + "created_at": "2019-05-20T14:17:28.025314Z", + "updated_at": "2019-05-20T14:20:57.05964Z" + }, + { + "id": 3373, + "time": "2019-05-20T15:24:52+03:00", + "type": "text", + "scope": "public", + "chat_id": 26, + "is_read": false, + "is_edit": false, + "status": "seen", + "from": { + "id": 35, + "type": "customer", + "avatar": "", + "name": "Иванов Иван", + "username": "username", + "first_name": "Иванов Иван" + }, + "content": "❤️", + "quote": null, + "channel_id": 61, + "created_at": "2019-05-20T12:24:52.478633Z", + "updated_at": "2019-05-20T12:24:52.585284Z" + } +] \ No newline at end of file