mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-17 21:49:57 +00:00
201 lines
7.1 KiB
PHP
201 lines
7.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace BushlanovDev\MaxMessengerBot\Tests;
|
|
|
|
use BushlanovDev\MaxMessengerBot\Api;
|
|
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
|
use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException;
|
|
use BushlanovDev\MaxMessengerBot\LongPollingHandler;
|
|
use BushlanovDev\MaxMessengerBot\Models\UpdateList;
|
|
use BushlanovDev\MaxMessengerBot\Models\Updates\AbstractUpdate;
|
|
use BushlanovDev\MaxMessengerBot\Models\Updates\BotStartedUpdate;
|
|
use BushlanovDev\MaxMessengerBot\Models\User;
|
|
use BushlanovDev\MaxMessengerBot\UpdateDispatcher;
|
|
use Error;
|
|
use Exception;
|
|
use phpmock\phpunit\PHPMock;
|
|
use PHPUnit\Framework\Attributes\CoversClass;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\Attributes\PreserveGlobalState;
|
|
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\Attributes\UsesClass;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
#[CoversClass(LongPollingHandler::class)]
|
|
#[UsesClass(UpdateDispatcher::class)]
|
|
#[UsesClass(UpdateList::class)]
|
|
#[UsesClass(AbstractUpdate::class)]
|
|
#[UsesClass(BotStartedUpdate::class)]
|
|
#[UsesClass(User::class)]
|
|
final class LongPollingHandlerTest extends TestCase
|
|
{
|
|
use PHPMock;
|
|
|
|
/**
|
|
* @param AbstractUpdate[] $updatesToReturn
|
|
* @param int $expectedDispatchCount
|
|
* @param int|null $expectedMarker
|
|
*/
|
|
#[Test]
|
|
#[DataProvider('processUpdatesProvider')]
|
|
public function processUpdates(
|
|
array $updatesToReturn,
|
|
int $expectedDispatchCount,
|
|
?int $expectedMarker,
|
|
): void {
|
|
$apiMock = $this->createMock(Api::class);
|
|
$loggerMock = $this->createMock(LoggerInterface::class);
|
|
$dispatcher = new UpdateDispatcher($apiMock);
|
|
|
|
$updateList = new UpdateList($updatesToReturn, $expectedMarker);
|
|
|
|
$apiMock->expects($this->once())
|
|
->method('getUpdates')
|
|
->with($this->isNull(), $this->equalTo(90), $this->isNull())
|
|
->willReturn($updateList);
|
|
|
|
$dispatchCount = 0;
|
|
$dispatcher->addHandler(UpdateType::BotStarted, function () use (&$dispatchCount) {
|
|
$dispatchCount++;
|
|
});
|
|
|
|
$handler = new LongPollingHandler($apiMock, $dispatcher, $loggerMock);
|
|
|
|
$returnedMarker = $handler->processUpdates(90, null);
|
|
|
|
$this->assertSame(
|
|
$expectedDispatchCount,
|
|
$dispatchCount,
|
|
"Dispatcher should be called $expectedDispatchCount times."
|
|
);
|
|
$this->assertSame($expectedMarker, $returnedMarker, 'Method should return the correct marker.');
|
|
}
|
|
|
|
public static function processUpdatesProvider(): array
|
|
{
|
|
$user = new User(1, 'Test', null, null, false, time());
|
|
$update1 = new BotStartedUpdate(time(), 1, $user, null, null);
|
|
$update2 = new BotStartedUpdate(time(), 2, $user, null, null);
|
|
|
|
return [
|
|
'with two updates' => [
|
|
'updatesToReturn' => [$update1, $update2],
|
|
'expectedDispatchCount' => 2,
|
|
'expectedMarker' => 12345,
|
|
],
|
|
'with no updates' => [
|
|
'updatesToReturn' => [],
|
|
'expectedDispatchCount' => 0,
|
|
'expectedMarker' => 54321,
|
|
],
|
|
];
|
|
}
|
|
|
|
#[Test]
|
|
#[PreserveGlobalState(false)]
|
|
#[RunInSeparateProcess]
|
|
public function runCatchesNetworkExceptionAndSleeps5Seconds(): void
|
|
{
|
|
$apiMock = $this->createMock(Api::class);
|
|
$loggerMock = $this->createMock(LoggerInterface::class);
|
|
$dispatcher = new UpdateDispatcher($apiMock);
|
|
|
|
$sleepMock = $this->getFunctionMock('BushlanovDev\MaxMessengerBot', 'sleep');
|
|
$sleepMock->expects($this->once())->with(5);
|
|
|
|
$apiMock->expects($this->exactly(2))
|
|
->method('getUpdates')
|
|
->willReturnOnConsecutiveCalls(
|
|
$this->throwException(new NetworkException('Connection timeout')),
|
|
$this->throwException(new Error('Stop test loop')),
|
|
);
|
|
|
|
$loggerMock->expects($this->once())
|
|
->method('error')
|
|
->with($this->stringContains('Long-polling network error'), $this->anything());
|
|
|
|
$handler = new LongPollingHandler($apiMock, $dispatcher, $loggerMock);
|
|
|
|
try {
|
|
$handler->handle();
|
|
} catch (Error $e) {
|
|
$this->assertSame('Stop test loop', $e->getMessage());
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
#[PreserveGlobalState(false)]
|
|
#[RunInSeparateProcess]
|
|
public function runCatchesGenericExceptionAndSleeps1Second(): void
|
|
{
|
|
$apiMock = $this->createMock(Api::class);
|
|
$loggerMock = $this->createMock(LoggerInterface::class);
|
|
$dispatcher = new UpdateDispatcher($apiMock);
|
|
|
|
$sleepMock = $this->getFunctionMock('BushlanovDev\MaxMessengerBot', 'sleep');
|
|
$sleepMock->expects($this->once())->with(1);
|
|
|
|
$apiMock->expects($this->exactly(2))
|
|
->method('getUpdates')
|
|
->willReturnOnConsecutiveCalls(
|
|
$this->throwException(new Exception('Something went wrong')),
|
|
$this->throwException(new Error('Stop test loop')),
|
|
);
|
|
|
|
$loggerMock->expects($this->once())
|
|
->method('error')
|
|
->with($this->stringContains('An error occurred during long-polling'), $this->anything());
|
|
|
|
$handler = new LongPollingHandler($apiMock, $dispatcher, $loggerMock);
|
|
|
|
try {
|
|
$handler->handle();
|
|
} catch (Error $e) {
|
|
$this->assertSame('Stop test loop', $e->getMessage());
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
public function processUpdatesContinuesAndLogsWhenHandlerThrows(): void
|
|
{
|
|
$apiMock = $this->createMock(Api::class);
|
|
$loggerMock = $this->createMock(LoggerInterface::class);
|
|
|
|
$dispatcher = new UpdateDispatcher($apiMock, $loggerMock);
|
|
|
|
$user = new User(1, 'Test', null, null, false, time());
|
|
$updateToFail = new BotStartedUpdate(time(), 1, $user, null, null);
|
|
$updateToSucceed = new BotStartedUpdate(time(), 2, $user, null, null);
|
|
|
|
$updateList = new UpdateList([$updateToFail, $updateToSucceed], 12345);
|
|
$exception = new Exception('Error inside handler');
|
|
|
|
$apiMock->expects($this->once())
|
|
->method('getUpdates')
|
|
->willReturn($updateList);
|
|
|
|
$handlerCallCount = 0;
|
|
$dispatcher->addHandler(UpdateType::BotStarted, function () use (&$handlerCallCount, $exception) {
|
|
$currentCall = $handlerCallCount++;
|
|
if ($currentCall === 0) {
|
|
throw $exception;
|
|
}
|
|
});
|
|
|
|
$loggerMock->expects($this->once())
|
|
->method('error')
|
|
->with('Error dispatching update', ['message' => 'Error inside handler', 'exception' => $exception]);
|
|
|
|
$handler = new LongPollingHandler($apiMock, $dispatcher, $loggerMock);
|
|
|
|
$returnedMarker = $handler->processUpdates(90, null);
|
|
|
|
$this->assertSame(12345, $returnedMarker, 'Method should return marker even if a handler failed.');
|
|
$this->assertSame(2, $handlerCallCount, 'Dispatcher should have attempted to process both updates.');
|
|
}
|
|
}
|