16-3: Add race-adjacent tests
Status: 2026-03-18
Contents key
This page
Related pages
Public steps
- 16-1: Locking für Rate-Limit und CAPTCHA ausrollen
- 16-2: Roll out locking for token rotation
- 16-4: Update operational note
Step-specific public work record for 16-3 under J01-16.
Goal
Add race-adjacent tests for the write paths locked in 16-1 and 16-2:
RateLimiter, CaptchaService and TokenService.
Test strategy
PHP is single-threaded; true parallel access requires process forks.
Instead, each service’s lock key is manually held by a direct FlockStore
instance before the service call starts.
This verifies the following properties:
- The write path actually runs under the expected lock key (no bypass).
RuntimeLockRunneraborts with a lock timeout when the key is already held.- After the lock is released, the written state is consistently readable.
- Read operations in
TokenServicedo not require a lock (no timeout).
Review plan
| Check | Expected | Evidence / location | Status |
|---|---|---|---|
RateLimiter.allow() runs under lock |
Timeout when ratelimit_{key} is already held |
ConcurrencyTest::testRateLimiterAllowTimesOutWhenLockBusy |
Done |
Sequential allow() calls are serialised |
Counter incremented correctly, limit enforced | ConcurrencyTest::testRateLimiterSerializesCallsForSameKey |
Done |
CaptchaService.verify() runs under lock |
Timeout when captcha_{id} is already held |
ConcurrencyTest::testCaptchaVerifyTimesOutWhenLockBusy |
Done |
used_at written atomically under lock |
Challenge inactive after verify, no torn state | ConcurrencyTest::testCaptchaVerifyUsedAtWrittenUnderLock |
Done |
TokenService.rotate() runs under lock |
Timeout when token_{profile} is already held |
ConcurrencyTest::testTokenRotateTimesOutWhenLockBusy |
Done |
rotate() writes atomically |
Hashes consistently readable after rotation | ConcurrencyTest::testTokenRotateWritesAtomically |
Done |
| Read operations need no lock | verify() and findProfileForToken() complete even when token lock is held |
ConcurrencyTest::testTokenReadOperationsNeedNoLock |
Done |
| Full test suite green | 33/33 PHPUnit tests after change | php vendor/bin/phpunit |
Done |