Part A — runBlocking → runTest
- Migrated all 5 runBlocking sites in core/database MigrationTest
(@Before + 4 @Test bodies) to kotlinx.coroutines.test.runTest so
test dispatchers are virtual-time aware and cancellation semantics
match the rest of the suite.
- Left DatabaseManagerLegacyCleanupTest on runBlocking: it polls with
delay(100) for async cleanup that runs on Dispatchers.IO. runTest's
virtual-time delay would collapse instantly and race the real IO
cleanup, so runBlocking is the correct choice there.
Part B — missing repository fakes
Added five in-memory fakes in core:testing (pure Kotlin, MutableStateFlow
backed) that implement their repository interface exactly:
- FakeDeviceHardwareRepository (DeviceHardwareRepository)
- FakeFirmwareReleaseRepository (FirmwareReleaseRepository)
- FakeQuickChatActionRepository (QuickChatActionRepository)
- FakeTracerouteSnapshotRepository (TracerouteSnapshotRepository)
- FakeRadioConfigRepository (RadioConfigRepository)
Each fake is exercised by a new test in
core/testing/src/commonTest/kotlin/.../RepositoryFakesTest.kt:
upsert/delete/reorder flows for quick-chat, seeded lookups +
call-recording for device hardware, stable/alpha emission for
firmware releases, snapshot round-trips for traceroute, and
channel-set mutations for radio config. PacketRepository was
skipped — 40+ members and a FakePacketRepository helper already
exists for informal use; left for a focused follow-up.
Validation (all green):
./gradlew spotlessApply spotlessCheck
./gradlew :core:testing:jvmTest :core:database:testAndroid
./gradlew detekt kmpSmokeCompile
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>