Refactoring

This commit is contained in:
Alex
2025-07-11 20:33:26 +03:00
parent 2b8a19cd3e
commit 0c91886ff0
19 changed files with 377 additions and 110 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
parameters:
level: 6
level: 8
paths:
- src
+16 -3
View File
@@ -12,6 +12,7 @@ use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Result;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use InvalidArgumentException;
use ReflectionException;
/**
* The main entry point for interacting with the Max Bot API.
@@ -63,6 +64,7 @@ class Api
* @throws ClientApiException
* @throws NetworkException
* @throws SerializationException
* @throws ReflectionException
*/
public function getBotInfo(): BotInfo
{
@@ -79,6 +81,7 @@ class Api
* @throws ClientApiException
* @throws NetworkException
* @throws SerializationException
* @throws ReflectionException
*/
public function getSubscriptions(): array
{
@@ -92,14 +95,19 @@ class Api
*
* @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.
* @param UpdateType[]|null $updateTypes List of update types.
*
* @return Result
*
* @throws ClientApiException
* @throws NetworkException
* @throws SerializationException
* @throws ReflectionException
*/
public function subscribe(
string $url,
?string $secret = null,
?array $update_types = null,
?array $updateTypes = null,
): Result {
return $this->modelFactory->createResult(
$this->client->request(
@@ -109,7 +117,7 @@ class Api
[
'url' => $url,
'secret' => $secret,
'update_types' => $update_types ? array_map(fn($type) => $type->value, $update_types) : null,
'update_types' => !empty($updateTypes) ? array_map(fn($type) => $type->value, $updateTypes) : null,
]
)
);
@@ -121,6 +129,11 @@ class Api
* @param string $url URL webhook.
*
* @return Result
*
* @throws ClientApiException
* @throws NetworkException
* @throws SerializationException
* @throws ReflectionException
*/
public function unsubscribe(string $url): Result
{
+18
View File
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_PROPERTY)]
final readonly class ArrayOf
{
/**
* @param class-string $class Class name (model or enum)
*/
public function __construct(public string $class)
{
}
}
+4
View File
@@ -6,8 +6,10 @@ namespace BushlanovDev\MaxMessengerBot;
use BushlanovDev\MaxMessengerBot\Exceptions\ForbiddenException;
use BushlanovDev\MaxMessengerBot\Exceptions\ClientApiException;
use BushlanovDev\MaxMessengerBot\Exceptions\MethodNotAllowedException;
use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException;
use BushlanovDev\MaxMessengerBot\Exceptions\NotFoundException;
use BushlanovDev\MaxMessengerBot\Exceptions\RateLimitExceededException;
use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException;
use BushlanovDev\MaxMessengerBot\Exceptions\UnauthorizedException;
use InvalidArgumentException;
@@ -121,6 +123,8 @@ final class Client implements ClientApiInterface
401 => new UnauthorizedException($errorMessage, $errorCode, $response),
403 => new ForbiddenException($errorMessage, $errorCode, $response),
404 => new NotFoundException($errorMessage, $errorCode, $response),
405 => new MethodNotAllowedException($errorMessage, $errorCode, $response),
429 => new RateLimitExceededException($errorMessage, $errorCode, $response),
default => new ClientApiException($errorMessage, $errorCode, $response, $statusCode),
};
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Exceptions;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class MethodNotAllowedException extends ClientApiException
{
public function __construct(
string $message,
string $errorCode,
?ResponseInterface $response,
?Throwable $previous = null,
) {
parent::__construct($message, $errorCode, $response, 405, $previous);
}
}
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Exceptions;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class RateLimitExceededException extends ClientApiException
{
public function __construct(
string $message,
string $errorCode,
?ResponseInterface $response,
?Throwable $previous = null,
) {
parent::__construct($message, $errorCode, $response, 429, $previous);
}
}
+5 -4
View File
@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Result;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use ReflectionException;
/**
* Creates DTOs from raw associative arrays returned by the API client.
@@ -20,6 +20,7 @@ class ModelFactory
* @param array<string, mixed> $data
*
* @return Result
* @throws ReflectionException
*/
public function createResult(array $data): Result
{
@@ -32,12 +33,10 @@ class ModelFactory
* @param array<string, mixed> $data
*
* @return BotInfo
* @throws ReflectionException
*/
public function createBotInfo(array $data): BotInfo
{
$data['commands'] = isset($data['commands']) && is_array($data['commands'])
? array_map([BotCommand::class, 'fromArray'], $data['commands']) : null;
return BotInfo::fromArray($data);
}
@@ -47,6 +46,7 @@ class ModelFactory
* @param array<string, mixed> $data
*
* @return Subscription
* @throws ReflectionException
*/
public function createSubscription(array $data): Subscription
{
@@ -59,6 +59,7 @@ class ModelFactory
* @param array<string, mixed> $data
*
* @return Subscription[]
* @throws ReflectionException
*/
public function createSubscriptions(array $data): array
{
+141 -5
View File
@@ -4,23 +4,159 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models;
use BackedEnum;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use ReflectionClass;
use ReflectionException;
use ReflectionNamedType;
use ReflectionProperty;
abstract readonly class AbstractModel
{
/**
* @param array<string, mixed> $data
*
* @return static
* @throws ReflectionException
*/
abstract public static function fromArray(array $data): static;
public static function fromArray(array $data): static
{
$reflectionClass = new ReflectionClass(static::class);
$constructorArgs = [];
foreach ($reflectionClass->getConstructor()?->getParameters() ?? [] as $param) {
$phpPropertyName = $param->getName();
$property = $reflectionClass->getProperty($phpPropertyName);
$jsonKey = self::toSnakeCase($phpPropertyName);
$rawValue = $data[$jsonKey] ?? null;
if (!array_key_exists($jsonKey, $data) && $param->isDefaultValueAvailable()) {
$constructorArgs[$phpPropertyName] = $param->getDefaultValue();
continue;
}
$constructorArgs[$phpPropertyName] = self::castValue($rawValue, $property);
}
return new static(...$constructorArgs); // @phpstan-ignore-line
}
/**
* @param mixed $value
* @param ReflectionProperty $property
*
* @return mixed
* @throws ReflectionException
*/
private static function castValue(mixed $value, ReflectionProperty $property): mixed
{
$type = $property->getType();
if (is_null($value) || !$type instanceof ReflectionNamedType) {
return $value;
}
$typeName = $type->getName();
if ($type->isBuiltin()) {
return match ($typeName) {
'int' => (int)$value,
'string' => (string)$value,
'bool' => (bool)$value,
'float' => (float)$value,
'array' => self::castArray($value, $property),
default => $value,
};
}
if (is_subclass_of($typeName, BackedEnum::class)) {
return $typeName::from($value);
}
if (is_subclass_of($typeName, self::class)) {
return $typeName::fromArray($value);
}
return $value;
}
/**
* @param mixed $value
* @param ReflectionProperty $property
*
* @return array<string, mixed>
* @throws ReflectionException
*/
private static function castArray(mixed $value, ReflectionProperty $property): array
{
if (!is_array($value)) {
return (array)$value;
}
$attributes = $property->getAttributes(ArrayOf::class);
if (empty($attributes)) {
return $value;
}
/** @var ArrayOf $arrayOfAttribute */
$arrayOfAttribute = $attributes[0]->newInstance();
$itemClassName = $arrayOfAttribute->class;
if (is_subclass_of($itemClassName, BackedEnum::class)) {
return array_map(fn($item) => $itemClassName::from($item), $value);
}
if (is_subclass_of($itemClassName, self::class)) {
return array_map(fn($item) => $itemClassName::fromArray($item), $value);
}
return $value;
}
/**
* @param string $input
*
* @return string
*/
private static function toSnakeCase(string $input): string
{
return strtolower((string)preg_replace('/(?<!^)[A-Z]/', '_$0', $input));
}
/**
* @return array<string, mixed>
* @throws ReflectionException
*/
public function toArray(): array
{
return array_map(function ($value) {
return $this->convertValue($value);
}, get_object_vars($this));
$reflectionClass = new ReflectionClass($this);
$properties = $reflectionClass->getProperties(ReflectionProperty::IS_PUBLIC);
$result = [];
foreach ($properties as $property) {
if (!$property->isInitialized($this)) {
continue;
}
$phpPropertyName = $property->getName();
$value = $property->getValue($this);
$jsonKey = self::toSnakeCase($phpPropertyName);
$result[$jsonKey] = $this->convertValue($value);
}
return $result;
}
/**
* @param mixed $value
*
* @return mixed
* @throws ReflectionException
*/
private function convertValue(mixed $value): mixed
{
if ($value instanceof AbstractModel) {
@@ -31,7 +167,7 @@ abstract readonly class AbstractModel
return array_map([$this, 'convertValue'], $value);
}
if ($value instanceof \BackedEnum) {
if ($value instanceof BackedEnum) {
return $value->value;
}
-11
View File
@@ -18,15 +18,4 @@ final readonly class BotCommand extends AbstractModel
public ?string $description,
) {
}
/**
* @inheritdoc
*/
public static function fromArray(array $data): static
{
return new static(
(string)$data['name'],
$data['description'] ? (string)$data['description'] : null
);
}
}
+17 -33
View File
@@ -4,53 +4,37 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
/**
* Information about the current bot.
*/
final readonly class BotInfo extends AbstractModel
{
/**
* @param int $user_id ID user.
* @param string $first_name User display name.
* @param string|null $last_name User's display last name.
* @param int $userId ID user.
* @param string $firstName User display name.
* @param string|null $lastName User's display last name.
* @param string|null $username Unique public name of the user, may be null if the user is not available or no name is set.
* @param bool $is_bot Is the user a bot.
* @param int $last_activity_time User last activity time in MAX (Unix time in milliseconds). May be irrelevant if the user has disabled the "online" status in the settings.
* @param bool $isBot Is the user a bot.
* @param int $lastActivityTime User last activity time in MAX (Unix time in milliseconds). May be irrelevant if the user has disabled the "online" status in the settings.
* @param string|null $description User description, may be null if the user has not filled it in (up to 16000 characters).
* @param string|null $avatar_url Avatar URL.
* @param string|null $full_avatar_url Larger Avatar URL.
* @param string|null $avatarUrl Avatar URL.
* @param string|null $fullAvatarUrl Larger Avatar URL.
* @param BotCommand[]|null $commands Commands supported by the bot (up to 32 elements).
*/
public function __construct(
public int $user_id,
public string $first_name,
public ?string $last_name,
public int $userId,
public string $firstName,
public ?string $lastName,
public ?string $username,
public bool $is_bot,
public int $last_activity_time,
public bool $isBot,
public int $lastActivityTime,
public ?string $description,
public ?string $avatar_url,
public ?string $full_avatar_url,
public ?string $avatarUrl,
public ?string $fullAvatarUrl,
#[ArrayOf(BotCommand::class)]
public ?array $commands,
) {
}
/**
* @inheritdoc
*/
public static function fromArray(array $data): static
{
return new static(
(int)$data['user_id'],
(string)$data['first_name'],
$data['last_name'] ? (string)$data['last_name'] : null,
$data['username'] ? (string)$data['username'] : null,
(bool)$data['is_bot'],
(int)$data['last_activity_time'],
$data['description'] ? (string)$data['description'] : null,
$data['avatar_url'] ? (string)$data['avatar_url'] : null,
$data['full_avatar_url'] ? (string)$data['full_avatar_url'] : null,
isset($data['commands']) && is_array($data['commands']) ? $data['commands'] : null,
);
}
}
-11
View File
@@ -18,15 +18,4 @@ final readonly class Result extends AbstractModel
public ?string $message,
) {
}
/**
* @inheritDoc
*/
public static function fromArray(array $data): static
{
return new static(
(bool)$data['success'],
$data['message'] ?? null,
);
}
}
+4 -23
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
/**
@@ -14,35 +15,15 @@ final readonly class Subscription extends AbstractModel
/**
* @param string $url URL webhook.
* @param int $time Unix-time of creating a subscription.
* @param UpdateType[]|null $update_types List of update types.
* @param UpdateType[]|null $updateTypes List of update types.
* @param string|null $version Version of the API.
*/
public function __construct(
public string $url,
public int $time,
public ?array $update_types,
#[ArrayOf(UpdateType::class)]
public ?array $updateTypes,
public ?string $version,
) {
}
/**
* @inheritdoc
*/
public static function fromArray(array $data): static
{
$updateTypes = null;
if (isset($data['update_types']) && is_array($data['update_types'])) {
$updateTypes = array_map(
fn(string $typeValue): UpdateType => UpdateType::from($typeValue),
$data['update_types'],
);
}
return new static(
(string)$data['url'],
(int)$data['time'],
$updateTypes,
$data['version'] ?? null,
);
}
}
+1 -1
View File
@@ -124,7 +124,7 @@ final class ApiTest extends TestCase
$this->assertIsArray($result);
$this->assertCount(1, $result);
$this->assertInstanceOf(Subscription::class, $result[0]);
$this->assertSame(UpdateType::MessageCreated, $result[0]->update_types[0]);
$this->assertSame(UpdateType::MessageCreated, $result[0]->updateTypes[0]);
}
#[Test]
+10 -1
View File
@@ -7,8 +7,10 @@ namespace BushlanovDev\MaxMessengerBot\Tests;
use BushlanovDev\MaxMessengerBot\Client;
use BushlanovDev\MaxMessengerBot\Exceptions\ClientApiException;
use BushlanovDev\MaxMessengerBot\Exceptions\ForbiddenException;
use BushlanovDev\MaxMessengerBot\Exceptions\MethodNotAllowedException;
use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException;
use BushlanovDev\MaxMessengerBot\Exceptions\NotFoundException;
use BushlanovDev\MaxMessengerBot\Exceptions\RateLimitExceededException;
use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException;
use BushlanovDev\MaxMessengerBot\Exceptions\UnauthorizedException;
use InvalidArgumentException;
@@ -224,10 +226,17 @@ final class ClientTest extends TestCase
public static function apiErrorProvider(): array
{
return [
'400 Bad Request' => [400, ClientApiException::class, 'bad.request', 'Invalid parameters'],
'401 Unauthorized' => [401, UnauthorizedException::class, 'verify.token', 'Invalid access_token'],
'403 Forbidden' => [403, ForbiddenException::class, 'access.denied', 'You don\'t have permissions'],
'404 Not Found' => [404, NotFoundException::class, 'not.found', 'Resource not found'],
'400 Bad Request' => [400, ClientApiException::class, 'bad.request', 'Invalid parameters'],
'405 Method Not Allowed' => [
405,
MethodNotAllowedException::class,
'method.not.allowed',
'Method not allowed',
],
'429 Rate Limit' => [429, RateLimitExceededException::class, 'rate.limit', 'Rate limit exceeded'],
'503 Service Unavailable' => [
503,
ClientApiException::class,
+4 -2
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\ModelFactory;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
@@ -20,6 +21,7 @@ use PHPUnit\Framework\TestCase;
#[UsesClass(BotCommand::class)]
#[UsesClass(Result::class)]
#[UsesClass(Subscription::class)]
#[UsesClass(ArrayOf::class)]
final class ModelFactoryTest extends TestCase
{
private ModelFactory $factory;
@@ -79,7 +81,7 @@ final class ModelFactoryTest extends TestCase
$botInfo = $this->factory->createBotInfo($rawData);
$this->assertInstanceOf(BotInfo::class, $botInfo);
$this->assertSame(12345, $botInfo->user_id);
$this->assertSame(12345, $botInfo->userId);
$this->assertIsArray($botInfo->commands);
$this->assertCount(2, $botInfo->commands);
@@ -130,6 +132,6 @@ final class ModelFactoryTest extends TestCase
$this->assertIsArray($subscriptions);
$this->assertCount(1, $subscriptions);
$this->assertInstanceOf(Subscription::class, $subscriptions[0]);
$this->assertSame(UpdateType::MessageCreated, $subscriptions[0]->update_types[0]);
$this->assertSame(UpdateType::MessageCreated, $subscriptions[0]->updateTypes[0]);
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(AbstractModel::class)]
#[CoversClass(ArrayOf::class)]
final class AbstractModelMappingTest extends TestCase
{
#[Test]
public function itCorrectlyCastsArrayOfEnums(): void
{
$result = DummyModelForMapping::fromArray([
'name' => 'Test With Enums',
'update_types' => ['message_created', 'bot_started'],
]);
$this->assertInstanceOf(DummyModelForMapping::class, $result);
$this->assertIsArray($result->updateTypes);
$this->assertCount(2, $result->updateTypes);
$this->assertInstanceOf(UpdateType::class, $result->updateTypes[0]);
$this->assertSame(UpdateType::MessageCreated, $result->updateTypes[0]);
$this->assertSame(UpdateType::BotStarted, $result->updateTypes[1]);
}
#[Test]
public function itCorrectlyCastsArrayOfModels(): void
{
$result = DummyModelForMapping::fromArray([
'name' => 'Test With Models',
'child_models' => [
['value' => 'child 1'],
['value' => 'child 2'],
],
]);
$this->assertIsArray($result->childModels);
$this->assertCount(2, $result->childModels);
$this->assertInstanceOf(DummyChildModel::class, $result->childModels[0]);
$this->assertSame('child 1', $result->childModels[0]->value);
}
#[Test]
public function itReturnsRawArrayWhenAttributeIsMissing(): void
{
$result = DummyModelForMapping::fromArray(['untyped_array' => ['a', 'b', 'c']]);
$this->assertIsArray($result->untypedArray);
$this->assertSame(['a', 'b', 'c'], $result->untypedArray);
}
#[Test]
public function itHandlesEmptyArraysCorrectly(): void
{
$result = DummyModelForMapping::fromArray([
'name' => 'Test with Empty',
'update_types' => [],
]);
$this->assertIsArray($result->updateTypes);
$this->assertEmpty($result->updateTypes);
}
#[Test]
public function itHandlesNullForNullableArray(): void
{
$result = DummyModelForMapping::fromArray(['child_models' => null]);
$this->assertNull($result->childModels);
}
}
final readonly class DummyModelForMapping extends AbstractModel
{
public function __construct(
public ?string $name,
#[ArrayOf(UpdateType::class)]
public ?array $updateTypes,
#[ArrayOf(DummyChildModel::class)]
public ?array $childModels,
public ?array $untypedArray,
)
{
}
}
final readonly class DummyChildModel extends AbstractModel
{
public function __construct(public string $value)
{
}
}
+2 -4
View File
@@ -38,12 +38,10 @@ final class BotCommandTest extends TestCase
#[Test]
public function canBeCreatedFromArrayWithOptionalDataNull(): void
{
$data = [
$command = BotCommand::fromArray([
'name' => 'help',
'description' => null,
];
$command = BotCommand::fromArray($data);
]);
$this->assertInstanceOf(BotCommand::class, $command);
$this->assertSame('help', $command->name);
+9 -9
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use PHPUnit\Framework\Attributes\CoversClass;
@@ -13,12 +14,13 @@ use PHPUnit\Framework\TestCase;
#[CoversClass(BotInfo::class)]
#[UsesClass(BotCommand::class)]
#[UsesClass(ArrayOf::class)]
final class BotInfoTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = [
$botInfo = BotInfo::fromArray([
'user_id' => 12345,
'first_name' => 'Test',
'last_name' => 'Bot',
@@ -29,17 +31,15 @@ final class BotInfoTest extends TestCase
'avatar_url' => 'http://example.com/avatar.jpg',
'full_avatar_url' => 'http://example.com/full_avatar.jpg',
'commands' => [
new BotCommand('start', 'Start the bot'),
new BotCommand('help', 'Show help'),
['name' => 'start', 'description' => 'Start the bot'],
['name' => 'help', 'description' => 'Show help'],
],
];
$botInfo = BotInfo::fromArray($data);
]);
$this->assertInstanceOf(BotInfo::class, $botInfo);
$this->assertSame(12345, $botInfo->user_id);
$this->assertSame('Test', $botInfo->first_name);
$this->assertTrue($botInfo->is_bot);
$this->assertSame(12345, $botInfo->userId);
$this->assertSame('Test', $botInfo->firstName);
$this->assertTrue($botInfo->isBot);
$this->assertCount(2, $botInfo->commands);
$this->assertInstanceOf(BotCommand::class, $botInfo->commands[0]);
$this->assertSame('start', $botInfo->commands[0]->name);
+5 -2
View File
@@ -4,13 +4,16 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Models\Subscription;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(Subscription::class)]
#[UsesClass(ArrayOf::class)]
final class SubscriptionTest extends TestCase
{
#[Test]
@@ -28,7 +31,7 @@ final class SubscriptionTest extends TestCase
$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([UpdateType::MessageCreated, UpdateType::BotStarted], $subscription->updateTypes);
$this->assertSame($data['version'], $subscription->version);
$array = $subscription->toArray();
@@ -52,7 +55,7 @@ final class SubscriptionTest extends TestCase
$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->updateTypes);
$this->assertNull($subscription->version);
$array = $subscription->toArray();