← Code-Übersicht

JsonInvoiceStateAdapterTest.php

Pfad: tests/Rechnung/JsonInvoiceStateAdapterTest.php

Ext: php

Größe: 1700 Bytes

Geändert: 2026-07-16 16:54:17+02

<?php
declare(strict_types=1);

namespace Tests\Rechnung;

use Demo\Domain\Rechnung\InvoiceRecord;
use Demo\Domain\Rechnung\InvoiceState;
use Demo\Domain\Rechnung\InvoiceStatus;
use Demo\Infrastructure\Rechnung\JsonInvoiceStateAdapter;
use PHPUnit\Framework\TestCase;
use RuntimeException;

/**
 * Prueft fail-closed und atomare JSON-Persistenz.
 */
final class JsonInvoiceStateAdapterTest extends TestCase
{
    private string $path;

    /** Erzeugt einen isolierten Testpfad. */
    protected function setUp(): void
    {
        $this->path = sys_get_temp_dir() . '/invoice-state-' . bin2hex(random_bytes(8)) . '.json';
    }

    /** Entfernt lokale Testartefakte. */
    protected function tearDown(): void
    {
        @unlink($this->path);
        @unlink($this->path . '.lock');
    }

    /** Korrupter State darf niemals als leerer Erstlauf gelten. */
    public function testKorruptesJsonBrichtAb(): void
    {
        file_put_contents($this->path, '{');
        $this->expectException(RuntimeException::class);
        (new JsonInvoiceStateAdapter($this->path))->load();
    }

    /** Save innerhalb des Prozesslocks bleibt atomar und reentrant. */
    public function testExklusiverRoundtrip(): void
    {
        $adapter = new JsonInvoiceStateAdapter($this->path);
        $record = new InvoiceRecord('source', 'key', InvoiceStatus::DISCOVERED);
        $adapter->withExclusiveLock(function () use ($adapter, $record): void {
            $adapter->save(InvoiceState::empty()->withRecord($record));
        });
        self::assertSame('source', $adapter->load()->all()[0]->quelleId);
        self::assertSame(InvoiceState::SCHEMA_VERSION, $adapter->load()->schemaVersion);
    }
}