Skip to content

Commit e83124b

Browse files
committed
Introduce trait SharedDatabases
1 parent 6a73754 commit e83124b

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

src/Test/SharedDatabases.php

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
<?php
2+
3+
namespace ipl\Sql\Test;
4+
5+
use ipl\Sql\Connection;
6+
use RuntimeException;
7+
8+
/**
9+
* Data provider for database connections. Use this to provide real database connections for your tests.
10+
*
11+
* To use it, implement {@see Databases::setUpSchema()} and {@see Databases::tearDownSchema()}.
12+
* The environment also needs to provide the following variables: (Replace * with the name of a supported adapter)
13+
* {@see Databases::SUPPORTED_ADAPTERS}
14+
*
15+
* Name | Description
16+
* ----------------- | ------------------------
17+
* *_TESTDB | The database to use
18+
* *_TESTDB_HOST | The server to connect to
19+
* *_TESTDB_PORT | The port to connect to
20+
* *_TESTDB_USER | The user to connect with
21+
* *_TESTDB_PASSWORD | The password of the user
22+
*
23+
* Each test case will run multiple times, once for each database.
24+
* The connection is passed as the first argument to it.
25+
*
26+
* A schema will be initialized once the first test case using this provider is run. Schemas will first be dropped
27+
* and then recreated to ensure a clean state. During the entire test run, the same schema will be used for all
28+
* tests.
29+
*
30+
* If you need to implement your own setUp() and tearDown() methods, and need access to the database connection,
31+
* use {@see Databases::getConnection()}.
32+
*/
33+
trait SharedDatabases
34+
{
35+
/**
36+
* All database connections
37+
*
38+
* @internal Only the trait itself should access this property
39+
*
40+
* @var array
41+
*/
42+
private static array $connections = [];
43+
44+
/** @var string[] */
45+
private const SUPPORTED_ADAPTERS = ['mssql', 'mysql', 'oracle', 'pgsql', 'sqlite'];
46+
47+
/**
48+
* Create the schema for the test database
49+
*
50+
* @param Connection $db
51+
* @param string $driver
52+
*
53+
* @return void
54+
*/
55+
abstract protected static function setUpSchema(Connection $db, string $driver): void;
56+
57+
/**
58+
* Drop the schema of the test database
59+
*
60+
* @param Connection $db
61+
* @param string $driver
62+
*
63+
* @return void
64+
*/
65+
abstract protected static function tearDownSchema(Connection $db, string $driver): void;
66+
67+
/**
68+
* Provide the database connections
69+
*
70+
* @return array<string, Connection[]>
71+
*/
72+
final public static function sharedDatabases(): array
73+
{
74+
self::initializeDatabases();
75+
76+
return self::$connections;
77+
}
78+
79+
/**
80+
* Get the current database connection
81+
*
82+
* @return Connection
83+
* @throws RuntimeException if the connection cannot be retrieved
84+
*/
85+
final protected function getConnection(): Connection
86+
{
87+
if (method_exists($this, 'getProvidedData')) {
88+
$connection = $this->getProvidedData();
89+
} elseif (method_exists($this, 'providedData')) {
90+
$connection = $this->providedData();
91+
} else {
92+
throw new RuntimeException('Cannot get connection: Unsupported PHPUnit version?');
93+
}
94+
95+
if (! $connection instanceof Connection) {
96+
throw new RuntimeException('Cannot get connection: Are all test cases using the same provider?');
97+
}
98+
99+
return $connection;
100+
}
101+
102+
/**
103+
* Get the value of an environment variable
104+
*
105+
* @param string $name
106+
*
107+
* @return string
108+
*
109+
* @throws RuntimeException if the environment variable is not set
110+
* @internal Only the trait itself should call this method
111+
*/
112+
final protected static function getEnvironmentVariable(string $name): string
113+
{
114+
$value = getenv($name);
115+
if ($value === false) {
116+
throw new RuntimeException("Environment variable $name is not set");
117+
}
118+
119+
return $value;
120+
}
121+
122+
/**
123+
* Create a database connection
124+
*
125+
* @param string $driver
126+
*
127+
* @return Connection
128+
*
129+
* @internal Only the trait itself should call this method
130+
*/
131+
final protected static function connectToDatabase(string $driver): Connection
132+
{
133+
return new Connection([
134+
'db' => $driver,
135+
'host' => self::getEnvironmentVariable(strtoupper($driver) . '_TESTDB_HOST'),
136+
'port' => self::getEnvironmentVariable(strtoupper($driver) . '_TESTDB_PORT'),
137+
'username' => self::getEnvironmentVariable(strtoupper($driver) . '_TESTDB_USER'),
138+
'password' => self::getEnvironmentVariable(strtoupper($driver) . '_TESTDB_PASSWORD'),
139+
'dbname' => self::getEnvironmentVariable(strtoupper($driver) . '_TESTDB'),
140+
]);
141+
}
142+
143+
/**
144+
* Set up the database connections
145+
*
146+
* @return void
147+
*
148+
* @internal Only the trait itself should call this method
149+
*/
150+
final protected static function initializeDatabases(): void
151+
{
152+
foreach (self::SUPPORTED_ADAPTERS as $driver) {
153+
if (isset($_SERVER[strtoupper($driver) . '_TESTDB'])) {
154+
if (! isset(self::$connections[$driver])) {
155+
self::$connections[$driver] = [self::connectToDatabase($driver)];
156+
static::tearDownSchema(self::$connections[$driver][0], $driver);
157+
static::setUpSchema(self::$connections[$driver][0], $driver);
158+
}
159+
}
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)