Testovanie
RefreshTokens::fake() nahradí manažéra zaznamenávajúcim fake a vráti ho. Volania pritom stále bežia naozaj — tokeny sa vydávajú, uplatňujú a odvolávajú vo vašej testovacej databáze, pretože náhrada, ktorá by na redeem() odpovedala inak než skutočné úložisko, by nechala prejsť pokazený tok obnovy — a zaznamená sa každé volanie, či prišlo cez fasádu, injektovaný RefreshTokensManager, for()->issue(), sessions() / session() alebo trait HasRefreshTokens:
use RoundlyConsulting\RefreshTokens\Facades\RefreshTokens;
$fake = RefreshTokens::fake();
// … exercise your login / refresh / logout endpoints …
$fake->assertIssued(for: $user);
$fake->assertIssued($user, fn (IssueContext $context): bool => $context->accessReference === $jti);
$fake->assertRedeemed(by: $user);
$fake->assertRotated(for: $user);
$fake->assertRevoked(for: $user, reason: RevocationReason::CredentialsChanged);
$fake->assertEnriched(for: $user);
$fake->assertPruned();
$fake->assertNothingIssued(); // …and assertNothingRedeemed/Rotated/Revoked/Enriched/Pruned()| Asercia | Zachytí |
|---|---|
assertIssued(?Model $for = null, ?Closure $where = null) / assertNothingIssued() | issue(), for()->issue() a $owner->issueRefreshToken(); $where dostane IssueContext. |
assertRedeemed(?Model $by = null) / assertNothingRedeemed() | redeem() — neúspešné uplatnenie sa zaznamená bez vlastníka. |
assertRotated(?Model $for = null) / assertNothingRotated() | rotate() — zaznamená sa len ako rotácia, nie aj ako uplatnenie a vydanie. |
assertRevoked(?Model $for = null, ?RevocationReason $reason = null) / assertNothingRevoked() | revoke($plain), každé sessions()->revoke…(), session()->revoke() a metódy traitu na odvolanie. |
assertEnriched(?Model $for = null) / assertNothingEnriched() | session()->enrich(). |
assertPruned() / assertNothingPruned() | prune() a príkaz refresh-tokens:prune. |
recorded() | Všetky volania ako RecordedOperation (operation, owner, reason, context, count). |
Volanie, ktoré vyhodí výnimku, sa nezaznamená. Asercie používajú PHPUnit Assert.
Odvolanie prístupových tokenov
Namiesto skutočného revokera zaregistrujte dodaný FakeAccessTokenRevoker a overte presne, ktoré odkazy na prístupové tokeny balík žiadal zneplatniť — bez vlastného spy objektu:
use RoundlyConsulting\RefreshTokens\Contracts\AccessTokenRevoker;
use RoundlyConsulting\RefreshTokens\Testing\FakeAccessTokenRevoker;
$revoker = new FakeAccessTokenRevoker();
$this->app->instance(AccessTokenRevoker::class, $revoker);
// … exercise reuse detection / session revocation …
$revoker->assertRevoked($jti);
$revoker->assertNotRevoked($otherJti);
$revoker->assertRevokedCount(2);
$revoker->assertNothingRevoked(); // when nothing should have been deniedJeho asercie vyhadzujú RevokerAssertionFailedException namiesto PHPUnit asercie, takže funguje v akomkoľvek test runneri.
Kompletný test detekcie opätovného použitia
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\RefreshTokens\Contracts\AccessTokenRevoker;
use RoundlyConsulting\RefreshTokens\DataTransferObjects\IssueContext;
use RoundlyConsulting\RefreshTokens\DataTransferObjects\RotationContext;
use RoundlyConsulting\RefreshTokens\Events\RefreshTokenReuseDetected;
use RoundlyConsulting\RefreshTokens\Facades\RefreshTokens;
use RoundlyConsulting\RefreshTokens\Testing\FakeAccessTokenRevoker;
it('revokes the whole family when a rotated token is reused', function () {
Event::fake([RefreshTokenReuseDetected::class]);
$revoker = new FakeAccessTokenRevoker();
$this->app->instance(AccessTokenRevoker::class, $revoker);
$user = User::factory()->create();
$a = RefreshTokens::issue($user, new IssueContext(accessReference: 'acc-a'));
RefreshTokens::rotate($a->plainText, new RotationContext(accessReference: 'acc-b'));
expect(RefreshTokens::redeem($a->plainText))->toBeNull(); // replay = theft signal
$revoker->assertRevoked('acc-b');
$revoker->assertRevokedCount(1);
Event::assertDispatchedTimes(RefreshTokenReuseDetected::class, 1);
});Factory
Model RefreshToken má factory so stavmi expired(), revoked($reason), forFamily($id) a forOwner($owner):
use RoundlyConsulting\RefreshTokens\Enums\RevocationReason;
use RoundlyConsulting\RefreshTokens\Models\RefreshToken;
RefreshToken::factory()->forOwner($user)->create(); // a live session row
RefreshToken::factory()->forOwner($user)->expired()->create();
RefreshToken::factory()->forOwner($user)->revoked(RevocationReason::Logout)->create();
RefreshToken::factory()->forOwner($user)->forFamily($familyId)->create();Riadky z factory majú náhodný odtlačok, nikdy skutočný plaintext, takže sa nedajú uplatniť — použite ich na testy výpisu, odvolania a čistenia a issue(), keď potrebujete token, ktorý sa dá uplatniť.
Vlastné testy balíka
composer test
composer test-coveragePrejavte 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.