JsonInvoiceStateParallelismTest.php
Pfad: tests/Rechnung/JsonInvoiceStateParallelismTest.php
Ext: php
Größe: 1908 Bytes
Geändert: 2026-07-16 18:23:01+02
<?php
declare(strict_types=1);
namespace Tests\Rechnung;
use Demo\Domain\Rechnung\InvoiceRecord;
use Demo\Infrastructure\Rechnung\JsonInvoiceStateAdapter;
use PHPUnit\Framework\TestCase;
final class JsonInvoiceStateParallelismTest extends TestCase
{
public function testTwentyWorkersDoNotLoseUpdates(): void
{
self::assertTrue(function_exists('pcntl_fork'), 'pcntl ist fuer das Parallel-Gateway erforderlich');
$statePath = tempnam(sys_get_temp_dir(), 'invoice-state-parallel-');
self::assertIsString($statePath);
unlink($statePath);
$children = [];
for ($worker = 0; $worker < 20; ++$worker) {
$pid = pcntl_fork();
self::assertNotSame(-1, $pid);
if ($pid === 0) {
$adapter = new JsonInvoiceStateAdapter($statePath);
$adapter->withExclusiveLock(function () use ($adapter, $worker): void {
$state = $adapter->load();
usleep(1_000 + ($worker % 5) * 1_000);
$record = new InvoiceRecord('source-' . $worker, 'invoice-' . $worker, 'DISCOVERED');
$adapter->save($state->withRecord($record));
});
exit(0);
}
$children[] = $pid;
}
foreach ($children as $pid) {
pcntl_waitpid($pid, $status);
self::assertTrue(pcntl_wifexited($status));
self::assertSame(0, pcntl_wexitstatus($status));
}
try {
$state = (new JsonInvoiceStateAdapter($statePath))->load();
self::assertCount(20, $state->all());
for ($worker = 0; $worker < 20; ++$worker) {
self::assertNotNull($state->findBySourceId('source-' . $worker));
}
} finally {
@unlink($statePath);
@unlink($statePath . '.lock');
}
}
}