mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-17 08:02:20 +00:00
Refactoring
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
||||
parameters:
|
||||
level: 6
|
||||
level: 8
|
||||
paths:
|
||||
- src
|
||||
|
||||
+16
-3
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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
@@ -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
@@ -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,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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user