Skip to content

Commit e39a08f

Browse files
authored
feature: postgresql phpunit bridge (#2299)
- wrap tests in transactions - add phpunit postgresql bridge as optional dependency of postgresql bundle
1 parent 892e5e2 commit e39a08f

File tree

46 files changed

+1050
-91
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1050
-91
lines changed

.codecov.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ component_management:
172172
name: adapter-postgresql
173173
paths:
174174
- src/adapter/etl-adapter-postgresql/**
175+
- component_id: bridge-phpunit-postgresql
176+
name: bridge-phpunit-postgresql
177+
paths:
178+
- src/bridge/phpunit/postgresql/**
175179
- component_id: bridge-phpunit-telemetry
176180
name: bridge-phpunit-telemetry
177181
paths:

.github/workflows/monorepo-split.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ jobs:
104104
split_repository: 'symfony-telemetry-bundle'
105105
- local_path: 'src/bridge/telemetry/otlp'
106106
split_repository: 'telemetry-otlp-bridge'
107+
- local_path: 'src/bridge/phpunit/postgresql'
108+
split_repository: 'phpunit-postgresql-bridge'
107109
- local_path: 'src/bridge/phpunit/telemetry'
108110
split_repository: 'phpunit-telemetry-bridge'
109111

composer.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
"flow-php/telemetry": "self.version",
117117
"flow-php/telemetry-otlp-bridge": "self.version",
118118
"flow-php/types": "self.version",
119+
"flow-php/phpunit-postgresql-bridge": "self.version",
119120
"flow-php/phpunit-telemetry": "self.version",
120121
"flow-php/phpunit-telemetry-bridge": "self.version"
121122
},
@@ -152,6 +153,7 @@
152153
"src/bridge/symfony/postgresql-messenger/src/Flow",
153154
"src/bridge/symfony/telemetry-bundle/src/Flow",
154155
"src/bridge/telemetry/otlp/src/Flow",
156+
"src/bridge/phpunit/postgresql/src/Flow",
155157
"src/bridge/phpunit/telemetry/src/Flow",
156158
"src/cli/src/Flow",
157159
"src/core/etl/src/Flow",
@@ -194,6 +196,7 @@
194196
"src/bridge/filesystem/azure/src/Flow/Filesystem/Bridge/Azure/DSL/functions.php",
195197
"src/bridge/monolog/http/src/Flow/Bridge/Monolog/Http/DSL/functions.php",
196198
"src/bridge/monolog/telemetry/src/Flow/Bridge/Monolog/Telemetry/DSL/functions.php",
199+
"src/bridge/phpunit/postgresql/src/Flow/Bridge/PHPUnit/PostgreSQL/DSL/functions.php",
197200
"src/bridge/openapi/specification/src/Flow/Bridge/OpenAPI/Specification/DSL/functions.php",
198201
"src/bridge/psr7/telemetry/src/Flow/Bridge/Psr7/Telemetry/DSL/functions.php",
199202
"src/bridge/psr18/telemetry/src/Flow/Bridge/Psr18/Telemetry/DSL/functions.php",
@@ -253,6 +256,7 @@
253256
"src/bridge/symfony/postgresql-messenger/tests/Flow",
254257
"src/bridge/symfony/telemetry-bundle/tests/Flow",
255258
"src/bridge/telemetry/otlp/tests/Flow",
259+
"src/bridge/phpunit/postgresql/tests/Flow",
256260
"src/bridge/phpunit/telemetry/tests/Flow",
257261
"src/cli/tests/Flow",
258262
"src/core/etl/tests/Flow",
@@ -321,6 +325,7 @@
321325
"@test:bridge:monolog-http",
322326
"@test:bridge:monolog-telemetry",
323327
"@test:bridge:openapi-specification",
328+
"@test:bridge:phpunit-postgresql",
324329
"@test:bridge:phpunit-telemetry",
325330
"@test:bridge:psr7-telemetry",
326331
"@test:bridge:psr18-telemetry",
@@ -496,6 +501,10 @@
496501
"tools/phpunit/vendor/bin/phpunit --testsuite=adapter-postgresql-unit --log-junit ./var/phpunit/logs/adapter-postgresql-unit.junit.xml --coverage-clover=./var/phpunit/coverage/clover/adapter-postgresql-unit.coverage.xml",
497502
"tools/phpunit/vendor/bin/phpunit --testsuite=adapter-postgresql-integration --log-junit ./var/phpunit/logs/adapter-postgresql-integration.junit.xml --coverage-clover=./var/phpunit/coverage/clover/adapter-postgresql-integration.coverage.xml"
498503
],
504+
"test:bridge:phpunit-postgresql": [
505+
"tools/phpunit/vendor/bin/phpunit --testsuite=bridge-phpunit-postgresql-unit --log-junit ./var/phpunit/logs/bridge-phpunit-postgresql-unit.junit.xml --coverage-clover=./var/phpunit/coverage/clover/bridge-phpunit-postgresql-unit.coverage.xml",
506+
"tools/phpunit/vendor/bin/phpunit --testsuite=bridge-phpunit-postgresql-integration --log-junit ./var/phpunit/logs/bridge-phpunit-postgresql-integration.junit.xml --coverage-clover=./var/phpunit/coverage/clover/bridge-phpunit-postgresql-integration.coverage.xml"
507+
],
499508
"test:bridge:phpunit-telemetry": [
500509
"tools/phpunit/vendor/bin/phpunit --testsuite=bridge-phpunit-telemetry-unit --log-junit ./var/phpunit/logs/bridge-phpunit-telemetry-unit.junit.xml --coverage-clover=./var/phpunit/coverage/clover/bridge-phpunit-telemetry-unit.coverage.xml"
501510
],
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# PHPUnit PostgreSQL Bridge
2+
3+
PHPUnit extension for flow-php/postgresql that can wrap tests in transactions.
4+
5+
- [Back](/documentation/introduction.md)
6+
- [Packagist](https://packagist.org/packages/flow-php/phpunit-postgresql-bridge)
7+
- [➡️ Installation](/documentation/installation/packages/phpunit-postgresql-bridge.md)
8+
- [GitHub](https://github.com/flow-php/phpunit-postgresql-bridge)
9+
- [API Reference](/documentation/api/bridge/phpunit/postgresql)
10+
11+
[TOC]
12+
13+
## Installation
14+
15+
For detailed installation instructions, see the [installation page](/documentation/installation/packages/phpunit-postgresql-bridge.md).
16+
17+
## Usage
18+
19+
### PHPUnit Extension Configuration
20+
21+
Add the extension to your `phpunit.xml` or `phpunit.xml.dist`:
22+
23+
```xml
24+
<extensions>
25+
<bootstrap class="Flow\Bridge\PHPUnit\PostgreSQL\PostgreSQLExtension"/>
26+
</extensions>
27+
```
28+
29+
### Connecting to the Database
30+
31+
Use the `static_pgsql_client()` DSL function instead of `pgsql_client()` in your tests:
32+
33+
```php
34+
use function Flow\Bridge\PHPUnit\PostgreSQL\DSL\static_pgsql_client;
35+
use function Flow\PostgreSql\DSL\pgsql_connection_dsn;
36+
37+
$client = static_pgsql_client(pgsql_connection_dsn($dsn));
38+
```
39+
40+
When the PHPUnit extension is active, this returns a cached client wrapped in a transaction. When inactive, it delegates directly to `PgSqlClient::connect`.
41+
42+
For Symfony bundle integration, see the [PostgreSQL Bundle documentation](/documentation/components/bridges/symfony-postgresql-bundle.md).
43+
44+
### How It Works
45+
46+
1. When PHPUnit starts, the extension enables `StaticClient` caching
47+
2. Before each test, the extension rolls back any previous transaction and begins a new one
48+
3. All database operations during the test run inside this transaction
49+
4. When the test ends, the next test's preparation rolls back all changes
50+
5. After all tests finish, all connections are closed
51+
52+
This means database changes made during a test are automatically undone, keeping your test database clean.
53+
54+
### Nested Transactions
55+
56+
The flow-php/postgresql library supports nested transactions via SAVEPOINTs. If your test code calls `beginTransaction()`, it creates a savepoint (level 2+). When the extension rolls back (level 1), all nested changes are undone.
57+
58+
### Skipping Transaction Rollback
59+
60+
Use the `#[SkipTransactionRollback]` attribute to opt out of transaction wrapping for specific tests:
61+
62+
```php
63+
use Flow\Bridge\PHPUnit\PostgreSQL\SkipTransactionRollback;
64+
65+
// Skip for an entire class
66+
#[SkipTransactionRollback]
67+
final class MigrationTest extends TestCase
68+
{
69+
// Tests in this class will NOT be wrapped in a transaction
70+
}
71+
72+
// Skip for a specific method
73+
final class SomeTest extends TestCase
74+
{
75+
#[SkipTransactionRollback]
76+
public function test_something_that_needs_real_commits(): void
77+
{
78+
// This test will NOT be wrapped in a transaction
79+
}
80+
}
81+
82+
// Skip via abstract parent class (applies to all subclasses)
83+
#[SkipTransactionRollback]
84+
abstract class AbstractMigrationTest extends TestCase
85+
{
86+
}
87+
88+
final class ConcreteMigrationTest extends AbstractMigrationTest
89+
{
90+
// Inherits skip behavior from parent
91+
}
92+
```

documentation/components/bridges/symfony-postgresql-bundle.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,54 @@ The `MessengerCatalogProvider` is registered automatically when `messenger.enabl
358358

359359
For full documentation, see the [Symfony PostgreSQL Messenger Bridge](/documentation/components/bridges/symfony-postgresql-messenger-bridge.md).
360360

361+
## Test Transaction Rollback
362+
363+
The bundle integrates with [flow-php/phpunit-postgresql-bridge](/documentation/components/bridges/phpunit-postgresql-bridge.md) to automatically wrap each PHPUnit test in a database transaction and roll it back after the test finishes — keeping your test database clean without manual teardown.
364+
365+
### Setup
366+
367+
1. Install the PHPUnit bridge:
368+
369+
```bash
370+
composer require --dev flow-php/phpunit-postgresql-bridge:~--FLOW_PHP_VERSION--
371+
```
372+
373+
2. Register the PHPUnit extension in `phpunit.xml.dist`:
374+
375+
```xml
376+
<extensions>
377+
<bootstrap class="Flow\Bridge\PHPUnit\PostgreSQL\PostgreSQLExtension"/>
378+
</extensions>
379+
```
380+
381+
3. Enable transaction rollback per connection in a test-environment config:
382+
383+
```yaml
384+
# config/packages/test/flow_postgresql.yaml
385+
flow_postgresql:
386+
connections:
387+
default:
388+
test_transaction_rollback: true
389+
readonly:
390+
test_transaction_rollback: false # default, no wrapping
391+
```
392+
393+
When `test_transaction_rollback` is `true`, the bundle replaces `PgSqlClient::connect` with `StaticClient::connect` for that connection. This ensures the exact same `Client` instance is reused across kernel reboots within the same test, so the transaction started by the PHPUnit extension covers all database operations performed by your services.
394+
395+
### How It Works
396+
397+
1. PHPUnit starts → extension enables `StaticClient` caching
398+
2. Before each test → extension rolls back the previous transaction and begins a new one
399+
3. Symfony kernel boots → bundle creates the client via `StaticClient::connect()` → returns the cached instance with an active transaction
400+
4. Test runs → all queries go through the same cached client, inside the transaction
401+
5. Next test starts → extension rolls back all changes from the previous test
402+
403+
PostgreSQL supports transactional DDL, so even `CREATE TABLE` and `ALTER TABLE` statements are rolled back.
404+
405+
### Skipping Rollback
406+
407+
Use the `#[SkipTransactionRollback]` attribute to opt out for specific tests, classes, or abstract parent classes. See the [PHPUnit PostgreSQL Bridge documentation](/documentation/components/bridges/phpunit-postgresql-bridge.md) for details.
408+
361409
## Complete Example
362410

363411
```yaml

documentation/installation.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Instead of installing whole repository with all dependencies, it's recommended t
6262
- [Monolog HTTP Bridge](/documentation/installation/packages/monolog-http-bridge.md)
6363
- [Monolog Telemetry Bridge](/documentation/installation/packages/monolog-telemetry-bridge.md)
6464
- [OpenAPI Specification Bridge](/documentation/installation/packages/openapi-specification-bridge.md)
65+
- [PHPUnit PostgreSQL Bridge](/documentation/installation/packages/phpunit-postgresql-bridge.md)
6566
- [PHPUnit Telemetry Bridge](/documentation/installation/packages/phpunit-telemetry-bridge.md)
6667
- [PSR-18 Telemetry Bridge](/documentation/installation/packages/psr18-telemetry-bridge.md)
6768
- [PSR-7 Telemetry Bridge](/documentation/installation/packages/psr7-telemetry-bridge.md)
@@ -114,6 +115,7 @@ Instead of installing whole repository with all dependencies, it's recommended t
114115
| [flow-php/monolog-http-bridge](/documentation/installation/packages/monolog-http-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/monolog-http-bridge/downloads)](https://packagist.org/packages/flow-php/monolog-http-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/monolog-http-bridge/v/stable)](https://packagist.org/packages/flow-php/monolog-http-bridge) |
115116
| [flow-php/monolog-telemetry-bridge](/documentation/installation/packages/monolog-telemetry-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/monolog-telemetry-bridge/downloads)](https://packagist.org/packages/flow-php/monolog-telemetry-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/monolog-telemetry-bridge/v/stable)](https://packagist.org/packages/flow-php/monolog-telemetry-bridge) |
116117
| [flow-php/openapi-specification-bridge](/documentation/installation/packages/openapi-specification-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/openapi-specification-bridge/downloads)](https://packagist.org/packages/flow-php/openapi-specification-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/openapi-specification-bridge/v/stable)](https://packagist.org/packages/flow-php/openapi-specification-bridge) |
118+
| [flow-php/phpunit-postgresql-bridge](/documentation/installation/packages/phpunit-postgresql-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/phpunit-postgresql-bridge/downloads)](https://packagist.org/packages/flow-php/phpunit-postgresql-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/phpunit-postgresql-bridge/v/stable)](https://packagist.org/packages/flow-php/phpunit-postgresql-bridge) |
117119
| [flow-php/phpunit-telemetry-bridge](/documentation/installation/packages/phpunit-telemetry-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/phpunit-telemetry-bridge/downloads)](https://packagist.org/packages/flow-php/phpunit-telemetry-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/phpunit-telemetry-bridge/v/stable)](https://packagist.org/packages/flow-php/phpunit-telemetry-bridge) |
118120
| [flow-php/psr18-telemetry-bridge](/documentation/installation/packages/psr18-telemetry-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/psr18-telemetry-bridge/downloads)](https://packagist.org/packages/flow-php/psr18-telemetry-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/psr18-telemetry-bridge/v/stable)](https://packagist.org/packages/flow-php/psr18-telemetry-bridge) |
119121
| [flow-php/psr7-telemetry-bridge](/documentation/installation/packages/psr7-telemetry-bridge.md) | [![Total Downloads](https://poser.pugx.org/flow-php/psr7-telemetry-bridge/downloads)](https://packagist.org/packages/flow-php/psr7-telemetry-bridge) | [![Latest Stable Version](https://poser.pugx.org/flow-php/psr7-telemetry-bridge/v/stable)](https://packagist.org/packages/flow-php/psr7-telemetry-bridge) |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
seo_title: "Installing PHPUnit PostgreSQL Bridge"
3+
seo_description: >
4+
How to install flow-php/phpunit-postgresql-bridge in your PHP project using Composer.
5+
---
6+
7+
# PHPUnit PostgreSQL Bridge
8+
9+
- [⬅️️ Back](/documentation/installation.md)
10+
- [📜 Documentation](/documentation/components/bridges/phpunit-postgresql-bridge.md)
11+
- [📦 Packagist](https://packagist.org/packages/flow-php/phpunit-postgresql-bridge)
12+
13+
[TOC]
14+
15+
## Composer
16+
17+
```bash
18+
composer require flow-php/phpunit-postgresql-bridge:~--FLOW_PHP_VERSION--
19+
```
20+
21+
## Core Dependencies
22+
23+
- [flow-php/postgresql](https://packagist.org/packages/flow-php/postgresql)
24+
- [phpunit/phpunit](https://packagist.org/packages/phpunit/phpunit)

manifest.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@
151151
"path": "src/lib/postgresql",
152152
"type": "lib"
153153
},
154+
{
155+
"name": "flow-php/phpunit-postgresql-bridge",
156+
"path": "src/bridge/phpunit/postgresql",
157+
"type": "bridge"
158+
},
154159
{
155160
"name": "flow-php/phpunit-telemetry-bridge",
156161
"path": "src/bridge/phpunit/telemetry",
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<phpdocumentor
3+
configVersion="3"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns="https://www.phpdoc.org"
6+
>
7+
<title>Flow PHP</title>
8+
<paths>
9+
<output>./../web/landing/build/documentation/api/bridge/phpunit/postgresql</output>
10+
<cache>./../var/phpdocumentor/cache/bridge/phpunit/postgresql</cache>
11+
</paths>
12+
<version number="1.x">
13+
<api format="php">
14+
<source dsn="./../">
15+
<path>src/bridge/phpunit/postgresql/src</path>
16+
</source>
17+
<output>postgresql</output>
18+
<default-package-name>PHPUnit PostgreSQL</default-package-name>
19+
<visibility>public</visibility>
20+
<include-source>false</include-source>
21+
</api>
22+
</version>
23+
<setting name="template.color" value="orange"/>
24+
</phpdocumentor>

phpstan.neon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ parameters:
4848
- src/lib/types/src
4949
- src/lib/postgresql/src
5050
- src/tools/documentation/src
51+
- src/bridge/phpunit/postgresql/src
52+
- src/bridge/phpunit/postgresql/tests
5153
- src/bridge/phpunit/telemetry/src
5254
- src/bridge/phpunit/telemetry/tests
5355
- src/core/etl/tests

0 commit comments

Comments
 (0)