Adds DatabaseModuleInstrumentedTest (app/src/androidTest/kotlin/org/libremail/di/), an instrumented test that pins the "native lib loaded before keyed open" invariant (592a797) at the DatabaseModule.provideDatabase factory site, not just inside DatabaseProvisioner.
DatabaseProvisionerTest (mocked) and DatabaseProvisionerInstrumentedTest (real SQLCipher) both already pin that prepareCache() calls ensureNativeLibraryLoaded() for the encrypted branch, but neither exercises DatabaseModule.provideDatabase itself — the instrumented one opens through a hand-rolled SupportOpenHelperFactory, bypassing the branch that actually maps the reported CacheOpenMode to a real factory (SupportOpenHelperFactory vs FrameworkSQLiteOpenHelperFactory). A regression that broke that wiring (e.g. swapped branches, or stopped gating the open on prepareCache()) would slip through both existing guards.
Three new tests call DatabaseModule.provideDatabase(context, provisioner) directly (no Hilt graph needed) and drive the first real open through its own DeferredOpenHelperFactory lambda:
encryptedBranchLoadsNativeLibraryBeforeTheKeyedOpenSucceeds — steady-state encrypted cache (nothing for ensureEncrypted to convert), real SQLCipher open through the actual factory branch, ensureNativeLibraryLoaded() verified.
plaintextBranchNeverTouchesTheNativeLibrary — counterpart guard for the unencrypted branch.
encryptedOpenNeverSucceedsIfTheNativeLibraryLoadFails — fault injection: stubs the load to throw and asserts the keyed open cannot proceed, pinning that the open is causally gated on the load rather than just usually preceded by it (the "wired without a preceding load" regression the issue calls out).
Targeted run on a manually-booted dev36_google_apis_x86_64_Pixel_2 emulator: :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=org.libremail.di.DatabaseModuleInstrumentedTest — 3/3 passed, 0 failed.
Closes #220
## Summary
- Adds `DatabaseModuleInstrumentedTest` (`app/src/androidTest/kotlin/org/libremail/di/`), an instrumented test that pins the "native lib loaded before keyed open" invariant (592a797) at the `DatabaseModule.provideDatabase` factory site, not just inside `DatabaseProvisioner`.
- `DatabaseProvisionerTest` (mocked) and `DatabaseProvisionerInstrumentedTest` (real SQLCipher) both already pin that `prepareCache()` calls `ensureNativeLibraryLoaded()` for the encrypted branch, but neither exercises `DatabaseModule.provideDatabase` itself — the instrumented one opens through a hand-rolled `SupportOpenHelperFactory`, bypassing the branch that actually maps the reported `CacheOpenMode` to a real factory (`SupportOpenHelperFactory` vs `FrameworkSQLiteOpenHelperFactory`). A regression that broke that wiring (e.g. swapped branches, or stopped gating the open on `prepareCache()`) would slip through both existing guards.
- Three new tests call `DatabaseModule.provideDatabase(context, provisioner)` directly (no Hilt graph needed) and drive the first real open through its own `DeferredOpenHelperFactory` lambda:
- `encryptedBranchLoadsNativeLibraryBeforeTheKeyedOpenSucceeds` — steady-state encrypted cache (nothing for `ensureEncrypted` to convert), real SQLCipher open through the actual factory branch, `ensureNativeLibraryLoaded()` verified.
- `plaintextBranchNeverTouchesTheNativeLibrary` — counterpart guard for the unencrypted branch.
- `encryptedOpenNeverSucceedsIfTheNativeLibraryLoadFails` — fault injection: stubs the load to throw and asserts the keyed open cannot proceed, pinning that the open is causally gated on the load rather than just usually preceded by it (the "wired without a preceding load" regression the issue calls out).
No production code changed.
## Local validation
- `:app:compileDebugAndroidTestKotlin :app:ktlintCheck :app:detekt` — green.
- Targeted run on a manually-booted `dev36_google_apis_x86_64_Pixel_2` emulator: `:app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=org.libremail.di.DatabaseModuleInstrumentedTest` — **3/3 passed, 0 failed**.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #220
Summary
DatabaseModuleInstrumentedTest(app/src/androidTest/kotlin/org/libremail/di/), an instrumented test that pins the "native lib loaded before keyed open" invariant (592a797) at theDatabaseModule.provideDatabasefactory site, not just insideDatabaseProvisioner.DatabaseProvisionerTest(mocked) andDatabaseProvisionerInstrumentedTest(real SQLCipher) both already pin thatprepareCache()callsensureNativeLibraryLoaded()for the encrypted branch, but neither exercisesDatabaseModule.provideDatabaseitself — the instrumented one opens through a hand-rolledSupportOpenHelperFactory, bypassing the branch that actually maps the reportedCacheOpenModeto a real factory (SupportOpenHelperFactoryvsFrameworkSQLiteOpenHelperFactory). A regression that broke that wiring (e.g. swapped branches, or stopped gating the open onprepareCache()) would slip through both existing guards.DatabaseModule.provideDatabase(context, provisioner)directly (no Hilt graph needed) and drive the first real open through its ownDeferredOpenHelperFactorylambda:encryptedBranchLoadsNativeLibraryBeforeTheKeyedOpenSucceeds— steady-state encrypted cache (nothing forensureEncryptedto convert), real SQLCipher open through the actual factory branch,ensureNativeLibraryLoaded()verified.plaintextBranchNeverTouchesTheNativeLibrary— counterpart guard for the unencrypted branch.encryptedOpenNeverSucceedsIfTheNativeLibraryLoadFails— fault injection: stubs the load to throw and asserts the keyed open cannot proceed, pinning that the open is causally gated on the load rather than just usually preceded by it (the "wired without a preceding load" regression the issue calls out).No production code changed.
Local validation
:app:compileDebugAndroidTestKotlin :app:ktlintCheck :app:detekt— green.dev36_google_apis_x86_64_Pixel_2emulator::app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=org.libremail.di.DatabaseModuleInstrumentedTest— 3/3 passed, 0 failed.🤖 Generated with Claude Code