Close the test-coverage gap on the app-lock security core (#100): the
branching that decides when to WIPE user data or drop the lock, which
shipped largely untested.
- AppLockViewModelTest: pin the onAuthenticated unlock/arm classification
(OK / UNRECOVERABLE / RETRY) and the onForeground LockAction dispatch --
DISABLE_APP_LOCK persists the setting, CLEAR_* set the pending flag and
drop the gate BEFORE the awaited re-sync enqueue and process restart,
and CLEAR_AND_REQUIRE_AUTH clears + restarts but keeps app-lock on.
- KeyInvalidationPolicyTest: make the exhaustive 16-row decision table a
test, with a completeness guard so no row can be dropped. The common
(appLock on, encrypt off, secure, valid) -> REQUIRE_AUTH row is now
pinned, so a mutation to PROCEED (a silent lock bypass) fails.
- DatabaseKeyStoreTest: new JVM tests for the dual-seal exchange
(sealWithAuth dropping SEALED_MASTER, sealWithMaster, resetSealedPassphrase,
unlockWithAuth, clear-pending) pinning the "never both seals at once" and
"not recoverable without auth" invariants.
- SettingsViewModelTest: setAppLock reject / reseal / disable branches.
To make the device-only DatabaseKeyStore crypto JVM-testable, add a
minimal @VisibleForTesting DataStore seam (mirroring AppLockViewModel's
injectable dispatcher); production still uses the real per-app DataStore.
No crypto plumbing is refactored.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>