← Code-Übersicht

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');
        }
    }
}