Added MessageStat & Attachments

This commit is contained in:
Alex
2025-08-04 18:48:51 +03:00
parent c5d55a5c0f
commit 728536f064
41 changed files with 1210 additions and 95 deletions
+88 -2
View File
@@ -5,11 +5,32 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Enums\InlineButtonType;
use BushlanovDev\MaxMessengerBot\Enums\MarkupType;
use BushlanovDev\MaxMessengerBot\Enums\ReplyButtonType;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\AbstractAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\AudioAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\AbstractInlineButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\CallbackButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\ChatButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\LinkButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\RequestContactButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\RequestGeoLocationButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\AbstractReplyButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendContactButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendGeoLocationButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendMessageButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ContactAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\DataAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\FileAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\InlineKeyboardAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\LocationAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\PhotoAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ReplyKeyboardAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ShareAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\StickerAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\VideoAttachment;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Chat;
use BushlanovDev\MaxMessengerBot\Models\ChatList;
@@ -142,15 +163,80 @@ class ModelFactory
*/
public function createAttachment(array $data): AbstractAttachment
{
return match (AttachmentType::tryFrom($data['type'] ?? '')) {
$attachmentType = AttachmentType::tryFrom($data['type'] ?? '');
if ($attachmentType === AttachmentType::ReplyKeyboard
&& isset($data['buttons']) && is_array($data['buttons'])) {
$data['buttons'] = array_map(
fn($rowOfButtons) => array_map([$this, 'createReplyButton'], $rowOfButtons),
$data['buttons'],
);
}
if ($attachmentType === AttachmentType::InlineKeyboard
&& isset($data['payload']['buttons']) && is_array($data['payload']['buttons'])) {
$data['payload']['buttons'] = array_map(
fn($rowOfButtons) => array_map([$this, 'createInlineButton'], $rowOfButtons),
$data['payload']['buttons']
);
}
return match ($attachmentType) {
AttachmentType::Data => DataAttachment::fromArray($data),
AttachmentType::Share => ShareAttachment::fromArray($data),
AttachmentType::Image => PhotoAttachment::fromArray($data),
AttachmentType::Video => VideoAttachment::fromArray($data),
AttachmentType::Audio => AudioAttachment::fromArray($data),
AttachmentType::File => FileAttachment::fromArray($data),
AttachmentType::Sticker => StickerAttachment::fromArray($data),
AttachmentType::Contact => ContactAttachment::fromArray($data),
AttachmentType::InlineKeyboard => InlineKeyboardAttachment::fromArray($data),
AttachmentType::ReplyKeyboard => ReplyKeyboardAttachment::fromArray($data),
AttachmentType::Location => LocationAttachment::fromArray($data),
default => throw new LogicException("Unknown or unsupported attachment type: " . ($data['type'] ?? 'none')),
};
}
/**
* Creates a specific ReplyButton model based on the 'type' field.
*
* @param array<string, mixed> $data
*
* @return AbstractReplyButton
* @throws ReflectionException
* @throws LogicException
*/
public function createReplyButton(array $data): AbstractReplyButton
{
return match (ReplyButtonType::tryFrom($data['type'] ?? '')) {
ReplyButtonType::Message => SendMessageButton::fromArray($data),
ReplyButtonType::UserContact => SendContactButton::fromArray($data),
ReplyButtonType::UserGeoLocation => SendGeoLocationButton::fromArray($data),
default => throw new LogicException(
'Unknown or unsupported Attachment type: ' . ($data['type'] ?? 'none')
'Unknown or unsupported reply button type: ' . ($data['type'] ?? 'none')
),
};
}
/**
* Creates a specific InlineButton model based on the 'type' field.
*
* @param array<string, mixed> $data
* @return AbstractInlineButton
* @throws ReflectionException
* @throws LogicException
*/
public function createInlineButton(array $data): AbstractInlineButton
{
return match (InlineButtonType::tryFrom($data['type'] ?? '')) {
InlineButtonType::Callback => CallbackButton::fromArray($data),
InlineButtonType::Link => LinkButton::fromArray($data),
InlineButtonType::RequestContact => RequestContactButton::fromArray($data),
InlineButtonType::RequestGeoLocation => RequestGeoLocationButton::fromArray($data),
InlineButtonType::Chat => ChatButton::fromArray($data),
default => throw new LogicException("Unknown or unsupported inline button type: " . ($data['type'] ?? 'none')),
};
}
/**
* List of messages.
*
+1 -1
View File
@@ -137,7 +137,7 @@ abstract readonly class AbstractModel
return $typeName::from($value);
}
if (is_subclass_of($typeName, self::class)) {
if (is_subclass_of($typeName, self::class) && is_array($value)) {
return $typeName::fromArray($value);
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\MediaAttachmentPayload;
final readonly class AudioAttachment extends AbstractAttachment
{
/**
* @param MediaAttachmentPayload $payload Audio attachment payload.
* @param string|null $transcription Audio transcription.
*/
public function __construct(
public MediaAttachmentPayload $payload,
public ?string $transcription,
) {
parent::__construct(AttachmentType::Audio);
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\ContactAttachmentPayload;
final readonly class ContactAttachment extends AbstractAttachment
{
/**
* @param ContactAttachmentPayload $payload Contact attachment payload.
*/
public function __construct(public ContactAttachmentPayload $payload)
{
parent::__construct(AttachmentType::Contact);
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\FileAttachmentPayload;
final readonly class FileAttachment extends AbstractAttachment
{
/**
* @param FileAttachmentPayload $payload File attachment payload.
* @param string $filename Uploaded file name.
* @param int $size File size in bytes.
*/
public function __construct(
public FileAttachmentPayload $payload,
public string $filename,
public int $size,
) {
parent::__construct(AttachmentType::File);
}
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\KeyboardPayload;
final readonly class InlineKeyboardAttachment extends AbstractAttachment
{
/**
* @param KeyboardPayload $payload Keyboard payload.
*/
public function __construct(public KeyboardPayload $payload) {
parent::__construct(AttachmentType::InlineKeyboard);
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
final readonly class LocationAttachment extends AbstractAttachment
{
/**
* @param float $latitude
* @param float $longitude
*/
public function __construct(
public float $latitude,
public float $longitude,
) {
parent::__construct(AttachmentType::Location);
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
use BushlanovDev\MaxMessengerBot\Models\User;
/**
* Payload of a contact attachment.
*/
final readonly class ContactAttachmentPayload extends AbstractModel
{
/**
* @param string|null $vcfInfo User info in VCF format.
* @param User|null $maxInfo User info if the contact is a Max user.
*/
public function __construct(
public ?string $vcfInfo,
public ?User $maxInfo,
) {
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
/**
* Payload for a file attachment.
*/
final readonly class FileAttachmentPayload extends AbstractModel
{
/**
* @param string $url Media attachment URL.
* @param string $token Token to reuse the same attachment in other messages.
*/
public function __construct(
public string $url,
public string $token,
) {
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\AbstractInlineButton;
/**
* Represents an inline keyboard structure.
*/
final readonly class KeyboardPayload extends AbstractModel
{
/**
* @param AbstractInlineButton[][] $buttons Two-dimensional array of buttons.
*/
public function __construct(public array $buttons)
{
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
/**
* Payload for media attachments like audio or video.
*/
final readonly class MediaAttachmentPayload extends AbstractModel
{
/**
* @param string $url Media attachment URL.
* @param string $token Token to reuse the same attachment in other messages.
*/
public function __construct(
public string $url,
public string $token,
) {
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
/**
* Payload for a photo attachment.
*/
final readonly class PhotoAttachmentPayload extends AbstractModel
{
/**
* @param string $url Media attachment URL.
* @param string $token Token to reuse the same attachment in other messages.
* @param int $photoId Unique identifier of this image.
*/
public function __construct(
public string $url,
public string $token,
public int $photoId,
) {
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
/**
* Payload for a sticker attachment.
*/
final readonly class StickerAttachmentPayload extends AbstractModel
{
/**
* @param string $url Media attachment URL.
* @param string $code Sticker identifier.
*/
public function __construct(
public string $url,
public string $code,
) {
}
}
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
/**
* Represents a video thumbnail image.
*/
final readonly class VideoThumbnail extends AbstractModel
{
/**
* @param string $url Media attachment URL.
*/
public function __construct(public string $url)
{
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentPayload;
final readonly class PhotoAttachment extends AbstractAttachment
{
/**
* @param PhotoAttachmentPayload $payload Photo attachment payload.
*/
public function __construct(public PhotoAttachmentPayload $payload)
{
parent::__construct(AttachmentType::Image);
}
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\AbstractReplyButton;
final readonly class ReplyKeyboardAttachment extends AbstractAttachment
{
/**
* @param AbstractReplyButton[][] $buttons
*/
public function __construct(public array $buttons) {
parent::__construct(AttachmentType::ReplyKeyboard);
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\StickerAttachmentPayload;
final readonly class StickerAttachment extends AbstractAttachment
{
/**
* @param StickerAttachmentPayload $payload Sticker attachment payload.
* @param int $width
* @param int $height
*/
public function __construct(
public StickerAttachmentPayload $payload,
public int $width,
public int $height,
) {
parent::__construct(AttachmentType::Sticker);
}
}
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Enums\AttachmentType;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\MediaAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\VideoThumbnail;
final readonly class VideoAttachment extends AbstractAttachment
{
/**
* @param MediaAttachmentPayload $payload Video attachment payload.
* @param VideoThumbnail|null $thumbnail Video thumbnail.
* @param int|null $width Video width.
* @param int|null $height Video height.
* @param int|null $duration Video duration in seconds.
*/
public function __construct(
public MediaAttachmentPayload $payload,
public ?VideoThumbnail $thumbnail,
public ?int $width,
public ?int $height,
public ?int $duration,
) {
parent::__construct(AttachmentType::Video);
}
}
+2
View File
@@ -16,6 +16,7 @@ final readonly class Message extends AbstractModel
* @param User|null $sender User who sent this message. Can be null if message has been posted on behalf of a channel.
* @param string|null $url Message public URL. Can be null for dialogs or non-public chats/channels.
* @param LinkedMessage|null $link Forwarded or replied message.
* @param MessageStat|null $stat Message statistics. Available only for channels.
*/
public function __construct(
public int $timestamp,
@@ -24,6 +25,7 @@ final readonly class Message extends AbstractModel
public ?User $sender,
public ?string $url,
public ?LinkedMessage $link,
public ?MessageStat $stat,
) {
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models;
/**
* Message statistics.
*/
final readonly class MessageStat extends AbstractModel
{
/**
* @param int $views Number of views.
*/
public function __construct(public int $views)
{
}
}
+195 -5
View File
@@ -9,9 +9,24 @@ use BushlanovDev\MaxMessengerBot\Enums\ChatAdminPermission;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\ModelFactory;
use BushlanovDev\MaxMessengerBot\Models\Attachments\AbstractAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\AbstractInlineButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\CallbackButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\ChatButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\LinkButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\RequestContactButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\RequestGeoLocationButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\AbstractReplyButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendContactButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendMessageButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\DataAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\InlineKeyboardAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\LocationAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\KeyboardPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentRequestPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\ShareAttachmentRequestPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\PhotoAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ReplyKeyboardAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ShareAttachment;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
@@ -40,6 +55,7 @@ use BushlanovDev\MaxMessengerBot\Models\VideoAttachmentDetails;
use BushlanovDev\MaxMessengerBot\Models\VideoUrls;
use LogicException;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
@@ -76,6 +92,21 @@ use PHPUnit\Framework\TestCase;
#[UsesClass(LinkMarkup::class)]
#[UsesClass(AbstractMarkup::class)]
#[UsesClass(StrongMarkup::class)]
#[UsesClass(PhotoAttachmentPayload::class)]
#[UsesClass(PhotoAttachment::class)]
#[UsesClass(AbstractReplyButton::class)]
#[UsesClass(SendContactButton::class)]
#[UsesClass(SendMessageButton::class)]
#[UsesClass(ReplyKeyboardAttachment::class)]
#[UsesClass(AbstractInlineButton::class)]
#[UsesClass(ChatButton::class)]
#[UsesClass(RequestContactButton::class)]
#[UsesClass(CallbackButton::class)]
#[UsesClass(RequestGeoLocationButton::class)]
#[UsesClass(LinkButton::class)]
#[UsesClass(LocationAttachment::class)]
#[UsesClass(InlineKeyboardAttachment::class)]
#[UsesClass(KeyboardPayload::class)]
final class ModelFactoryTest extends TestCase
{
private ModelFactory $factory;
@@ -540,6 +571,7 @@ final class ModelFactoryTest extends TestCase
'description' => null,
'image_url' => null,
],
['type' => 'image', 'payload' => ['photo_id' => 1, 'token' => 't', 'url' => 'u']]
],
'markup' => null,
],
@@ -548,17 +580,21 @@ final class ModelFactoryTest extends TestCase
];
$message = $this->factory->createMessage($rawData);
$attachments = $message->body->attachments;
$this->assertInstanceOf(Message::class, $message);
$this->assertInstanceOf(MessageBody::class, $message->body);
$this->assertIsArray($message->body->attachments);
$this->assertCount(2, $message->body->attachments);
$this->assertIsArray($attachments);
$this->assertCount(3, $attachments);
$this->assertInstanceOf(DataAttachment::class, $message->body->attachments[0]);
$this->assertSame('payload_from_reply_button', $message->body->attachments[0]->data);
$this->assertInstanceOf(DataAttachment::class, $attachments[0]);
$this->assertSame('payload_from_reply_button', $attachments[0]->data);
$this->assertInstanceOf(ShareAttachment::class, $message->body->attachments[1]);
$this->assertSame('Test Share', $message->body->attachments[1]->title);
$this->assertSame('Test Share', $attachments[1]->title);
$this->assertInstanceOf(PhotoAttachment::class, $attachments[2]);
$this->assertSame(1, $attachments[2]->payload->photoId);
}
#[Test]
@@ -598,4 +634,158 @@ final class ModelFactoryTest extends TestCase
$this->factory->createMarkupElement(['type' => 'brand_new_unsupported_type']);
}
#[Test]
public function createAttachmentCorrectlyHydratesReplyKeyboard(): void
{
$data = [
'type' => 'reply_keyboard',
'buttons' => [
[['type' => 'message', 'text' => 'Hello']],
[['type' => 'user_contact', 'text' => 'My Contact']]
]
];
$attachment = $this->factory->createAttachment($data);
$this->assertInstanceOf(ReplyKeyboardAttachment::class, $attachment);
$this->assertCount(2, $attachment->buttons);
$this->assertInstanceOf(SendMessageButton::class, $attachment->buttons[0][0]);
$this->assertInstanceOf(SendContactButton::class, $attachment->buttons[1][0]);
$this->assertSame('My Contact', $attachment->buttons[1][0]->text);
}
#[Test]
public function createReplyButtonThrowsExceptionForUnknownType(): void
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Unknown or unsupported reply button type: new_fancy_button');
$this->factory->createReplyButton(['type' => 'new_fancy_button']);
}
/**
* Data provider for successful inline button creation.
* @return array<string, array{0: array<string, mixed>, 1: class-string}>
*/
public static function inlineButtonProvider(): array
{
return [
'CallbackButton' => [
['type' => 'callback', 'text' => 'Press', 'payload' => 'p'],
CallbackButton::class,
],
'LinkButton' => [
['type' => 'link', 'text' => 'Visit', 'url' => 'https://a.com'],
LinkButton::class,
],
'RequestContactButton' => [
['type' => 'request_contact', 'text' => 'Share Contact'],
RequestContactButton::class,
],
'RequestGeoLocationButton' => [
['type' => 'request_geo_location', 'text' => 'Share Location', 'quick' => false],
RequestGeoLocationButton::class,
],
'ChatButton' => [
['type' => 'chat', 'text' => 'Join Chat', 'chat_title' => 'My Chat'],
ChatButton::class,
],
];
}
#[Test]
#[DataProvider('inlineButtonProvider')]
public function createInlineButtonSuccessfully(array $data, string $expectedClass): void
{
$button = $this->factory->createInlineButton($data);
$this->assertInstanceOf($expectedClass, $button);
$this->assertSame($data['text'], $button->text);
}
/**
* Data provider for invalid inline button data.
* @return array<string, array{0: array<string, mixed>, 1: string}>
*/
public static function invalidInlineButtonProvider(): array
{
return [
'unknown type' => [
['type' => 'unknown_button_type', 'text' => 'Unknown'],
'Unknown or unsupported inline button type: unknown_button_type',
],
'missing type' => [
['text' => 'No type here'],
'Unknown or unsupported inline button type: none',
],
];
}
#[Test]
#[DataProvider('invalidInlineButtonProvider')]
public function createInlineButtonThrowsExceptionForInvalidType(array $invalidData, string $expectedMessage): void
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage($expectedMessage);
$this->factory->createInlineButton($invalidData);
}
/**
* Data provider for testing various attachment types in createAttachment.
* @return array<string, array{0: array<string, mixed>, 1: class-string, 2: callable}>
*/
public static function attachmentTypeProvider(): array
{
return [
'Data Attachment' => [
['type' => 'data', 'data' => 'test_payload'],
DataAttachment::class,
function (TestCase $test, DataAttachment $attachment) {
$test->assertSame('test_payload', $attachment->data);
}
],
'Location Attachment' => [
['type' => 'location', 'latitude' => 55.751244, 'longitude' => 37.618423],
LocationAttachment::class,
function (TestCase $test, LocationAttachment $attachment) {
$test->assertSame(55.751244, $attachment->latitude);
}
],
'Inline Keyboard Attachment' => [
[
'type' => 'inline_keyboard',
'payload' => [
'buttons' => [
[['type' => 'callback', 'text' => 'Test', 'payload' => 'p']]
]
]
],
InlineKeyboardAttachment::class,
function (TestCase $test, InlineKeyboardAttachment $attachment) {
$test->assertInstanceOf(KeyboardPayload::class, $attachment->payload);
$test->assertIsArray($attachment->payload->buttons);
$test->assertInstanceOf(CallbackButton::class, $attachment->payload->buttons[0][0]);
$test->assertSame('p', $attachment->payload->buttons[0][0]->payload);
}
],
];
}
#[Test]
#[DataProvider('attachmentTypeProvider')]
public function createAttachmentSuccessfullyCreatesVariousTypes(
array $data,
string $expectedClass,
callable $assertionCallback,
): void {
$attachment = $this->factory->createAttachment($data);
$this->assertInstanceOf($expectedClass, $attachment);
$assertionCallback($this, $attachment);
}
}
+26
View File
@@ -7,9 +7,11 @@ namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
use DateTimeInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use stdClass;
#[CoversClass(AbstractModel::class)]
@@ -176,6 +178,30 @@ final class AbstractModelMappingTest extends TestCase
$this->assertEquals($expectedArray, $resultArray);
$this->assertArrayNotHasKey('uninitialized_prop', $resultArray);
}
#[Test]
public function castValueReturnsValueAsIsForUnmanagedScalarAssignedToObjectType(): void
{
$dateString = '2025-08-26';
$reflectionClass = new ReflectionClass(ModelForUnmanagedType::class);
$abstractModelReflection = $reflectionClass->getParentClass();
$castValueMethod = $abstractModelReflection->getMethod('castValue');
$castValueMethod->setAccessible(true);
$property = $reflectionClass->getProperty('eventDate');
$result = $castValueMethod->invoke(null, $dateString, $property);
$this->assertSame($dateString, $result);
}
}
final readonly class ModelForUnmanagedType extends AbstractModel
{
public function __construct(public DateTimeInterface $eventDate)
{
}
}
final readonly class DummyModelForUninitializedProperty extends AbstractModel
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\AudioAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\MediaAttachmentPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(AudioAttachment::class)]
#[UsesClass(MediaAttachmentPayload::class)]
final class AudioAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = ['type' => 'audio', 'payload' => ['url' => 'u', 'token' => 't'], 'transcription' => 'Hello world.'];
$attachment = AudioAttachment::fromArray($data);
$this->assertInstanceOf(AudioAttachment::class, $attachment);
$this->assertSame('Hello world.', $attachment->transcription);
}
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ContactAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\ContactAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\User;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(ContactAttachment::class)]
#[UsesClass(ContactAttachmentPayload::class)]
#[UsesClass(User::class)]
final class ContactAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = ['type' => 'contact', 'payload' => ['vcf_info' => 'vcf', 'max_info' => null]];
$attachment = ContactAttachment::fromArray($data);
$this->assertInstanceOf(ContactAttachment::class, $attachment);
$this->assertSame('vcf', $attachment->payload->vcfInfo);
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\FileAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\FileAttachmentPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(FileAttachment::class)]
#[UsesClass(FileAttachmentPayload::class)]
final class FileAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = [
'type' => 'file',
'payload' => ['url' => 'u', 'token' => 't'],
'filename' => 'doc.pdf',
'size' => 1024,
];
$attachment = FileAttachment::fromArray($data);
$this->assertInstanceOf(FileAttachment::class, $attachment);
$this->assertSame('doc.pdf', $attachment->filename);
$this->assertSame(1024, $attachment->size);
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\CallbackButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\InlineKeyboardAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\KeyboardPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(InlineKeyboardAttachment::class)]
#[UsesClass(KeyboardPayload::class)]
#[UsesClass(CallbackButton::class)]
final class InlineKeyboardAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = [
'type' => 'inline_keyboard',
'payload' => ['buttons' => [[['type' => 'callback', 'text' => 'Press', 'payload' => 'p']]]],
];
$attachment = InlineKeyboardAttachment::fromArray($data);
$this->assertInstanceOf(InlineKeyboardAttachment::class, $attachment);
$this->assertInstanceOf(KeyboardPayload::class, $attachment->payload);
$this->assertCount(1, $attachment->payload->buttons);
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\LocationAttachment;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(LocationAttachment::class)]
final class LocationAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = ['type' => 'location', 'latitude' => 55.75, 'longitude' => 37.61];
$attachment = LocationAttachment::fromArray($data);
$this->assertInstanceOf(LocationAttachment::class, $attachment);
$this->assertSame(55.75, $attachment->latitude);
}
}
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\ContactAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\User;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(ContactAttachmentPayload::class)]
#[UsesClass(User::class)]
final class ContactAttachmentPayloadTest extends TestCase
{
#[Test]
public function canBeCreatedAndSerialized(): void
{
$user = User::fromArray(
['user_id' => 101, 'first_name' => 'MaxUser', 'is_bot' => false, 'last_activity_time' => time()]
);
$payload = new ContactAttachmentPayload('vcf_info_string', $user);
$this->assertSame('vcf_info_string', $payload->vcfInfo);
$this->assertSame($user, $payload->maxInfo);
$this->assertEquals(['vcf_info' => 'vcf_info_string', 'max_info' => $user->toArray()], $payload->toArray());
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\FileAttachmentPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(FileAttachmentPayload::class)]
final class FileAttachmentPayloadTest extends TestCase
{
#[Test]
public function canBeCreatedAndSerialized(): void
{
$payload = new FileAttachmentPayload('https://example.com/doc.pdf', 'file_token_456');
$this->assertSame('https://example.com/doc.pdf', $payload->url);
$this->assertSame('file_token_456', $payload->token);
$this->assertEquals(['url' => 'https://example.com/doc.pdf', 'token' => 'file_token_456'], $payload->toArray());
}
}
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\ModelFactory;
use BushlanovDev\MaxMessengerBot\Models\Attachments\AbstractAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\AbstractInlineButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\CallbackButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Inline\LinkButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\InlineKeyboardAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\KeyboardPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(KeyboardPayload::class)]
#[UsesClass(ModelFactory::class)]
#[UsesClass(AbstractAttachment::class)]
#[UsesClass(AbstractInlineButton::class)]
#[UsesClass(CallbackButton::class)]
#[UsesClass(LinkButton::class)]
#[UsesClass(InlineKeyboardAttachment::class)]
final class KeyboardPayloadTest extends TestCase
{
#[Test]
public function canBeCreatedFromArrayViaFactory(): void
{
$data = [
'type' => 'inline_keyboard',
'payload' => [
'buttons' => [
[
['type' => 'callback', 'text' => 'Press Me', 'payload' => 'payload_123'],
],
[
['type' => 'link', 'text' => 'Docs', 'url' => 'https://dev.max.ru'],
['type' => 'callback', 'text' => 'Cancel', 'payload' => 'cancel_op'],
],
]
]
];
$factory = new ModelFactory();
$attachment = $factory->createAttachment($data);
$this->assertInstanceOf(InlineKeyboardAttachment::class, $attachment);
$this->assertInstanceOf(KeyboardPayload::class, $attachment->payload);
$buttons = $attachment->payload->buttons;
$this->assertCount(2, $buttons);
$this->assertCount(1, $buttons[0]);
$this->assertInstanceOf(CallbackButton::class, $buttons[0][0]);
$this->assertSame('payload_123', $buttons[0][0]->payload);
$this->assertCount(2, $buttons[1]);
$this->assertInstanceOf(LinkButton::class, $buttons[1][0]);
$this->assertSame('https://dev.max.ru', $buttons[1][0]->url);
$this->assertInstanceOf(CallbackButton::class, $buttons[1][1]);
$this->assertSame('cancel_op', $buttons[1][1]->payload);
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\MediaAttachmentPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(MediaAttachmentPayload::class)]
final class MediaAttachmentPayloadTest extends TestCase
{
#[Test]
public function canBeCreatedAndSerialized(): void
{
$payload = new MediaAttachmentPayload('https://example.com/video.mp4', 'video_token_123');
$this->assertSame('https://example.com/video.mp4', $payload->url);
$this->assertSame('video_token_123', $payload->token);
$this->assertEquals(
['url' => 'https://example.com/video.mp4', 'token' => 'video_token_123'],
$payload->toArray(),
);
}
}
@@ -4,105 +4,33 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentRequestPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoToken;
use InvalidArgumentException;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(PhotoAttachmentRequestPayload::class)]
#[UsesClass(PhotoToken::class)]
#[UsesClass(ArrayOf::class)]
#[CoversClass(PhotoAttachmentPayload::class)]
#[UsesClass(AbstractModel::class)]
final class PhotoAttachmentPayloadTest extends TestCase
{
#[Test]
public function canBeCreatedWithUrlOnly(): void
public function canBeCreatedAndSerialized(): void
{
$payload = new PhotoAttachmentRequestPayload(url: 'https://example.com/photo.jpg');
$this->assertSame('https://example.com/photo.jpg', $payload->url);
$this->assertNull($payload->token);
$this->assertNull($payload->photos);
$expectedArray = [
'url' => 'https://example.com/photo.jpg',
'token' => null,
'photos' => null,
$data = [
'photo_id' => 987654321,
'token' => 'some_received_photo_token',
'url' => 'https://cdn.max.ru/photos/image.jpg'
];
$this->assertEquals($expectedArray, $payload->toArray());
}
#[Test]
public function canBeCreatedWithTokenOnly(): void
{
$payload = new PhotoAttachmentRequestPayload(token: 'uploaded_token_abc');
$payload = PhotoAttachmentPayload::fromArray($data);
$this->assertSame('uploaded_token_abc', $payload->token);
$this->assertNull($payload->url);
$this->assertNull($payload->photos);
$this->assertInstanceOf(PhotoAttachmentPayload::class, $payload);
$this->assertSame(987654321, $payload->photoId);
$this->assertSame('some_received_photo_token', $payload->token);
$this->assertSame('https://cdn.max.ru/photos/image.jpg', $payload->url);
$expectedArray = [
'token' => 'uploaded_token_abc',
'url' => null,
'photos' => null,
];
$this->assertEquals($expectedArray, $payload->toArray());
}
#[Test]
public function canBeCreatedWithPhotosOnly(): void
{
$photos = [
new PhotoToken('token_1'),
new PhotoToken('token_2'),
];
$payload = new PhotoAttachmentRequestPayload(photos: $photos);
$this->assertSame($photos, $payload->photos);
$this->assertNull($payload->url);
$this->assertNull($payload->token);
$expectedArray = [
'photos' => [
['token' => 'token_1'],
['token' => 'token_2'],
],
'url' => null,
'token' => null,
];
$this->assertEquals($expectedArray, $payload->toArray());
}
/**
* Data provider for invalid constructor arguments.
*
* @return array<string, array{0: string|null, 1: string|null, 2: array|null}>
*/
public static function invalidPayloadProvider(): array
{
return [
'all null (no arguments)' => [null, null, null],
'url and token provided' => ['https://a.com', 'token123', null],
'url and photos provided' => ['https://a.com', null, [new PhotoToken('t')]],
'token and photos provided' => [null, 'token123', [new PhotoToken('t')]],
'all three arguments provided' => ['https://a.com', 'token123', [new PhotoToken('t')]],
];
}
#[Test]
#[DataProvider('invalidPayloadProvider')]
public function constructorThrowsExceptionForInvalidArguments(
?string $url,
?string $token,
?array $photos
): void {
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Provide exactly one of "url", "token", or "photos" for PhotoAttachmentRequestPayload.');
new PhotoAttachmentRequestPayload($url, $token, $photos);
$this->assertEquals($data, $payload->toArray());
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\StickerAttachmentPayload;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(StickerAttachmentPayload::class)]
final class StickerAttachmentPayloadTest extends TestCase
{
#[Test]
public function canBeCreatedAndSerialized(): void
{
$payload = new StickerAttachmentPayload('https://example.com/sticker.webp', 'sticker_code_abc');
$this->assertSame('https://example.com/sticker.webp', $payload->url);
$this->assertSame('sticker_code_abc', $payload->code);
$this->assertEquals(
['url' => 'https://example.com/sticker.webp', 'code' => 'sticker_code_abc'],
$payload->toArray(),
);
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments\Payloads;
use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\VideoThumbnail;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(VideoThumbnail::class)]
#[UsesClass(AbstractModel::class)]
final class VideoThumbnailTest extends TestCase
{
#[Test]
public function canBeCreatedAndSerialized(): void
{
$data = [
'url' => 'https://example.com/video_thumbnail.jpg',
];
$thumbnail = VideoThumbnail::fromArray($data);
$this->assertInstanceOf(VideoThumbnail::class, $thumbnail);
$this->assertSame('https://example.com/video_thumbnail.jpg', $thumbnail->url);
$this->assertEquals($data, $thumbnail->toArray());
$directInstance = new VideoThumbnail('https://example.com/video_thumbnail.jpg');
$this->assertSame('https://example.com/video_thumbnail.jpg', $directInstance->url);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\PhotoAttachment;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(PhotoAttachment::class)]
#[UsesClass(PhotoAttachmentPayload::class)]
final class PhotoAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = ['type' => 'image', 'payload' => ['photo_id' => 1, 'token' => 't', 'url' => 'u']];
$attachment = PhotoAttachment::fromArray($data);
$this->assertInstanceOf(PhotoAttachment::class, $attachment);
$this->assertInstanceOf(PhotoAttachmentPayload::class, $attachment->payload);
$this->assertSame(1, $attachment->payload->photoId);
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\ModelFactory;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\AbstractReplyButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendContactButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Buttons\Reply\SendMessageButton;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ReplyKeyboardAttachment;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(ReplyKeyboardAttachment::class)]
#[UsesClass(AbstractReplyButton::class)]
#[UsesClass(SendContactButton::class)]
#[UsesClass(SendMessageButton::class)]
#[UsesClass(ModelFactory::class)]
final class ReplyKeyboardAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArrayViaFactory(): void
{
$data = [
'type' => 'reply_keyboard',
'buttons' => [
[['type' => 'message', 'text' => 'Hello']],
[['type' => 'user_contact', 'text' => 'My Contact']]
]
];
$factory = new ModelFactory();
$attachment = $factory->createAttachment($data);
$this->assertInstanceOf(ReplyKeyboardAttachment::class, $attachment);
$this->assertCount(2, $attachment->buttons);
$this->assertInstanceOf(SendContactButton::class, $attachment->buttons[1][0]);
$this->assertSame('My Contact', $attachment->buttons[1][0]->text);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\StickerAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\StickerAttachment;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(StickerAttachment::class)]
#[UsesClass(StickerAttachmentPayload::class)]
final class StickerAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = ['type' => 'sticker', 'payload' => ['url' => 'u', 'code' => 'c'], 'width' => 128, 'height' => 128];
$attachment = StickerAttachment::fromArray($data);
$this->assertInstanceOf(StickerAttachment::class, $attachment);
$this->assertSame(128, $attachment->width);
$this->assertSame('c', $attachment->payload->code);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Attachments;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\MediaAttachmentPayload;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\VideoThumbnail;
use BushlanovDev\MaxMessengerBot\Models\Attachments\VideoAttachment;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(VideoAttachment::class)]
#[UsesClass(MediaAttachmentPayload::class)]
#[UsesClass(VideoThumbnail::class)]
final class VideoAttachmentTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$data = [
'type' => 'video',
'payload' => ['url' => 'u', 'token' => 't'],
'thumbnail' => ['url' => 'thumb_url'],
'width' => 1920,
'height' => 1080,
'duration' => 120,
];
$attachment = VideoAttachment::fromArray($data);
$this->assertInstanceOf(VideoAttachment::class, $attachment);
$this->assertInstanceOf(MediaAttachmentPayload::class, $attachment->payload);
$this->assertInstanceOf(VideoThumbnail::class, $attachment->thumbnail);
$this->assertSame(120, $attachment->duration);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Models\MessageStat;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
#[CoversClass(MessageStat::class)]
final class MessageStatTest extends TestCase
{
#[Test]
public function canBeCreatedAndSerialized(): void
{
$data = ['views' => 1234];
$stat = MessageStat::fromArray($data);
$this->assertInstanceOf(MessageStat::class, $stat);
$this->assertSame(1234, $stat->views);
$this->assertEquals($data, $stat->toArray());
}
}
+7
View File
@@ -7,6 +7,7 @@ namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Models\LinkedMessage;
use BushlanovDev\MaxMessengerBot\Models\Message;
use BushlanovDev\MaxMessengerBot\Models\MessageBody;
use BushlanovDev\MaxMessengerBot\Models\MessageStat;
use BushlanovDev\MaxMessengerBot\Models\Recipient;
use BushlanovDev\MaxMessengerBot\Models\User;
use PHPUnit\Framework\Attributes\CoversClass;
@@ -19,6 +20,7 @@ use PHPUnit\Framework\TestCase;
#[UsesClass(Recipient::class)]
#[UsesClass(User::class)]
#[UsesClass(LinkedMessage::class)]
#[UsesClass(MessageStat::class)]
final class MessageTest extends TestCase
{
#[Test]
@@ -59,6 +61,9 @@ final class MessageTest extends TestCase
'sender' => null,
'chat_id' => null,
],
'stat' => [
'views' => 500,
],
];
$message = Message::fromArray($data);
@@ -69,6 +74,8 @@ final class MessageTest extends TestCase
$this->assertInstanceOf(Recipient::class, $message->recipient);
$this->assertInstanceOf(User::class, $message->sender);
$this->assertSame($data['url'], $message->url);
$this->assertInstanceOf(MessageStat::class, $message->stat);
$this->assertSame(500, $message->stat->views);
$array = $message->toArray();
+1
View File
@@ -84,6 +84,7 @@ final class WebhookHandlerTest extends TestCase
null,
null,
null,
null,
);
return new MessageCreatedUpdate(