Coverage Summary for Class: OfflineWiringKt (com.georgeci.moneysurfer.offline.di)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| OfflineWiringKt |
100%
(1/1)
|
90.9%
(20/22)
|
|
88.9%
(32/36)
|
95.4%
(206/216)
|
package com.georgeci.moneysurfer.offline.di
import com.georgeci.moneysurfer.appconfig.BuildConfigSource
import com.georgeci.moneysurfer.appconfig.HostConfigKeys
import com.georgeci.moneysurfer.appconfig.RemoteGlobalConfigSource
import com.georgeci.moneysurfer.domain.firstrun.FirstRunSeeder
import com.georgeci.moneysurfer.domain.repositories.AppConfigRepository
import com.georgeci.moneysurfer.domain.repositories.AppVersionGate
import com.georgeci.moneysurfer.domain.repositories.AuthRemoteRepository
import com.georgeci.moneysurfer.domain.repositories.ExchangeRateRemoteSource
import com.georgeci.moneysurfer.domain.repositories.RemoteDataResetRepository
import com.georgeci.moneysurfer.domain.repositories.SessionShutdownGate
import com.georgeci.moneysurfer.domain.repositories.UserAccountDeletionRepository
import com.georgeci.moneysurfer.domain.repositories.UserRemoteRepository
import com.georgeci.moneysurfer.domain.repositories.WorkspaceSyncer
import com.georgeci.moneysurfer.domain.telemetry.CrashReporter
import com.georgeci.moneysurfer.domain.usecase.DemoLoginUseCase
import com.georgeci.moneysurfer.domain.usecase.SeedDefaultsUseCase
import com.georgeci.moneysurfer.offline.firstrun.OfflineFirstRunSeeder
import com.georgeci.moneysurfer.offline.noop.NoOpAppConfigRepository
import com.georgeci.moneysurfer.offline.noop.NoOpAppVersionGate
import com.georgeci.moneysurfer.offline.noop.NoOpAuthRemoteRepository
import com.georgeci.moneysurfer.offline.noop.NoOpCrashReporter
import com.georgeci.moneysurfer.offline.noop.NoOpExchangeRateRemoteSource
import com.georgeci.moneysurfer.offline.noop.NoOpOutboxEnqueuer
import com.georgeci.moneysurfer.offline.noop.NoOpPendingMutationQueue
import com.georgeci.moneysurfer.offline.noop.NoOpRemoteDataResetRepository
import com.georgeci.moneysurfer.offline.noop.NoOpSessionShutdownGate
import com.georgeci.moneysurfer.offline.noop.NoOpSyncMetaRepository
import com.georgeci.moneysurfer.offline.noop.NoOpUserAccountDeletionRepository
import com.georgeci.moneysurfer.offline.noop.NoOpUserRemoteRepository
import com.georgeci.moneysurfer.offline.noop.NoOpWorkspaceSyncer
import com.georgeci.moneysurfer.sync.repository.OutboxEnqueuer
import com.georgeci.moneysurfer.sync.repository.PendingMutationQueue
import com.georgeci.moneysurfer.sync.repository.SyncMetaRepository
import org.koin.core.module.Module
import org.koin.dsl.module
private val offlineNoOpModule: Module = module {
single<OutboxEnqueuer> { NoOpOutboxEnqueuer() }
single<CrashReporter> { NoOpCrashReporter() }
single<AuthRemoteRepository> { NoOpAuthRemoteRepository() }
single<UserRemoteRepository> { NoOpUserRemoteRepository() }
single<AppConfigRepository> { NoOpAppConfigRepository() }
single<AppVersionGate> { NoOpAppVersionGate() }
single<WorkspaceSyncer> { NoOpWorkspaceSyncer() }
single<RemoteDataResetRepository> { NoOpRemoteDataResetRepository() }
single<UserAccountDeletionRepository> { NoOpUserAccountDeletionRepository() }
single<SessionShutdownGate> { NoOpSessionShutdownGate() }
single<PendingMutationQueue> { NoOpPendingMutationQueue() }
single<SyncMetaRepository> { NoOpSyncMetaRepository() }
single<ExchangeRateRemoteSource> { NoOpExchangeRateRemoteSource() }
}
/**
* Offline-only first-run seed: pre-creates demo user + default workspace + Cash account on a
* clean install so the app lands on a populated Dashboard. Online builds bind their own no-op
* (Firestore-backed onboarding handles empty workspaces).
*/
private val offlineFirstRunModule: Module = module {
single<FirstRunSeeder> {
OfflineFirstRunSeeder(
session = get(),
demoLoginUseCase = get<DemoLoginUseCase>(),
seedDefaultsUseCase = get<SeedDefaultsUseCase>(),
)
}
}
/**
* The offline host's Build layer, replacing the four flag data classes it used to override
* individually.
*
* Sign-in is demo-only (no remote auth backend), the Transfer segment is out of the offline MVP
* scope, and sync has no Firestore-backed implementation here at all. Keeping these host-owned is
* what stops the offline build regressing to the online surface through Koin module load order.
*
* The RemoteGlobal layer binds `Empty`: the offline build has no Firestore, and `Empty` lives in
* `app-config/api` precisely so binding it needs no dependency on a remote implementation.
*/
private val offlineConfigModule: Module = module {
single<BuildConfigSource> {
BuildConfigSource {
put(HostConfigKeys.isOffline, true)
put(HostConfigKeys.signInEmailPassword, false)
put(HostConfigKeys.signInAnonymous, false)
put(HostConfigKeys.signInDemo, true)
put(HostConfigKeys.transferEnabled, false)
// Same dark flag as the online host — the picker is unfinished, not host-specific.
put(HostConfigKeys.dashboardWidgetStyle, false)
put(HostConfigKeys.syncEnabled, false)
}
}
single<RemoteGlobalConfigSource> { RemoteGlobalConfigSource.Empty }
}
/**
* Modules layered on top of shared's `AppModule` graph for the offline build.
* No data-remote, no sync-surfer, no sync/default — every remote-side
* dependency is satisfied by an explicit no-op binding.
*/
val offlineWiring: List<Module> = listOf(
offlineNoOpModule,
offlineConfigModule,
offlineFirstRunModule,
OfflineKoinApp().module(),
)