diff --git a/src/Api.php b/src/Api.php index 425722b..e70f5ae 100644 --- a/src/Api.php +++ b/src/Api.php @@ -4,13 +4,13 @@ declare(strict_types=1); namespace BushlanovDev\MaxMessengerBot; +use BushlanovDev\MaxMessengerBot\Enums\UpdateType; use BushlanovDev\MaxMessengerBot\Exceptions\ClientApiException; use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException; use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException; use BushlanovDev\MaxMessengerBot\Models\BotInfo; -use BushlanovDev\MaxMessengerBot\Models\ResultModel; +use BushlanovDev\MaxMessengerBot\Models\Result; use BushlanovDev\MaxMessengerBot\Models\Subscription; -use BushlanovDev\MaxMessengerBot\Models\SubscriptionRequestBody; use InvalidArgumentException; /** @@ -23,7 +23,7 @@ class Api { private const string METHOD_GET = 'GET'; private const string METHOD_POST = 'POST'; - // private const string METHOD_DELETE = 'DELETE'; + private const string METHOD_DELETE = 'DELETE'; private const string ACTION_ME = '/me'; private const string ACTION_SUBSCRIPTIONS = '/subscriptions'; @@ -90,22 +90,41 @@ class Api /** * Subscribes the bot to receive updates via WebHook. * - * @param SubscriptionRequestBody $body + * @param string $url URL webhook. + * @param string|null $secret Secret key for verifying the authenticity of requests. + * @param UpdateType[]|null $update_types List of update types. * - * @return ResultModel - * - * @throws ClientApiException - * @throws NetworkException - * @throws SerializationException + * @return Result */ - public function subscribe(SubscriptionRequestBody $body): ResultModel - { + public function subscribe( + string $url, + ?string $secret = null, + ?array $update_types = null, + ): Result { return $this->modelFactory->createResult( $this->client->request( self::METHOD_POST, self::ACTION_SUBSCRIPTIONS, [], - $body->toArray(), + compact('url', 'secret', 'update_types'), + ) + ); + } + + /** + * Unsubscribes bot from receiving updates via WebHook. + * + * @param string $url URL webhook. + * + * @return Result + */ + public function unsubscribe(string $url): Result + { + return $this->modelFactory->createResult( + $this->client->request( + self::METHOD_DELETE, + self::ACTION_SUBSCRIPTIONS, + compact('url'), ) ); } diff --git a/src/ModelFactory.php b/src/ModelFactory.php index a839116..b2d7662 100644 --- a/src/ModelFactory.php +++ b/src/ModelFactory.php @@ -6,7 +6,7 @@ namespace BushlanovDev\MaxMessengerBot; use BushlanovDev\MaxMessengerBot\Models\BotCommand; use BushlanovDev\MaxMessengerBot\Models\BotInfo; -use BushlanovDev\MaxMessengerBot\Models\ResultModel; +use BushlanovDev\MaxMessengerBot\Models\Result; use BushlanovDev\MaxMessengerBot\Models\Subscription; /** @@ -19,11 +19,11 @@ class ModelFactory * * @param array $data * - * @return ResultModel + * @return Result */ - public function createResult(array $data): ResultModel + public function createResult(array $data): Result { - return ResultModel::fromArray($data); + return Result::fromArray($data); } /** diff --git a/src/Models/ResultModel.php b/src/Models/Result.php similarity index 92% rename from src/Models/ResultModel.php rename to src/Models/Result.php index 71a74c3..468f24e 100644 --- a/src/Models/ResultModel.php +++ b/src/Models/Result.php @@ -7,7 +7,7 @@ namespace BushlanovDev\MaxMessengerBot\Models; /** * Simple response to request. */ -final readonly class ResultModel extends AbstractModel +final readonly class Result extends AbstractModel { /** * @param bool $success true if request was successful, false otherwise. diff --git a/src/Models/SubscriptionRequestBody.php b/src/Models/SubscriptionRequestBody.php deleted file mode 100644 index c98c5f3..0000000 --- a/src/Models/SubscriptionRequestBody.php +++ /dev/null @@ -1,45 +0,0 @@ - UpdateType::from($typeValue), - $data['update_types'], - ); - } - - return new static( - (string)$data['url'], - $data['secret'] ?? null, - $updateTypes, - $data['version'] ?? null, - ); - } -} diff --git a/tests/ApiTest.php b/tests/ApiTest.php index e8668a4..e2717d8 100644 --- a/tests/ApiTest.php +++ b/tests/ApiTest.php @@ -7,24 +7,31 @@ namespace BushlanovDev\MaxMessengerBot\Tests; use BushlanovDev\MaxMessengerBot\Api; use BushlanovDev\MaxMessengerBot\Client; use BushlanovDev\MaxMessengerBot\ClientApiInterface; +use BushlanovDev\MaxMessengerBot\Enums\UpdateType; use BushlanovDev\MaxMessengerBot\ModelFactory; use BushlanovDev\MaxMessengerBot\Models\BotInfo; +use BushlanovDev\MaxMessengerBot\Models\Subscription; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; +use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use ReflectionClass; #[CoversClass(Api::class)] -#[UsesClass(BotInfo::class)] #[UsesClass(Client::class)] +#[UsesClass(BotInfo::class)] +#[UsesClass(Subscription::class)] final class ApiTest extends TestCase { private MockObject&ClientApiInterface $clientMock; private MockObject&ModelFactory $modelFactoryMock; private Api $api; + /** + * @throws Exception + */ protected function setUp(): void { parent::setUp(); @@ -43,11 +50,9 @@ final class ApiTest extends TestCase $reflection = new ReflectionClass($api); $clientProp = $reflection->getProperty('client'); - $clientProp->setAccessible(true); $this->assertInstanceOf(ClientApiInterface::class, $clientProp->getValue($api)); $factoryProp = $reflection->getProperty('modelFactory'); - $factoryProp->setAccessible(true); $this->assertInstanceOf(ModelFactory::class, $factoryProp->getValue($api)); } @@ -56,7 +61,7 @@ final class ApiTest extends TestCase { $rawResponseData = ['user_id' => 123, 'first_name' => 'ApiTestBot']; - $expectedBotInfo = new BotInfo( + $botInfo = new BotInfo( 123, 'ApiTestBot', null, null, true, 0, null, null, null, null, @@ -72,10 +77,51 @@ final class ApiTest extends TestCase ->expects($this->once()) ->method('createBotInfo') ->with($rawResponseData) - ->willReturn($expectedBotInfo); + ->willReturn($botInfo); $result = $this->api->getBotInfo(); - $this->assertSame($expectedBotInfo, $result); + $this->assertSame($botInfo, $result); + } + + #[Test] + public function testSubscribeCallsClientAndFactoryCorrectly(): void + { + $rawResponseData = [ + 'subscriptions' => [ + [ + 'url' => 'https://example.com/webhook', + 'time' => 1678886400000, + 'update_types' => ['message_created'], + 'version' => '0.0.1', + ], + ], + ]; + + $subscription = new Subscription( + 'https://example.com/webhook', + 1678886400000, + [UpdateType::MessageCreated], + '0.0.1', + ); + + $this->clientMock + ->expects($this->once()) + ->method('request') + ->with('GET', '/subscriptions') + ->willReturn($rawResponseData); + + $this->modelFactoryMock + ->expects($this->once()) + ->method('createSubscriptions') + ->with($rawResponseData) + ->willReturn([$subscription]); + + $result = $this->api->getSubscriptions(); + + $this->assertIsArray($result); + $this->assertCount(1, $result); + $this->assertInstanceOf(Subscription::class, $result[0]); + $this->assertSame(UpdateType::MessageCreated, $result[0]->update_types[0]); } } diff --git a/tests/ModelFactoryTest.php b/tests/ModelFactoryTest.php index ff16018..94682ac 100644 --- a/tests/ModelFactoryTest.php +++ b/tests/ModelFactoryTest.php @@ -4,9 +4,12 @@ declare(strict_types=1); namespace BushlanovDev\MaxMessengerBot\Tests; +use BushlanovDev\MaxMessengerBot\Enums\UpdateType; use BushlanovDev\MaxMessengerBot\ModelFactory; use BushlanovDev\MaxMessengerBot\Models\BotCommand; use BushlanovDev\MaxMessengerBot\Models\BotInfo; +use BushlanovDev\MaxMessengerBot\Models\Result; +use BushlanovDev\MaxMessengerBot\Models\Subscription; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; @@ -15,6 +18,8 @@ use PHPUnit\Framework\TestCase; #[CoversClass(ModelFactory::class)] #[UsesClass(BotInfo::class)] #[UsesClass(BotCommand::class)] +#[UsesClass(Result::class)] +#[UsesClass(Subscription::class)] final class ModelFactoryTest extends TestCase { private ModelFactory $factory; @@ -25,6 +30,33 @@ final class ModelFactoryTest extends TestCase $this->factory = new ModelFactory(); } + #[Test] + public function createResultSuccessfully(): void + { + $rawData = ['success' => true]; + + $result = $this->factory->createResult($rawData); + + $this->assertInstanceOf(Result::class, $result); + $this->assertTrue($result->success); + $this->assertNull($result->message); + } + + #[Test] + public function createResultNotSuccessfully() + { + $rawData = [ + 'success' => false, + 'message' => 'error message', + ]; + + $result = $this->factory->createResult($rawData); + + $this->assertInstanceOf(Result::class, $result); + $this->assertFalse($result->success); + $this->assertSame('error message', $result->message); + } + #[Test] public function createBotInfoCorrectlyHydratesCommands(): void { @@ -78,4 +110,26 @@ final class ModelFactoryTest extends TestCase $this->assertInstanceOf(BotInfo::class, $botInfo); $this->assertNull($botInfo->commands); } + + #[Test] + public function createSubscriptions(): void + { + $rawData = [ + 'subscriptions' => [ + [ + 'url' => 'https://example.com/webhook', + 'time' => 1678886400000, + 'update_types' => ['message_created'], + 'version' => '0.0.1', + ], + ], + ]; + + $subscriptions = $this->factory->createSubscriptions($rawData); + + $this->assertIsArray($subscriptions); + $this->assertCount(1, $subscriptions); + $this->assertInstanceOf(Subscription::class, $subscriptions[0]); + $this->assertSame(UpdateType::MessageCreated, $subscriptions[0]->update_types[0]); + } } diff --git a/tests/Models/ResultTest.php b/tests/Models/ResultTest.php new file mode 100644 index 0000000..9141dea --- /dev/null +++ b/tests/Models/ResultTest.php @@ -0,0 +1,52 @@ + true, + 'message' => null, + ]; + $result = Result::fromArray($data); + + $this->assertInstanceOf(Result::class, $result); + $this->assertTrue($result->success); + $this->assertNull($result->message); + + $array = $result->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } + + #[Test] + public function canBeCreatedFromArrayWithNullMessage(): void + { + $data = [ + 'success' => false, + 'message' => 'error message', + ]; + $result = Result::fromArray($data); + + $this->assertInstanceOf(Result::class, $result); + $this->assertFalse($result->success); + $this->assertSame('error message', $result->message); + + $array = $result->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } +} diff --git a/tests/Models/SubscriptionTest.php b/tests/Models/SubscriptionTest.php new file mode 100644 index 0000000..bb82f4a --- /dev/null +++ b/tests/Models/SubscriptionTest.php @@ -0,0 +1,63 @@ + 'https://example.com/webhook', + 'time' => time(), + 'update_types' => [UpdateType::MessageCreated->value, UpdateType::BotStarted->value], + 'version' => '0.0.1', + ]; + + $subscription = Subscription::fromArray($data); + + $this->assertInstanceOf(Subscription::class, $subscription); + $this->assertSame($data['url'], $subscription->url); + $this->assertSame($data['time'], $subscription->time); + $this->assertSame([UpdateType::MessageCreated, UpdateType::BotStarted], $subscription->update_types); + $this->assertSame($data['version'], $subscription->version); + + $array = $subscription->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } + + #[Test] + public function canBeCreatedFromArrayWithOptionalDataNull(): void + { + $data = [ + 'url' => 'https://example.com/webhook', + 'time' => time(), + 'update_types' => null, + 'version' => null, + ]; + + $subscription = Subscription::fromArray($data); + + $this->assertInstanceOf(Subscription::class, $subscription); + $this->assertSame($data['url'], $subscription->url); + $this->assertSame($data['time'], $subscription->time); + $this->assertNull($subscription->update_types); + $this->assertNull($subscription->version); + + $array = $subscription->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } +}