Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
..
/vendor
.phpunit.cache
.phpunit.result.cache
.phpunit.result.cache
composer.lock
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased Changes

### Added

- Introduces a `Cspray\DatabaseTestCase\AbstractConnectionAdapter` for implementing functionality common across all `Cspray\DatabaseTestCase\ConnectionAdapter` implementations.
- Provides the `Cspray\DatabaseTestCase\AmpPostgresConnectionAdapter` for working with the amphp/postgres library.

### Changed

- The `Cspray\DatabaseTestCase\PdoConnectionAdapter` now extends the new `AbstractConnectionAdapter` and has been simplified.
- Added `declare(strict_types=1)` in all files it was missing.

## [0.1.0](https://github.com/cspray/database-test-case/releases/tag/0.1.0) - 2023-03-02

### Added

- Adds a `Cspray\DatabaseTestCase\DatabaseTestCase` that allows for testing database interactions.
- Adds a `Cspray\DatabaseTestCase\ConnectionAdapter` interface that is responsible for actual calls to an underlying connection.
- Provides a `Cspray\DatabaseTestCase\PdoConnectionAdapter` with support for PostgreSQL databases.
- Provides a mechanism for loading fixtures per test, using the Attribute `#[LoadFixture]` and providing an implementation of `Cspray\DatabaseTestCase\Fixture`.
- Provides a mechanism for retrieving the state of a database table at a given point in time with `Cspray\DatabaseTestCase\DatabaseRepresentation\Table`.
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
"database"
],
"license": ["MIT"],
"minimum-stability": "beta",
"prefer-stable": true,
"require": {
"php": "^8.2",
"phpunit/phpunit": "^10.0"
},
"require-dev": {
"ext-pdo": "*",
"ext-pdo_pgsql": "*",
"amphp/postgres": "^v2.0.0-beta.2",
"roave/security-advisories": "dev-latest"
},
"autoload": {
Expand All @@ -27,6 +30,7 @@
}
},
"suggest": {
"ext-pdo": "To enable the PdoConnectionAdapter"
"ext-pdo": "To enable the PdoConnectionAdapter",
"amphp/postgres": "To enable the AmpPostgresConnectionAdapter"
}
}
2 changes: 1 addition & 1 deletion docker/php/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ RUN apt-get update -y \
&& apt-get upgrade -y

RUN apt-get install git libsodium-dev libzip-dev libpq-dev -y
RUN docker-php-ext-install sodium zip pdo pdo_pgsql
RUN docker-php-ext-install sodium zip pdo pdo_pgsql pgsql

RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

Expand Down
67 changes: 67 additions & 0 deletions src/AbstractConnectionAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

use Cspray\DatabaseTestCase\DatabaseRepresentation\Row;
use Cspray\DatabaseTestCase\DatabaseRepresentation\Table;
use Cspray\DatabaseTestCase\Exception\UnableToGetTable;
use Throwable;

abstract class AbstractConnectionAdapter implements ConnectionAdapter {

final public function loadFixture(Fixture $fixture, Fixture ...$additionalFixture) : void {
/** @var Fixture $f */
foreach ([$fixture, ...$additionalFixture] as $f) {
foreach ($f->getFixtureRecords() as $fixtureRecord) {
$sql = $this->generateInsertSqlForParameters($fixtureRecord);
$parameters = $fixtureRecord->parameters;
$this->executeInsertSql($sql, $parameters);
}
}
}

final public function getTable(string $name) : Table {
try {
$table = Table::forName($name);
foreach ($this->executeSelectAllSql($name) as $row) {
$r = null;
foreach ($row as $col => $val) {
$r = $r === null ? Row::forValue($col, $val) : $r->withValue($col, $val);
}
$table = $table->withRow($r);
}
return $table;
} catch (Throwable $throwable) {
throw new UnableToGetTable(
message: sprintf('Unable to fetch table "%s", please check previous Exception for more details.', $name),
previous: $throwable
);
}
}

protected function generateInsertSqlForParameters(FixtureRecord $fixtureRecord) : string {
$table = $fixtureRecord->table;
$parameters = $fixtureRecord->parameters;
$colsString = implode(
', ',
array_keys($parameters)
);
$paramString = implode(
', ',
array_map(static fn(string $col) => ':' . $col, array_keys($parameters))
);
return <<<SQL
INSERT INTO $table ($colsString)
VALUES ($paramString)
SQL;
}

abstract protected function executeInsertSql(string $sql, array $parameters) : void;

/**
* @return list<array<string, mixed>>
* @throws Throwable
*/
abstract protected function executeSelectAllSql(string $table) : array;

}
65 changes: 65 additions & 0 deletions src/AmpPostgresConnectionAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

use Amp\Postgres\PostgresConfig;
use Amp\Postgres\PostgresLink;
use Cspray\DatabaseTestCase\Exception\MissingRequiredComposerPackage;
use function Amp\Postgres\connect;

if (! interface_exists(PostgresLink::class)) {
throw new MissingRequiredComposerPackage('You must install amphp/postgres to use ' . AmpPostgresConnectionAdapter::class);
}

class AmpPostgresConnectionAdapter extends AbstractConnectionAdapter {

private ?PostgresLink $connection = null;

public function __construct(
private readonly ConnectionAdapterConfig $adapterConfig
) {}

public function establishConnection() : void {
$this->connection = connect(
PostgresConfig::fromString(sprintf(
'db=%s host=%s port=%d user=%s pass=%s',
$this->adapterConfig->database,
$this->adapterConfig->host,
$this->adapterConfig->port,
$this->adapterConfig->user,
$this->adapterConfig->password
))
);
}

public function onTestStart() : void {
$this->connection->query('START TRANSACTION');
}

public function onTestStop() : void {
$this->connection->query('ROLLBACK');
}

public function closeConnection() : void {
$this->connection->close();
$this->connection = null;
}

public function getUnderlyingConnection() : PostgresLink {
return $this->connection;
}

protected function executeInsertSql(string $sql, array $parameters) : void {
$statement = $this->connection->prepare($sql);
$statement->execute($parameters);
}

protected function executeSelectAllSql(string $table) : array {
$result = $this->connection->query(sprintf('SELECT * FROM %s', $table));
$rows = [];
while ($row = $result->fetchRow()) {
$rows[] = $row;
}
return $rows;
}
}
2 changes: 1 addition & 1 deletion src/ConnectionAdapter.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
2 changes: 1 addition & 1 deletion src/ConnectionAdapterConfig.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
2 changes: 1 addition & 1 deletion src/DatabaseRepresentation/Row.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase\DatabaseRepresentation;

Expand Down
5 changes: 1 addition & 4 deletions src/DatabaseTestCase.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand All @@ -7,9 +7,6 @@
use PHPUnit\Framework\TestCase;
use ReflectionMethod;

/**
*
*/
abstract class DatabaseTestCase extends TestCase {

private static ?ConnectionAdapter $connectionAdapter;
Expand Down
2 changes: 1 addition & 1 deletion src/Exception/ConnectionNotYetEstablished.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase\Exception;

Expand Down
2 changes: 1 addition & 1 deletion src/Exception/InvalidFixture.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase\Exception;

Expand Down
9 changes: 9 additions & 0 deletions src/Exception/MissingRequiredComposerPackage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase\Exception;

use RuntimeException;

final class MissingRequiredComposerPackage extends RuntimeException {

}
2 changes: 1 addition & 1 deletion src/Exception/MissingRequiredExtension.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase\Exception;

Expand Down
2 changes: 1 addition & 1 deletion src/Exception/UnableToGetTable.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase\Exception;

Expand Down
2 changes: 1 addition & 1 deletion src/Fixture.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
2 changes: 1 addition & 1 deletion src/FixtureRecord.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
2 changes: 1 addition & 1 deletion src/LoadFixture.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
59 changes: 11 additions & 48 deletions src/PdoConnectionAdapter.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand All @@ -13,7 +13,7 @@
throw new MissingRequiredExtension('You must enable ext-pdo to use the ' . PdoConnectionAdapter::class);
}

final class PdoConnectionAdapter implements ConnectionAdapter {
final class PdoConnectionAdapter extends AbstractConnectionAdapter {

private ?PDO $connection = null;

Expand Down Expand Up @@ -49,59 +49,22 @@ public function closeConnection() : void {
$this->connection = null;
}

public function loadFixture(Fixture $fixture, Fixture ...$additionalFixture) : void {
/** @var Fixture $f */
foreach ([$fixture, ...$additionalFixture] as $f) {
foreach ($f->getFixtureRecords() as $fixtureRecord) {
$statement = $this->connection->prepare($this->generateInsertSqlForParameters($fixtureRecord));
foreach ($fixtureRecord->parameters as $col => $val) {
$statement->bindValue($col, $val);
}
$statement->execute();
}
}
}

public function getUnderlyingConnection() : PDO {
return $this->connection;
}

public function getTable(string $name) : Table {
try {
$query = sprintf('SELECT * FROM %s', $name);
$result = $this->connection->query($query)->fetchAll(PDO::FETCH_ASSOC);
$table = Table::forName($name);
foreach ($result as $row) {
$r = null;
foreach ($row as $col => $val) {
$r = $r === null ? Row::forValue($col, $val) : $r->withValue($col, $val);
}
$table = $table->withRow($r);
}
return $table;
} catch (PDOException $pdoException) {
throw new UnableToGetTable(
message: sprintf('Unable to fetch table "%s", please check previous Exception for more details.', $name),
previous: $pdoException
);

protected function executeInsertSql(string $sql, array $parameters) : void {
$statement = $this->getUnderlyingConnection()->prepare($sql);
foreach ($parameters as $col => $val) {
$statement->bindValue($col, $val);
}
$statement->execute();
}

private function generateInsertSqlForParameters(FixtureRecord $fixtureRecord) : string {
$table = $fixtureRecord->table;
$parameters = $fixtureRecord->parameters;
$colsString = implode(
', ',
array_keys($parameters)
);
$paramString = implode(
', ',
array_map(static fn(string $col) => ':' . $col, array_keys($parameters))
);
return <<<SQL
INSERT INTO $table ($colsString)
VALUES ($paramString)
SQL;
protected function executeSelectAllSql(string $table) : array {
$query = sprintf('SELECT * FROM %s', $table);
return $this->connection->query($query)->fetchAll(PDO::FETCH_ASSOC);
}

}
2 changes: 1 addition & 1 deletion src/PdoDriver.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
2 changes: 1 addition & 1 deletion src/SingleRecordFixture.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);

namespace Cspray\DatabaseTestCase;

Expand Down
Loading