The 2FA flow moved from a middleware-gated i/auth/checkpoint model to a
pending-login model (auth.pending session, POST /login/2fa, /login?step=2fa
challenge). The old tests referenced the removed route and dead session keys
(2fa.session.active, 2fa.attempts) and failed with 404s.
Rewritten against the new code as source of truth:
- Checkpoint test: throttle assertion retargeted to the login/2fa route;
failed-verification audit log now driven through a pending 2FA session.
- Logout-session test: asserts auth.pending is cleared and the user stays a
guest after MAX_2FA_ATTEMPTS failures (replacing the old flag cleanup).
- TwoFactorTest: challenge-redirect and challenge-page cases rewritten around
the login flow; setup/recovery password-confirmation cases unchanged.
- MiddlewarePipelineTest: 2FA is enforced at login, not per-request, so an
authenticated 2FA user browses normally.
Full suite: 715 passed.
- Suspended/missing user guards for get, increase, decrement, recalculate
- Staleness window boundary (fresh at N-1h, stale at N+1h)
- Sub-1000-byte rounding on increase/decrement (floor to KB)
- Command --stale filter (only recomputes stale/never-calculated users) and
missing --user id failure
- Migration dispatches the backfill job to the low queue (Bus::fake)
Repair accounts whose storage counter drifted before the self-heal logic
existed (#7169). A data migration dispatches RecalculateAllUserStoragePipeline
to the low queue so the deploy is not blocked while every user is recomputed
from source. The job is unique and idempotent, so re-runs are harmless.
- RecalculateAllUserStoragePipeline: chunked recalc of all active users
- Migration dispatches the job (no inline heavy work during deploy)
- Test covers bulk recalculation from actual media
UserStorageService::get() now recalculates from source when the cached
counter is missing or older than STALE_AFTER_HOURS, instead of returning a
possibly-inflated cached value. This is what unblocks a user stuck at the
account size limit: the limit check on their next upload attempt reads the
freshly recalculated real usage rather than the drifted value (#7169).
The upload flow reads get() and enforces the limit BEFORE the write-path
heal runs, so a blocked user could never self-heal via upload/delete alone.
Healing on read closes that gap and makes the scheduled reconciler a
belt-and-suspenders safety net rather than a requirement.
A fresh counter is still trusted as-is (no per-read SUM). Adds tests for the
stale-get recompute and fresh-get trust paths.
Make increaseStorageUsed/decrementStorageUsed recalculate from source when
the cached counter is older than STALE_AFTER_HOURS (168h) or never
calculated, so an affected user is corrected the next time they upload or
delete without waiting for the nightly reconciler. Callers save/delete the
media row before calling these, so the from-source recalc already reflects
the change and the incremental delta is skipped on the recalc path.
- Add UserStorageService::STALE_AFTER_HOURS and isStale() helper (no extra
query: reads the already-loaded model), with defensive Carbon parsing
- Cast users.storage_used_updated_at to datetime so freshness comparisons
work on a Carbon instance
- Add tests for stale/fresh/never-calculated increase and decrement paths
users.storage_used only ever grew: uploads incremented it but no deletion
path decremented it, so users hit the account size limit even when their
real media usage was well below it.
- Decrement storage_used in MediaDeletePipeline when media is removed
- Add UserStorageService::increaseStorageUsed / decrementStorageUsed as the
fast, symmetric hot-path counter updates (floor-based, clamped at zero)
- Refactor the 6 upload call sites to use increaseStorageUsed instead of
duplicated inline writes (also fixes ceil/floor drift vs the reconciler)
- Add (user_id, size) covering index so per-user SUM(size) is not a full
table scan (INPLACE/LOCK=NONE, skipped on sqlite)
- Add user:storage:recalculate command to repair affected accounts, with a
daily --stale=168 scheduled reconciler to correct any drift
- Add regression tests for the pipeline and UserStorageService