Skip to content

Commit 17acd6d

Browse files
committed
Laravel support tests
1 parent b37e7b6 commit 17acd6d

File tree

3 files changed

+274
-8
lines changed

3 files changed

+274
-8
lines changed

composer.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@
2424
},
2525
"require-dev": {
2626
"friendsofphp/php-cs-fixer": "^3.77",
27-
"illuminate/console": "^12.22",
28-
"illuminate/http": "^12.22",
29-
"illuminate/testing": "^11.0||^12.0",
3027
"jaschilz/php-coverage-badger": "^2.0",
28+
"laravel/framework": "^11.0",
3129
"mikey179/vfsstream": "^1.6",
3230
"mockery/mockery": "^1.6",
31+
"orchestra/testbench": "^9.0",
3332
"php-mock/php-mock-phpunit": "^2.13",
3433
"phpstan/phpstan": "^2.1",
3534
"phpunit/phpunit": "^12.0",

src/Laravel/MaxBotServiceProvider.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,6 @@ public function register(): void
8686
return new ModelFactory();
8787
});
8888

89-
$this->app->singleton(UpdateDispatcher::class, function (Application $app) {
90-
return new UpdateDispatcher($app->make(Api::class));
91-
});
92-
9389
$this->app->singleton(Api::class, function (Application $app) {
9490
/** @var Config $config */
9591
$config = $app->make(Config::class);
@@ -106,10 +102,14 @@ public function register(): void
106102
$app->make(ClientApiInterface::class),
107103
$app->make(ModelFactory::class),
108104
$app->make(LoggerInterface::class),
109-
$app->make(UpdateDispatcher::class),
105+
null,
110106
);
111107
});
112108

109+
$this->app->singleton(UpdateDispatcher::class, function (Application $app) {
110+
return $app->make(Api::class)->getUpdateDispatcher();
111+
});
112+
113113
$this->app->bind(WebhookHandler::class, function (Application $app) {
114114
/** @var Config $config */
115115
$config = $app->make(Config::class);
@@ -171,6 +171,7 @@ public function boot(): void
171171
* Get the services provided by the provider.
172172
*
173173
* @return array<int, string>
174+
* @codeCoverageIgnore
174175
*/
175176
public function provides(): array
176177
{
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BushlanovDev\MaxMessengerBot\Tests\Laravel;
6+
7+
use BushlanovDev\MaxMessengerBot\Api;
8+
use BushlanovDev\MaxMessengerBot\Client;
9+
use BushlanovDev\MaxMessengerBot\ClientApiInterface;
10+
use BushlanovDev\MaxMessengerBot\Laravel\Commands\PollingStartCommand;
11+
use BushlanovDev\MaxMessengerBot\Laravel\Commands\WebhookListCommand;
12+
use BushlanovDev\MaxMessengerBot\Laravel\Commands\WebhookSubscribeCommand;
13+
use BushlanovDev\MaxMessengerBot\Laravel\Commands\WebhookUnsubscribeCommand;
14+
use BushlanovDev\MaxMessengerBot\Laravel\MaxBotManager;
15+
use BushlanovDev\MaxMessengerBot\Laravel\MaxBotServiceProvider;
16+
use BushlanovDev\MaxMessengerBot\LongPollingHandler;
17+
use BushlanovDev\MaxMessengerBot\ModelFactory;
18+
use BushlanovDev\MaxMessengerBot\UpdateDispatcher;
19+
use BushlanovDev\MaxMessengerBot\WebhookHandler;
20+
use Illuminate\Support\Facades\Artisan;
21+
use InvalidArgumentException;
22+
use LogicException;
23+
use Orchestra\Testbench\TestCase;
24+
use phpmock\phpunit\PHPMock;
25+
use PHPUnit\Framework\Attributes\CoversClass;
26+
use PHPUnit\Framework\Attributes\DataProvider;
27+
use PHPUnit\Framework\Attributes\PreserveGlobalState;
28+
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
29+
use PHPUnit\Framework\Attributes\Test;
30+
use PHPUnit\Framework\Attributes\UsesClass;
31+
use Psr\Log\LoggerInterface;
32+
use Psr\Log\NullLogger;
33+
34+
#[CoversClass(MaxBotServiceProvider::class)]
35+
#[UsesClass(Api::class)]
36+
#[UsesClass(Client::class)]
37+
#[UsesClass(UpdateDispatcher::class)]
38+
#[UsesClass(MaxBotManager::class)]
39+
#[UsesClass(WebhookHandler::class)]
40+
final class MaxBotServiceProviderTest extends TestCase
41+
{
42+
use PHPMock;
43+
44+
protected function getEnvironmentSetUp($app): void
45+
{
46+
$app['config']->set('maxbot.access_token', 'test-token');
47+
$app['config']->set('maxbot.webhook_secret', 'test-secret');
48+
$app['config']->set('maxbot.base_url', 'https://test.max.ru');
49+
$app['config']->set('maxbot.api_version', 'test-version');
50+
}
51+
52+
protected function getPackageProviders($app): array
53+
{
54+
return [MaxBotServiceProvider::class];
55+
}
56+
57+
#[Test]
58+
public function serviceProviderIsLoaded(): void
59+
{
60+
$this->assertInstanceOf(MaxBotServiceProvider::class, $this->app->getProvider(MaxBotServiceProvider::class));
61+
}
62+
63+
#[Test]
64+
public function itThrowsExceptionWhenAccessTokenIsMissingForClient(): void
65+
{
66+
$this->expectException(InvalidArgumentException::class);
67+
$this->expectExceptionMessage(
68+
'Max Bot access token is not configured. Please set MAXBOT_ACCESS_TOKEN in your .env file.'
69+
);
70+
71+
$this->app['config']->set('maxbot.access_token', null);
72+
73+
$this->app->make(ClientApiInterface::class);
74+
}
75+
76+
#[Test]
77+
public function itThrowsExceptionWhenAccessTokenIsMissingForApi(): void
78+
{
79+
$this->expectException(InvalidArgumentException::class);
80+
$this->expectExceptionMessage(
81+
'Max Bot access token is not configured. Please set MAXBOT_ACCESS_TOKEN in your .env file.'
82+
);
83+
84+
$this->app['config']->set('maxbot.access_token', null);
85+
86+
$this->app->make(Api::class);
87+
}
88+
89+
#[Test]
90+
#[RunInSeparateProcess]
91+
#[PreserveGlobalState(false)]
92+
public function itThrowsExceptionWhenGuzzleIsMissing(): void
93+
{
94+
$this->expectException(LogicException::class);
95+
$this->expectExceptionMessage(
96+
'Guzzle HTTP client is required. Please run "composer require guzzlehttp/guzzle".'
97+
);
98+
99+
$classExistsMock = $this->getFunctionMock('BushlanovDev\\MaxMessengerBot\\Laravel', 'class_exists');
100+
$classExistsMock->expects($this->once())->with(\GuzzleHttp\Client::class)->willReturn(false);
101+
102+
(new MaxBotServiceProvider($this->app))->register();
103+
104+
$this->app->make(ClientApiInterface::class);
105+
}
106+
107+
108+
/**
109+
* @return array<string, array{0: string, 1: class-string}>
110+
*/
111+
public static function servicesProvider(): array
112+
{
113+
return [
114+
'Api::class' => [Api::class, Api::class],
115+
'maxbot alias' => ['maxbot', Api::class],
116+
'maxbot.api alias' => ['maxbot.api', Api::class],
117+
'ClientApiInterface::class' => [ClientApiInterface::class, Client::class],
118+
'maxbot.client alias' => ['maxbot.client', Client::class],
119+
'ModelFactory::class' => [ModelFactory::class, ModelFactory::class],
120+
'UpdateDispatcher::class' => [UpdateDispatcher::class, UpdateDispatcher::class],
121+
'maxbot.dispatcher alias' => ['maxbot.dispatcher', UpdateDispatcher::class],
122+
'WebhookHandler::class' => [WebhookHandler::class, WebhookHandler::class],
123+
'maxbot.webhook alias' => ['maxbot.webhook', WebhookHandler::class],
124+
'LongPollingHandler::class' => [LongPollingHandler::class, LongPollingHandler::class],
125+
'maxbot.polling alias' => ['maxbot.polling', LongPollingHandler::class],
126+
'MaxBotManager::class' => [MaxBotManager::class, MaxBotManager::class],
127+
'maxbot.manager alias' => ['maxbot.manager', MaxBotManager::class],
128+
];
129+
}
130+
131+
#[Test]
132+
#[DataProvider('servicesProvider')]
133+
public function allServicesAreRegisteredCorrectly(string $service, string $expectedClass): void
134+
{
135+
$this->assertInstanceOf($expectedClass, $this->app->make($service));
136+
}
137+
138+
/**
139+
* @return array<string, array{0: string}>
140+
*/
141+
public static function singletonsProvider(): array
142+
{
143+
return [
144+
'Api' => [Api::class],
145+
'ClientApiInterface' => [ClientApiInterface::class],
146+
'ModelFactory' => [ModelFactory::class],
147+
'UpdateDispatcher' => [UpdateDispatcher::class],
148+
'MaxBotManager' => [MaxBotManager::class],
149+
];
150+
}
151+
152+
#[Test]
153+
#[DataProvider('singletonsProvider')]
154+
public function servicesAreRegisteredAsSingletons(string $service): void
155+
{
156+
$instance1 = $this->app->make($service);
157+
$instance2 = $this->app->make($service);
158+
159+
$this->assertSame($instance1, $instance2);
160+
}
161+
162+
#[Test]
163+
public function clientIsConfiguredCorrectlyFromConfig(): void
164+
{
165+
/** @var Client $client */
166+
$client = $this->app->make(ClientApiInterface::class);
167+
$this->assertInstanceOf(Client::class, $client);
168+
169+
$reflection = new \ReflectionClass($client);
170+
171+
$accessTokenProp = $reflection->getProperty('accessToken');
172+
$baseUrlProp = $reflection->getProperty('baseUrl');
173+
$apiVersionProp = $reflection->getProperty('apiVersion');
174+
175+
$this->assertSame('test-token', $accessTokenProp->getValue($client));
176+
$this->assertSame('https://test.max.ru', $baseUrlProp->getValue($client));
177+
$this->assertSame('test-version', $apiVersionProp->getValue($client));
178+
}
179+
180+
#[Test]
181+
public function clientIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
182+
{
183+
$this->app['config']->set('maxbot.logging.enabled', true);
184+
185+
$mockLogger = $this->createMock(LoggerInterface::class);
186+
$this->app->instance(LoggerInterface::class, $mockLogger);
187+
188+
/** @var Client $client */
189+
$client = $this->app->make(ClientApiInterface::class);
190+
191+
$reflection = new \ReflectionClass($client);
192+
$loggerProp = $reflection->getProperty('logger');
193+
$actualLogger = $loggerProp->getValue($client);
194+
195+
$this->assertSame($mockLogger, $actualLogger);
196+
}
197+
198+
#[Test]
199+
public function clientIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
200+
{
201+
$this->app['config']->set('maxbot.logging.enabled', false);
202+
203+
/** @var Client $client */
204+
$client = $this->app->make(ClientApiInterface::class);
205+
206+
$reflection = new \ReflectionClass($client);
207+
$loggerProp = $reflection->getProperty('logger');
208+
$actualLogger = $loggerProp->getValue($client);
209+
210+
$this->assertInstanceOf(NullLogger::class, $actualLogger);
211+
}
212+
213+
#[Test]
214+
public function webhookHandlerIsConfiguredWithSecretFromConfig(): void
215+
{
216+
/** @var WebhookHandler $handler */
217+
$handler = $this->app->make(WebhookHandler::class);
218+
$this->assertInstanceOf(WebhookHandler::class, $handler);
219+
220+
$reflection = new \ReflectionClass($handler);
221+
$secretProp = $reflection->getProperty('secret');
222+
223+
$this->assertSame('test-secret', $secretProp->getValue($handler));
224+
}
225+
226+
#[Test]
227+
public function apiIsCreatedWithAllDependenciesFromContainer(): void
228+
{
229+
/** @var Api $api */
230+
$api = $this->app->make(Api::class);
231+
232+
$reflection = new \ReflectionClass($api);
233+
234+
$clientProp = $reflection->getProperty('client');
235+
$factoryProp = $reflection->getProperty('modelFactory');
236+
$loggerProp = $reflection->getProperty('logger');
237+
$dispatcherProp = $reflection->getProperty('updateDispatcher');
238+
239+
$this->assertSame($this->app->make(ClientApiInterface::class), $clientProp->getValue($api));
240+
$this->assertSame($this->app->make(ModelFactory::class), $factoryProp->getValue($api));
241+
$this->assertSame($this->app->make(LoggerInterface::class), $loggerProp->getValue($api));
242+
$this->assertSame($this->app->make(UpdateDispatcher::class), $dispatcherProp->getValue($api));
243+
}
244+
245+
/**
246+
* @return array<string, array{0: string}>
247+
*/
248+
public static function commandsProvider(): array
249+
{
250+
return [
251+
'WebhookSubscribeCommand' => [WebhookSubscribeCommand::class, 'maxbot:webhook:subscribe'],
252+
'WebhookUnsubscribeCommand' => [WebhookUnsubscribeCommand::class, 'maxbot:webhook:unsubscribe'],
253+
'WebhookListCommand' => [WebhookListCommand::class, 'maxbot:webhook:list'],
254+
'PollingStartCommand' => [PollingStartCommand::class, 'maxbot:polling:start'],
255+
];
256+
}
257+
258+
#[Test]
259+
#[DataProvider('commandsProvider')]
260+
public function bootMethodRegistersCommandsInConsole(string $class, string $signature): void
261+
{
262+
$commands = Artisan::all();
263+
$this->assertArrayHasKey($signature, $commands);
264+
$this->assertInstanceOf($class, $commands[$signature]);
265+
}
266+
}

0 commit comments

Comments
 (0)