Skip to content

Commit 96b5f0d

Browse files
committed
Add driver factorty tests
1 parent 1c93c89 commit 96b5f0d

File tree

6 files changed

+274
-44
lines changed

6 files changed

+274
-44
lines changed

pkg/enqueue/Client/DriverFactory.php

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
use Enqueue\Dsn\Dsn;
88
use Enqueue\Stomp\Client\ManagementClient;
99
use Enqueue\Stomp\Client\RabbitMqStompDriver;
10+
use Enqueue\Stomp\StompConnectionFactory;
11+
use Interop\Amqp\AmqpConnectionFactory;
1012
use Interop\Queue\PsrConnectionFactory;
1113

12-
class DriverFactory
14+
final class DriverFactory implements DriverFactoryInterface
1315
{
1416
/**
1517
* @var Config
@@ -30,55 +32,93 @@ public function __construct(Config $config, QueueMetaRegistry $queueMetaRegistry
3032
public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface
3133
{
3234
$dsn = new Dsn($dsn);
33-
$protocol = $dsn->getSchemeProtocol();
34-
$extensions = $dsn->getSchemeExtensions();
3535

36-
$foundDriverClass = null;
37-
foreach (Resources::getAvailableDrivers() as $driverClass => $info) {
38-
if (false == in_array($protocol, $info['supportedSchemeProtocols'], true)) {
39-
continue;
36+
if ($driverClass = $this->findDriverClass($dsn, Resources::getAvailableDrivers())) {
37+
if (RabbitMqDriver::class === $driverClass) {
38+
if (false == $factory instanceof AmqpConnectionFactory) {
39+
throw new \LogicException(sprintf(
40+
'The factory must be instance of "%s", got "%s"',
41+
AmqpConnectionFactory::class,
42+
get_class($factory)
43+
));
44+
}
45+
46+
return new RabbitMqDriver($factory->createContext(), $this->config, $this->queueMetaRegistry);
4047
}
4148

42-
if ($info['requiredSchemeExtensions']) {
43-
if (empty($extensions)) {
44-
continue;
49+
if (RabbitMqStompDriver::class === $driverClass) {
50+
if (false == $factory instanceof StompConnectionFactory) {
51+
throw new \LogicException(sprintf(
52+
'The factory must be instance of "%s", got "%s"',
53+
StompConnectionFactory::class,
54+
get_class($factory)
55+
));
4556
}
4657

47-
foreach ($info['requiredSchemeExtensions'] as $requiredSchemeExtension) {
48-
if (false == in_array($requiredSchemeExtension, $extensions, true)) {
49-
continue;
50-
}
58+
if (isset($config['rabbitmq_management_dsn'])) {
59+
$managementDsn = new Dsn($config['rabbitmq_management_dsn']);
60+
61+
$managementClient = ManagementClient::create(
62+
ltrim($managementDsn->getPath(), '/'),
63+
$managementDsn->getHost(),
64+
$managementDsn->getPort(),
65+
$managementDsn->getUser(),
66+
$managementDsn->getPassword()
67+
);
68+
} else {
69+
$managementClient = ManagementClient::create(
70+
ltrim($dsn->getPath(), '/'),
71+
$dsn->getHost(),
72+
isset($config['management_plugin_port']) ? $config['management_plugin_port'] : 15672,
73+
$dsn->getUser(),
74+
$dsn->getPassword()
75+
);
5176
}
5277

53-
$foundDriverClass = $driverClass;
54-
55-
break;
78+
return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
5679
}
5780

58-
$foundDriverClass = $driverClass;
59-
break;
81+
return new $driverClass($factory->createContext(), $this->config, $this->queueMetaRegistry);
6082
}
6183

62-
if (false == $foundDriverClass) {
63-
throw new \LogicException(sprintf('The driver class that supports scheme %s could not be found', $dsn->getScheme()));
84+
$knownDrivers = Resources::getKnownDrivers();
85+
if ($driverClass = $this->findDriverClass($dsn, $knownDrivers)) {
86+
throw new \LogicException(sprintf(
87+
'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
88+
$dsn->getScheme(),
89+
$knownDrivers[$driverClass]['package']
90+
));
6491
}
6592

66-
if (RabbitMqDriver::class === $foundDriverClass) {
67-
return new RabbitMqDriver($factory->createContext(), $this->config, $this->queueMetaRegistry);
68-
}
93+
throw new \LogicException(sprintf(
94+
'A given scheme "%s" is not supported. Maybe it is a custom driver, make sure you registered it with "%s::addDriver".',
95+
$dsn->getScheme(),
96+
Resources::class
97+
));
98+
}
6999

70-
if (RabbitMqStompDriver::class === $foundDriverClass) {
71-
$managementClient = ManagementClient::create(
72-
ltrim($dsn->getPath(), '/'),
73-
$dsn->getHost(),
74-
$config['management_plugin_port'],
75-
$dsn->getUser(),
76-
$dsn->getPassword()
77-
);
100+
private function findDriverClass(Dsn $dsn, array $factories): ?string
101+
{
102+
$protocol = $dsn->getSchemeProtocol();
103+
foreach ($factories as $driverClass => $info) {
104+
if (false == in_array($protocol, $info['schemes'], true)) {
105+
continue;
106+
}
107+
108+
if (false == $dsn->getSchemeExtensions()) {
109+
return $driverClass;
110+
}
78111

79-
return new RabbitMqStompDriver($factory->createContext(), $this->config, $this->queueMetaRegistry, $managementClient);
112+
if (empty($info['requiredSchemeExtensions'])) {
113+
continue;
114+
}
115+
116+
$diff = array_diff($dsn->getSchemeExtensions(), $info['requiredSchemeExtensions']);
117+
if (empty($diff)) {
118+
return $driverClass;
119+
}
80120
}
81121

82-
return new $foundDriverClass($factory->createContext(), $this->config, $this->queueMetaRegistry);
122+
return null;
83123
}
84124
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Enqueue\Client;
4+
5+
use Interop\Queue\PsrConnectionFactory;
6+
7+
interface DriverFactoryInterface
8+
{
9+
public function create(PsrConnectionFactory $factory, string $dsn, array $config): DriverInterface;
10+
}

pkg/enqueue/Client/Resources.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,20 +108,20 @@ public static function getKnownDrivers(): array
108108
$map[DbalDriver::class] = [
109109
'schemes' => [
110110
'db2',
111-
'ibm_db2',
111+
'ibm-db2',
112112
'mssql',
113-
'pdo_sqlsrv',
113+
'sqlsrv',
114114
'mysql',
115115
'mysql2',
116-
'pdo_mysql',
116+
'mysql',
117117
'pgsql',
118118
'postgres',
119-
'pdo_pgsql',
119+
'pgsql',
120120
'sqlite',
121121
'sqlite3',
122-
'pdo_sqlite',
122+
'sqlite',
123123
],
124-
'requiredSchemeExtensions' => [],
124+
'requiredSchemeExtensions' => ['pdo'],
125125
'package' => 'enqueue/dbal',
126126
];
127127

pkg/enqueue/ConnectionFactoryFactory.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ public function create(string $dsn): PsrConnectionFactory
1111
{
1212
$dsn = new Dsn($dsn);
1313

14-
if ($factoryClass = $this->findFactory($dsn, Resources::getAvailableConnections())) {
14+
if ($factoryClass = $this->findFactoryClass($dsn, Resources::getAvailableConnections())) {
1515
return new $factoryClass((string) $dsn);
1616
}
1717

1818
$knownConnections = Resources::getKnownConnections();
19-
if ($factoryClass = $this->findFactory($dsn, $knownConnections)) {
19+
if ($factoryClass = $this->findFactoryClass($dsn, $knownConnections)) {
2020
throw new \LogicException(sprintf(
2121
'To use given scheme "%s" a package has to be installed. Run "composer req %s" to add it.',
2222
$dsn->getScheme(),
@@ -31,7 +31,7 @@ public function create(string $dsn): PsrConnectionFactory
3131
));
3232
}
3333

34-
private function findFactory(Dsn $dsn, array $factories): ?string
34+
private function findFactoryClass(Dsn $dsn, array $factories): ?string
3535
{
3636
$protocol = $dsn->getSchemeProtocol();
3737
foreach ($factories as $connectionClass => $info) {
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
namespace Enqueue\Tests\Client;
4+
5+
use Enqueue\Client\Amqp\AmqpDriver;
6+
use Enqueue\Client\Amqp\RabbitMqDriver;
7+
use Enqueue\Client\Config;
8+
use Enqueue\Client\DriverFactory;
9+
use Enqueue\Client\DriverFactoryInterface;
10+
use Enqueue\Client\Meta\QueueMetaRegistry;
11+
use Enqueue\Client\Resources;
12+
use Enqueue\Dbal\Client\DbalDriver;
13+
use Enqueue\Dbal\DbalConnectionFactory;
14+
use Enqueue\Dbal\DbalContext;
15+
use Enqueue\Fs\Client\FsDriver;
16+
use Enqueue\Fs\FsConnectionFactory;
17+
use Enqueue\Fs\FsContext;
18+
use Enqueue\Gearman\GearmanConnectionFactory;
19+
use Enqueue\Gps\Client\GpsDriver;
20+
use Enqueue\Gps\GpsConnectionFactory;
21+
use Enqueue\Gps\GpsContext;
22+
use Enqueue\Mongodb\Client\MongodbDriver;
23+
use Enqueue\Mongodb\MongodbConnectionFactory;
24+
use Enqueue\Mongodb\MongodbContext;
25+
use Enqueue\Null\Client\NullDriver;
26+
use Enqueue\Null\NullConnectionFactory;
27+
use Enqueue\Null\NullContext;
28+
use Enqueue\RdKafka\Client\RdKafkaDriver;
29+
use Enqueue\RdKafka\RdKafkaConnectionFactory;
30+
use Enqueue\RdKafka\RdKafkaContext;
31+
use Enqueue\Redis\Client\RedisDriver;
32+
use Enqueue\Redis\RedisConnectionFactory;
33+
use Enqueue\Redis\RedisContext;
34+
use Enqueue\Sqs\Client\SqsDriver;
35+
use Enqueue\Sqs\SqsConnectionFactory;
36+
use Enqueue\Sqs\SqsContext;
37+
use Enqueue\Stomp\Client\RabbitMqStompDriver;
38+
use Enqueue\Stomp\Client\StompDriver;
39+
use Enqueue\Stomp\StompConnectionFactory;
40+
use Enqueue\Stomp\StompContext;
41+
use Interop\Amqp\AmqpConnectionFactory;
42+
use Interop\Amqp\AmqpContext;
43+
use Interop\Queue\PsrConnectionFactory;
44+
use PHPUnit\Framework\TestCase;
45+
46+
class DriverFactoryTest extends TestCase
47+
{
48+
public function testShouldImplementDriverFactoryInterface()
49+
{
50+
$rc = new \ReflectionClass(DriverFactory::class);
51+
52+
$this->assertTrue($rc->implementsInterface(DriverFactoryInterface::class));
53+
}
54+
55+
public function testShouldBeFinal()
56+
{
57+
$rc = new \ReflectionClass(DriverFactory::class);
58+
59+
$this->assertTrue($rc->isFinal());
60+
}
61+
62+
public function testCouldBeConstructedWithConfigAndQueueMetaAsArguments()
63+
{
64+
new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
65+
}
66+
67+
public function testThrowIfPackageThatSupportSchemeNotInstalled()
68+
{
69+
$scheme = 'scheme5b7aa7d7cd213';
70+
$class = 'ConnectionClass5b7aa7d7cd213';
71+
72+
Resources::addDriver($class, [$scheme], [], 'thePackage');
73+
74+
$this->expectException(\LogicException::class);
75+
$this->expectExceptionMessage('To use given scheme "scheme5b7aa7d7cd213" a package has to be installed. Run "composer req thePackage" to add it.');
76+
$factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
77+
78+
$factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
79+
}
80+
81+
public function testThrowIfSchemeIsNotKnown()
82+
{
83+
$scheme = 'scheme5b7aa862e70a5';
84+
85+
$this->expectException(\LogicException::class);
86+
$this->expectExceptionMessage('A given scheme "scheme5b7aa862e70a5" is not supported. Maybe it is a custom driver, make sure you registered it with "Enqueue\Client\Resources::addDriver".');
87+
88+
$factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
89+
90+
$factory->create($this->createConnectionFactoryMock(), $scheme.'://foo', []);
91+
}
92+
93+
public function testThrowIfDsnInvalid()
94+
{
95+
$this->expectException(\LogicException::class);
96+
$this->expectExceptionMessage('The DSN is invalid. It does not have scheme separator ":".');
97+
98+
$factory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
99+
100+
$factory->create($this->createConnectionFactoryMock(), 'invalidDsn', []);
101+
}
102+
103+
/**
104+
* @dataProvider provideDSN
105+
*/
106+
public function testReturnsExpectedFactories(
107+
string $dsn,
108+
string $connectionFactoryClass,
109+
string $contextClass,
110+
array $conifg,
111+
string $expectedDriverClass
112+
) {
113+
$connectionFactoryMock = $this->createMock($connectionFactoryClass);
114+
$connectionFactoryMock
115+
->expects($this->once())
116+
->method('createContext')
117+
->willReturn($this->createMock($contextClass))
118+
;
119+
120+
$driverFactory = new DriverFactory($this->createConfigMock(), $this->createQueueMetaRegistryMock());
121+
122+
$driver = $driverFactory->create($connectionFactoryMock, $dsn, $conifg);
123+
124+
$this->assertInstanceOf($expectedDriverClass, $driver);
125+
}
126+
127+
public static function provideDSN()
128+
{
129+
yield ['null:', NullConnectionFactory::class, NullContext::class, [], NullDriver::class];
130+
131+
yield ['amqp:', AmqpConnectionFactory::class, AmqpContext::class, [], AmqpDriver::class];
132+
133+
yield ['amqp+rabbitmq:', AmqpConnectionFactory::class, AmqpContext::class, [], RabbitMqDriver::class];
134+
135+
yield ['mysql:', DbalConnectionFactory::class, DbalContext::class, [], DbalDriver::class];
136+
137+
yield ['file:', FsConnectionFactory::class, FsContext::class, [], FsDriver::class];
138+
139+
// https://github.com/php-enqueue/enqueue-dev/issues/511
140+
// yield ['gearman:', GearmanConnectionFactory::class, NullContext::class, [], NullDriver::class];
141+
142+
yield ['gps:', GpsConnectionFactory::class, GpsContext::class, [], GpsDriver::class];
143+
144+
yield ['mongodb:', MongodbConnectionFactory::class, MongodbContext::class, [], MongodbDriver::class];
145+
146+
yield ['kafka:', RdKafkaConnectionFactory::class, RdKafkaContext::class, [], RdKafkaDriver::class];
147+
148+
yield ['redis:', RedisConnectionFactory::class, RedisContext::class, [], RedisDriver::class];
149+
150+
yield ['sqs:', SqsConnectionFactory::class, SqsContext::class, [], SqsDriver::class];
151+
152+
yield ['stomp:', StompConnectionFactory::class, StompContext::class, [], StompDriver::class];
153+
154+
yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [], RabbitMqStompDriver::class];
155+
156+
yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [
157+
'rabbitmq_management_dsn' => 'http://guest:guest@localhost:15672/mqdev',
158+
], RabbitMqStompDriver::class];
159+
160+
yield ['stomp+rabbitmq:', StompConnectionFactory::class, StompContext::class, [
161+
'management_plugin_port' => 1234,
162+
], RabbitMqStompDriver::class];
163+
}
164+
165+
private function createConnectionFactoryMock(): PsrConnectionFactory
166+
{
167+
return $this->createMock(PsrConnectionFactory::class);
168+
}
169+
170+
private function createConfigMock(): Config
171+
{
172+
return $this->createMock(Config::class);
173+
}
174+
175+
private function createQueueMetaRegistryMock()
176+
{
177+
return $this->createMock(QueueMetaRegistry::class);
178+
}
179+
}

pkg/enqueue/Tests/ConnectionFactoryFactoryTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ public static function provideDSN()
113113

114114
yield ['file:', FsConnectionFactory::class];
115115

116-
yield ['gearman:', GearmanConnectionFactory::class];
116+
// https://github.com/php-enqueue/enqueue-dev/issues/511
117+
// yield ['gearman:', GearmanConnectionFactory::class];
117118

118119
yield ['gps:', GpsConnectionFactory::class];
119120

0 commit comments

Comments
 (0)