NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Testing for Laravel

Záznamník zámkov

SQLite kompiluje lockForUpdate() na prázdny reťazec, takže test nerozozná uzamknuté čítanie od neuzamknutého — a skutočné FOR UPDATE je na SQLite syntaktická chyba. Dva pozorovateľné varianty zapisujú každý zámok aj hĺbku transakcie, v ktorej prebehol, do LockRecorder. Zachytia súbehy, ktoré by na SQLite zostali neviditeľné: upozornenie, ktoré sa nikdy neotvorilo, úroveň eskalácie upozornenú dvakrát, predaný tovar nad stav skladu.

Variant A — model sa dá rozšíriť

Pridajte trait RecordsLocks do testovacej podtriedy. Vráti LockRecordingBuilder, ktorý zaznamená každé volanie lock(), lockForUpdate() a sharedLock() a potom ho odovzdá ďalej, takže príkaz prebehne nezmenený:

use Illuminate\Support\Facades\DB;
use RoundlyConsulting\Testing\Fixtures\Concerns\RecordsLocks;
use RoundlyConsulting\Testing\Fixtures\LockRecorder;

// Variant A — the model is subclassable:
final class RecordingCoupon extends Coupon { use RecordsLocks; }

it('locks the coupon inside the transaction', function (): void {
    LockRecorder::flush();

    DB::transaction(fn () => RecordingCoupon::query()->lockForUpdate()->get());

    expect(LockRecorder::recorded())->toHaveCount(1)
        ->and(LockRecorder::recorded()[0]['marker'])->toBe('lock-for-update')
        ->and(LockRecorder::recorded()[0]['transactionDepth'])->toBe(1);
});

Variant B — zámok je v kóde, ktorý nemožno vymeniť

Nainštalujte LockRecordingGrammar na SQLite pripojenie. Zámok skompiluje na koncový SQL komentár, ktorý SQLite bez problémov vykoná, a listenForMarkers() cez DB::listen() zaznamená každý označený dopyt:

use Illuminate\Support\Facades\DB;
use RoundlyConsulting\Testing\Fixtures\LockRecorder;
use RoundlyConsulting\Testing\Fixtures\LockRecordingGrammar;

// Variant B — the model is not subclassable, or the lock is buried in an action:
it('locks the shop row', function (): void {
    $connection = DB::connection();
    $connection->setQueryGrammar(new LockRecordingGrammar($connection));

    LockRecorder::flush();
    LockRecorder::listenForMarkers();

    DB::transaction(fn () => Shop::query()->lockForUpdate()->get());

    expect(LockRecorder::recorded()[0]['sql'])->toContain('/* lock-for-update */')
        ->and(LockRecorder::recorded()[0]['transactionDepth'])->toBe(1);
});

Prečo záleží na hĺbke transakcie

Zámok, ktorý skončí v savepointe uvoľnenom ešte pred zápisom do účtovnej knihy, je skutočná trieda chýb — práve kvôli nej neprešiel zamýšľaný pomocník na zamykanie. Hĺbka ju zviditeľní; vnorená transakcia zaznamená hĺbku 2:

DB::transaction(function (): void {
    DB::transaction(function (): void {
        RecordingCoupon::query()->lockForUpdate()->get();
    });
});

// The nested transaction is a savepoint — depth 2, not the outer transaction.
expect(LockRecorder::recorded()[0]['transactionDepth'])->toBe(2);

API LockRecorder

MetódaÚčel
LockRecorder::flush()Vyprázdni záznam — volajte v setUp() a pred spustením súbehu.
LockRecorder::recorded()Zámky od posledného vyprázdnenia v poradí: zoznam položiek marker, sql a transactionDepth.
LockRecorder::listenForMarkers()Zapojenie variantu B — DB::listen(), ktorý zaznamená každý označený dopyt. Zavolajte raz, po nainštalovaní gramatiky.
LockRecorder::record($marker, $transactionDepth, $sql = '')Ručné zaznamenanie jedného zámku.

Značky sú lock-for-update, lock-shared a lock-custom (textová klauzula zámku). Variant A zaznamená zámok pri volaní metódy buildera s prázdnym sql; variant B zaznamená celý vykonaný príkaz.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.