class

KemalIdentity::Testing::MemoryAccountRepository

Inherits KemalIdentity::Accounts::Repository < Reference < Object

In-memory Accounts::Repository.

Passes the same contract spec as the PostgreSQL adapter, which is the only thing that makes it trustworthy. Unreachable from a production build not because of where it lives — it is published as kemal_identity/testing — but because nothing in kemal_identity requires that tree. Measured: a consumer binary has zero KemalIdentity::Testing symbols.

Guarded by a Mutex because the contract requires adapters to be safe for concurrent use from multiple fibers on multiple threads, and since Crystal 1.21 that may genuinely mean multiple threads.

Constructors

Instance methods

bump_auth_version(id : String) : Int32 | Nil

Increments auth_version and returns the new value, or nil if no such account exists.

Invalidates every session for the account without enumerating rows: each session stores the auth_version it was minted under, and a mismatch fails the session on its next read. Used on password change and MFA recovery, alongside explicit revocation rather than instead of it (docs/02-security-model.md).

Also the answer for a change to the account's tenant, which is the one authorization input a session copies: without a bump or an explicit revocation, sessions that already exist keep the tenant they were minted with until they expire (Sessions::Record#tenant_id).

Source
disable(id : String, at : Time) : Bool

Test-setup only. Disabling an account is an application action, not something the authentication path does.

Source
find_by_id(id : String) : KemalIdentity::Accounts::Account | Nil

The account with this id, or nil.

Source
find_by_login(normalized_login : String, tenant_id : String | Nil = nil) : KemalIdentity::Accounts::Account | Nil

The account whose stored normalized_login equals normalized_login, within tenant_id.

The caller normalises. KemalIdentity::Accounts::Login.normalize is applied on the way in; this method compares by equality against the stored column so the index is used and the uniqueness constraint agrees with the lookup (docs/02-security-model.md).

A nil tenant_id means the single-tenant case and matches only rows whose tenant_id is also null — not "any tenant". In PostgreSQL that needs an explicit IS NULL, since = NULL matches nothing.

Source
insert(account : KemalIdentity::Accounts::Account) : Nil

Test-setup only, and deliberately not part of the repository contract: an adapter over an application's existing users table has no business inserting rows into it.

Source
mark_email_verified(id : String, at : Time) : Bool

Records that this account's address has been proved, and returns false if no such account exists.

Idempotent: confirming twice is not an error, and the second call moves the timestamp forward rather than refusing. A user who clicks a link twice has not done anything wrong.

Source
size
Source
update_password_digest(id : String, digest : String, scheme : String, at : Time) : Bool

Replaces the stored digest and scheme, and moves updated_at to at.

This is the lazy-rehash write: a successful login against a digest at an outdated cost rehashes at the current one, so old digests disappear as people sign in and nobody is forced through a password reset (docs/06-roadmap.md).

Returns false if no such account exists. Does not revoke sessions — a rehash of the same password is not a credential change, and revoking here would log everyone out of the application that just upgraded its cost.

Source