Testovanie
Sentinel::fake() vymení manažéra — vo fasáde aj v kontajneri, takže sa podvrhne aj injektovaný SentinelManager — za nahrávací fake, ktorý nepotrebuje kľúče a nezapisuje pečate ani záznamy denníka:
use RoundlyConsulting\Sentinel\Enums\VerificationStatus;
use RoundlyConsulting\Sentinel\Facades\Sentinel;
$fake = Sentinel::fake();
$fake->fakeStatus($invoice, VerificationStatus::Tampered, 'financial', changed: ['a:amount']);
$this->get("/invoices/{$invoice->id}")->assertStatus(409);
Sentinel::assertVerified($invoice);
Sentinel::assertNothingAcknowledged();
$fake->fakeLedgerFindings(new LedgerFinding(LedgerFindingKind::ChainBroken, 3, 17, Invoice::class, 1, 'financial', 'previous digest', 'mysql'));
$this->artisan('sentinel:verify --ledger --allow-empty')->assertExitCode(1);Produkčná sémantika, ktorú zachováva
- Definície kompiluje skutočný register: neznáma pečať, model bez pečatí alebo neplatná definícia vyhodí výnimku presne ako v produkcii.
- Politika zápisu do zmeneného modelu platí aj pre naskriptované stavy: pri refuse vyhodí Eloquent aktualizácia modelu podvrhnutého ako zmenený TamperedModelException.
- Dôvody, aktéri, politika potvrdenia aj sealing.allow_suspension sa vynucujú s tými istými výnimkami.
- Idempotencia (aj middleware jobov) a nonce bežia na skutočnom stavovom automate nad úložiskami v pamäti: prehratie, 409, 422, jediné spotrebovanie.
- sentinel.signed a verify() vrátia syntetický VerifiedSignature (id kľúča fake), pokiaľ ho nenaskriptujete; odchádzajúce sign() požiadavku zaznamená a vráti nepodpísanú.
- Kľúče: kruhy, algoritmy, kid, labely aj dôvody sa overujú a do skutočného úložiska sa nič nedostane. Importy sa rozparsujú ako v produkcii (vrátane obsadeného kid). Vygenerované, importované a rotáciou pridané databázové kľúče potom vracajú findKey(), listKeys() aj currentKey() s efektívnym stavom a vlastníkom; rotateKey() vychádza z kľúča, ktorý práve podpisuje, zosadí podvrhnuté kľúče, ktoré by ešte mohli podpisovať, a kľúč vlastného drivera odmietne; revokeKey() a retireKey() vracajú dátumy ako v produkcii.
- MAC: verifyMac() zachová produkčné kontroly kruhu, kid, stavu a algoritmu kľúča (podvrhnuté aj skutočné kľúče vrátane zoznamu odvolaní) aj kódovania a dĺžky MAC — nepočíta sa len samotný MAC, takže správne formátovaný MAC pre kľúč, ktorý overuje, sa prijme. mac() skontroluje kruh a podpisový kľúč a vráti zástupný IssuedMac, správne formátovaný a stabilný, no nikdy nie skutočný MAC. Odmietnutia sa zaznamenajú a vyhodia ako v produkcii.
- Zápisy a modely: overenie neuloženého modelu vyhodí SealingFailedException, zmena primárneho kľúča zapečateného riadku SealingMisconfiguredException, increment() prepečatí pečať pokrývajúcu updated_at a unseal() vráti, či bolo čo odpečatiť.
- Skeny rešpektujú where a progress, nad maxFindings nastavia truncated a s checkSchema odmietnu zapečatený stĺpec, ktorý tabuľke chýba; reseal() uplatní onlyOutdated, upgradeFormat aj fromKeyId; a fakeLedgerFindings() riadi verifyLedger() so skutočnou sémantikou LedgerReport, takže sentinel:verify --ledger pri naskriptovanom porušení skončí kódom 1.
- Zaznamená každé volanie, aj cez HasSeals, handly a pod-prístupy — a nespustí žiadne udalosti.
Priznané zjednodušenia: fake nemá kľúče pre pečate, takže reseal() započíta každý neporušený alebo zastaraný riadok ako prepečatený (nevie rozlíšiť riadok, ktorý už aktuálny kľúč má) a sealMissing() prevezme každý riadok, ku ktorému nemá históriu.
Ovládanie
Predvolené overenie je Intact. Každé ovládanie vracia fake, takže sa dajú reťaziť, a ovládanie aj asercie sa dajú volať aj staticky na fasáde:
| Ovládanie | Účinok |
|---|---|
fakeStatus(Model $model, VerificationStatus $status, ?string $seal = null, ?array $changed = null, ?string $reason = null) | Každé overenie modelu (jednej pečate alebo všetkých) vráti tento stav, kým ho nezmeníte — alebo kým fake pečať znovu nezapečatí či nepotvrdí. Missing má predvolene seal_deleted (ktoré seal() odmietne); pre nikdy nezapečatený riadok nastavte never_sealed. |
fakeStatusOnce(...) | Len najbližšie overenie. |
fakeVerifiedSignature(?VerifiedSignature $signature = null) | Čo vráti overenie podpisu. |
rejectSignatures(SignatureRejection $reason) | Každé overenie podpisu sa odmietne (401). |
fakeVerifiedMac(?KeyInfo $key = null) | Každé overenie MAC na kruhu pre MAC uspeje a vráti tento kľúč (null = syntetický kľúč hmac-sha256 so zadaným kruhom a kid, driver fake); kid, kľúč ani kódovanie sa nekontrolujú. Zruší rejectMacs(). |
rejectMacs(MacRejection $reason) | Každé overenie MAC na kruhu pre MAC sa odmietne s týmto dôvodom (MacVerificationException). |
fakeLedgerFindings(LedgerFinding ...$findings) | Každé overenie denníka hlási tieto zistenia, kým metódu nezavoláte znova — bez argumentov pre čistý denník. |
recorded(?string $method = null) | list<RecordedCall> (method, arguments, result). |
use RoundlyConsulting\Sentinel\DataTransferObjects\KeyInfo;
use RoundlyConsulting\Sentinel\DataTransferObjects\LedgerFinding;
use RoundlyConsulting\Sentinel\DataTransferObjects\VerifiedSignature;
use RoundlyConsulting\Sentinel\Enums\{Algorithm, KeyStatus, LedgerFindingKind, MacRejection, SignatureRejection};
$fake->fakeStatusOnce($invoice, VerificationStatus::Stale, 'financial'); // the next verification only
$fake->fakeVerifiedSignature(new VerifiedSignature('sig1', 'http', 'acme-2026-10', Algorithm::Ed25519, time(), null, 'n-1', null, ['@method'], 'partner', 7));
$fake->rejectSignatures(SignatureRejection::Replayed); // sentinel.signed answers 401
$fake->fakeVerifiedMac(new KeyInfo('logs', 'billing-1', Algorithm::HmacSha256, KeyStatus::VerifyOnly, 'database', false, label: 'billing'));
$fake->rejectMacs(MacRejection::Mismatch); // every verifyMac() throws
$fake->fakeLedgerFindings(new LedgerFinding(LedgerFindingKind::AnchorAhead, 12, null, null, null, null, 'anchor seq 14 > 12', 'mysql'));
$fake->fakeLedgerFindings(); // a clean ledger again
foreach ($fake->recorded('acknowledge') as $call) {
$call->method; // 'acknowledge'
$call->arguments; // the request DTO, or the list of scalar arguments
$call->result; // what the fake returned
}Asercie
Štyridsať asercií, každá prechádza a zlyháva ako v PHPUnite — neúspešná vyhodí ExpectationFailedException:
| Oblasť | Asercie |
|---|---|
| Pečate | assertSealed, assertNotSealed, assertNothingSealed, assertVerified, assertNotVerified, assertNothingVerified, assertAcknowledged, assertNotAcknowledged, assertNothingAcknowledged, assertUnsealed, assertNothingUnsealed, assertSealingSuspended, assertSealingNotSuspended |
| Hromadné operácie a denník | assertScanned, assertResealed, assertNothingResealed, assertCheckpointed, assertLedgerVerified |
| Kľúče | assertKeyGenerated, assertKeyImported, assertKeyRotated, assertKeyRevoked, assertKeyRetired, assertNoKeyChanges |
| Idempotencia a nonce | assertIdempotentRun, assertNoIdempotentRuns, assertIdempotencyKeyForgotten, assertNonceIssued, assertNoNoncesIssued, assertNonceConsumed, assertNonceNotConsumed, assertSingleUseUrlIssued, assertPruned |
| Podpisy | assertRequestSigned, assertNothingSigned, assertSignatureVerified |
| MAC | assertMacVerified, assertMacNotVerified, assertMacComputed, assertNoMacsComputed |
$fake = Sentinel::fake();
$invoice->update(['amount' => '12.00']); // through the model trait
$fake->assertSealed($invoice, 'financial', fn (SealResult $seal) => $seal->event === SealEvent::Resealed);
Sentinel::idempotency()->run('charge:42', 'billing', fn () => ['ok' => true]);
Sentinel::idempotency()->run('charge:42', 'billing', fn () => ['ok' => true]);
$fake->assertIdempotentRun('charge:42', replayed: true);
Http::fake();
Http::withSignature('acme-2026-10')->post('https://partner.example/events', []);
$fake->assertRequestSigned('acme-2026-10');Skutočné pečate v testoch
Factory zapečatiteľného modelu potrebuje podpisový kľúč a testovacie prostredie ho zvyčajne nemá — prvé create() vyhodí NoSigningKeyException, ktorej správa odkáže sem. Pridajte do testovacej triedy WithSentinelKeys: pred každým testom dostane každý kruh z konfigurácie bez kľúča nový dočasný kľúč HMAC-SHA-256 (kid test-<ring>, len v pamäti procesu — nič sa nezapíše do .env ani do databázy):
use RoundlyConsulting\Sentinel\Testing\WithSentinelKeys;
uses(TestCase::class, RefreshDatabase::class, WithSentinelKeys::class)->in('Feature');SentinelTestKeys::install(app(), Algorithm::Ed25519, rings: ['http']) urobí to isté na požiadanie — s iným algoritmom alebo pre databázový kruh (zreťazený za kľúčom z konfigurácie). Kruh, ktorý už nakonfigurovaný kľúč má, ostane nedotknutý.
Úložiská v pamäti
Fake drží idempotenčné kľúče a nonce vo vlastných InMemoryIdempotencyStore / InMemoryNonceStore — jeden na fake, takže medzi testami nič nepresiakne. Ak chcete spustiť skutočného manažéra bez tabuliek, naviažte jedno z nich samostatne:
use RoundlyConsulting\Sentinel\Contracts\IdempotencyStore;
use RoundlyConsulting\Sentinel\Testing\InMemoryIdempotencyStore;
$this->app->instance(IdempotencyStore::class, new InMemoryIdempotencyStore);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 kryptomienOdoslaní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.