Update tests and Throw InvalidArgumentException when building an empty comment body

This commit is contained in:
Alex
2026-10-01 11:26:19 +03:00
parent 093fafaff4
commit 21f6cb9e83
4 changed files with 223 additions and 4 deletions
+15 -4
View File
@@ -894,7 +894,7 @@ class Api
?int $count = null,
): array {
$query = [
'comment_ids' => $commentIds !== null ? implode(',', $commentIds) : null,
'comment_ids' => ($commentIds === null || $commentIds === []) ? null : implode(',', $commentIds),
'before' => $before,
'after' => $after,
'count' => $count,
@@ -938,10 +938,11 @@ class Api
* @param string|null $text Comment text.
* @param MessageFormat|null $format Comment format.
* @param MessageLink|null $link Link to a comment to reply to or forward.
* @param bool $disableLinkPreview If false, server will not generate media preview for links in text.
* @param bool $disableLinkPreview If true, server will not generate media preview for links in text.
*
* @return CommentMessage
* @throws ClientApiException
* @throws InvalidArgumentException
* @throws NetworkException
* @throws ReflectionException
* @throws SerializationException
@@ -957,7 +958,7 @@ class Api
$this->client->request(
self::METHOD_POST,
sprintf(self::ACTION_MESSAGE_COMMENTS, $messageId),
['disable_link_preview' => $disableLinkPreview],
$disableLinkPreview ? ['disable_link_preview' => true] : [],
$this->buildNewCommentBody($text, $format, $link),
)
);
@@ -974,6 +975,7 @@ class Api
*
* @return Result
* @throws ClientApiException
* @throws InvalidArgumentException
* @throws NetworkException
* @throws ReflectionException
* @throws SerializationException
@@ -1405,6 +1407,7 @@ class Api
* @param MessageLink|null $link
*
* @return array<string, mixed>
* @throws InvalidArgumentException
*/
private function buildNewCommentBody(?string $text, ?MessageFormat $format, ?MessageLink $link): array
{
@@ -1414,6 +1417,14 @@ class Api
'link' => $link,
];
return array_filter($body, fn($item) => $item !== null);
$body = array_filter($body, fn($item) => $item !== null);
if ($body === []) {
throw new InvalidArgumentException(
'At least one of $text, $format or $link must be provided to send or edit a comment.',
);
}
return $body;
}
}
+52
View File
@@ -1382,6 +1382,21 @@ final class ApiTest extends TestCase
$this->assertSame([], $this->api->getComments('mid.post'));
}
#[Test]
public function getCommentsTreatsEmptyCommentIdsListAsNoFilter(): void
{
$this->clientMock->expects($this->once())
->method('request')
->with('GET', '/messages/mid.post/comments', [])
->willReturn(['messages' => []]);
$this->modelFactoryMock->expects($this->once())
->method('createCommentMessages')
->willReturn([]);
$this->assertSame([], $this->api->getComments('mid.post', []));
}
#[Test]
public function getCommentByIdRequestsTheSingleComment(): void
{
@@ -1427,6 +1442,21 @@ final class ApiTest extends TestCase
$this->assertSame($expectedComment, $result);
}
#[Test]
public function sendCommentOmitsDisableLinkPreviewWhenFalse(): void
{
$this->clientMock->expects($this->once())
->method('request')
->with('POST', '/messages/mid.post/comments', [], ['text' => 'Nice post'])
->willReturn(['message' => $this->commentData()]);
$this->modelFactoryMock->expects($this->once())
->method('createCommentMessageFromSendResponse')
->willReturn($this->comment());
$this->api->sendComment('mid.post', 'Nice post');
}
#[Test]
public function editCommentPutsNewTextForTheComment(): void
{
@@ -1445,6 +1475,28 @@ final class ApiTest extends TestCase
$this->assertSame($expectedResult, $this->api->editComment('mid.post', 'mid.comment', 'Edited'));
}
#[Test]
public function sendCommentWithoutAnyFieldThrowsInvalidArgumentException(): void
{
$this->clientMock->expects($this->never())->method('request');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('At least one of $text, $format or $link must be provided');
$this->api->sendComment('mid.post');
}
#[Test]
public function editCommentWithoutAnyFieldThrowsInvalidArgumentException(): void
{
$this->clientMock->expects($this->never())->method('request');
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('At least one of $text, $format or $link must be provided');
$this->api->editComment('mid.post', 'mid.comment');
}
#[Test]
public function deleteCommentDeletesByCommentId(): void
{
+45
View File
@@ -966,6 +966,51 @@ final class ModelFactoryTest extends TestCase
$this->assertSame(7, $updateList->marker);
}
#[Test]
public function createUpdateListSkipsCommentAndBotPermissionUpdatesThatDoNotFit(): void
{
$logger = $this->createMock(LoggerInterface::class);
$logger->expects($this->exactly(4))->method('warning');
$factory = new ModelFactory($logger);
$updateList = $factory->createUpdateList([
'updates' => [
// TypeError: required `message` is missing
['update_type' => 'comment_created', 'timestamp' => 1],
// TypeError: required `message` is missing
['update_type' => 'comment_edited', 'timestamp' => 2],
// TypeError: required `chat_id` is missing
['update_type' => 'comment_removed', 'timestamp' => 3, 'message_id' => 'mid.c'],
// ValueError: unknown permission value
[
'update_type' => 'bot_admin_permissions_changed',
'timestamp' => 4,
'chat_id' => -100,
'user_id' => 50,
'bot_id' => 999,
'is_channel' => true,
'is_admin' => true,
'permissions' => ['not_a_permission'],
],
// valid comment_removed survives
[
'update_type' => 'comment_removed',
'timestamp' => 5,
'message_id' => 'mid.ok',
'chat_id' => -100,
'user_id' => 50,
'post_id' => 'mid.post',
],
],
'marker' => 9,
]);
$this->assertCount(1, $updateList->updates);
$this->assertInstanceOf(CommentRemovedUpdate::class, $updateList->updates[0]);
$this->assertSame('mid.ok', $updateList->updates[0]->messageId);
$this->assertSame(9, $updateList->marker);
}
public static function nonArrayPayloadProvider(): array
{
return [
+111
View File
@@ -5,12 +5,17 @@ declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests;
use BushlanovDev\MaxMessengerBot\Api;
use BushlanovDev\MaxMessengerBot\Enums\ChatAdminPermission;
use BushlanovDev\MaxMessengerBot\Enums\ChatType;
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
use BushlanovDev\MaxMessengerBot\Models\Message;
use BushlanovDev\MaxMessengerBot\Models\MessageBody;
use BushlanovDev\MaxMessengerBot\Models\Recipient;
use BushlanovDev\MaxMessengerBot\Models\Updates\BotAdminPermissionsChangedUpdate;
use BushlanovDev\MaxMessengerBot\Models\Updates\BotStartedUpdate;
use BushlanovDev\MaxMessengerBot\Models\Updates\CommentCreatedUpdate;
use BushlanovDev\MaxMessengerBot\Models\Updates\CommentEditedUpdate;
use BushlanovDev\MaxMessengerBot\Models\Updates\CommentRemovedUpdate;
use BushlanovDev\MaxMessengerBot\Models\Updates\MessageCreatedUpdate;
use BushlanovDev\MaxMessengerBot\Models\UserWithPhoto;
use BushlanovDev\MaxMessengerBot\UpdateDispatcher;
@@ -28,6 +33,10 @@ use PHPUnit\Framework\TestCase;
#[UsesClass(MessageBody::class)]
#[UsesClass(Recipient::class)]
#[UsesClass(MessageCreatedUpdate::class)]
#[UsesClass(CommentCreatedUpdate::class)]
#[UsesClass(CommentEditedUpdate::class)]
#[UsesClass(CommentRemovedUpdate::class)]
#[UsesClass(BotAdminPermissionsChangedUpdate::class)]
final class UpdateDispatcherTest extends TestCase
{
private Api $apiMock;
@@ -117,4 +126,106 @@ final class UpdateDispatcherTest extends TestCase
$this->assertFalse($commandCalled, 'onCommand handler should not be called for a regular message.');
$this->assertTrue($messageHandlerCalled, 'onMessageCreated handler was not called.');
}
#[Test]
public function onCommentCreatedDispatchesCommentCreatedUpdate(): void
{
$wasCalled = false;
$message = new Message(
time(),
new Recipient(ChatType::Channel, null, -100),
new MessageBody('mid.c1', 1, 'A comment', null, null),
new UserWithPhoto(50, 'Anna', null, null, false, time(), null, null, null),
null,
null,
null,
);
$update = new CommentCreatedUpdate(time(), $message);
$this->dispatcher->onCommentCreated(
function ($receivedUpdate, $receivedApi) use (&$wasCalled, $update) {
$this->assertSame($update, $receivedUpdate);
$this->assertSame($this->apiMock, $receivedApi);
$wasCalled = true;
},
);
$this->dispatcher->dispatch($update);
$this->assertTrue($wasCalled, 'onCommentCreated handler was not called.');
}
#[Test]
public function onCommentEditedDispatchesCommentEditedUpdate(): void
{
$wasCalled = false;
$message = new Message(
time(),
new Recipient(ChatType::Channel, null, -100),
new MessageBody('mid.c1', 2, 'Edited comment', null, null),
new UserWithPhoto(50, 'Anna', null, null, false, time(), null, null, null),
null,
null,
null,
);
$update = new CommentEditedUpdate(time(), $message);
$this->dispatcher->onCommentEdited(
function ($receivedUpdate, $receivedApi) use (&$wasCalled, $update) {
$this->assertSame($update, $receivedUpdate);
$this->assertSame($this->apiMock, $receivedApi);
$wasCalled = true;
},
);
$this->dispatcher->dispatch($update);
$this->assertTrue($wasCalled, 'onCommentEdited handler was not called.');
}
#[Test]
public function onCommentRemovedDispatchesCommentRemovedUpdate(): void
{
$wasCalled = false;
$update = new CommentRemovedUpdate(time(), 'mid.c1', -100, 50, 'mid.post');
$this->dispatcher->onCommentRemoved(
function ($receivedUpdate, $receivedApi) use (&$wasCalled, $update) {
$this->assertSame($update, $receivedUpdate);
$this->assertSame($this->apiMock, $receivedApi);
$wasCalled = true;
},
);
$this->dispatcher->dispatch($update);
$this->assertTrue($wasCalled, 'onCommentRemoved handler was not called.');
}
#[Test]
public function onBotAdminPermissionsChangedDispatchesPermissionsUpdate(): void
{
$wasCalled = false;
$update = new BotAdminPermissionsChangedUpdate(
time(),
-100,
50,
999,
true,
true,
[ChatAdminPermission::ReadAllMessages, ChatAdminPermission::Write],
);
$this->dispatcher->onBotAdminPermissionsChanged(
function ($receivedUpdate, $receivedApi) use (&$wasCalled, $update) {
$this->assertSame($update, $receivedUpdate);
$this->assertSame($this->apiMock, $receivedApi);
$wasCalled = true;
},
);
$this->dispatcher->dispatch($update);
$this->assertTrue($wasCalled, 'onBotAdminPermissionsChanged handler was not called.');
}
}