Coverage Summary for Class: ConfigDebugRowKind (com.georgeci.moneysurfer.domain.config)
| Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| ConfigDebugRowKind$Bool |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(2/2)
|
| ConfigDebugRowKind$Choice |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(7/7)
|
| ConfigDebugRowKind$FreeText |
100%
(1/1)
|
|
100%
(1/1)
|
100%
(2/2)
|
| Total |
100%
(3/3)
|
|
100%
(3/3)
|
100%
(11/11)
|
package com.georgeci.moneysurfer.domain.config
import kotlinx.coroutines.flow.Flow
/**
* Read/write view of every configuration key, for the QA panel in debug builds.
*
* Everything is pre-rendered as strings and the write path is keyed by name, so no `ConfigKey<*>`
* — and no `app-config` dependency — reaches `feature/settings`.
*/
interface DebugConfigInspector {
/**
* `false` in release builds, where the Debug layer is empty. The panel and its entry point are
* hidden rather than shown broken.
*/
val isAvailable: Boolean
val rows: Flow<List<ConfigDebugRow>>
/**
* Names of layers whose store could not be read at startup, pre-rendered. Empty on the happy
* path.
*
* Their rows resolve as if the layer were empty, so without this the panel would report a stale
* fallback as the real answer. Recovery is automatic once the store is readable again.
*/
val degradedLayers: List<String>
/** Fails when [raw] does not decode through the key's codec, or when the key is unknown. */
suspend fun override(name: String, raw: String): Result<Unit>
suspend fun clearOverride(name: String)
suspend fun resetAll()
}
/**
* One key as the panel sees it.
*
* @param effectiveValue the resolved value, encoded.
* @param winner name of the layer that won, or `"default"` when no layer holds the key.
* @param kind which control to render — a text field for `PaletteSource`'s `PRESET:<seed>` format
* is a routine way to fail.
* @param overridden `true` when the Debug layer holds anything for this key, i.e. this row can be
* cleared. Deliberately not "the Debug layer won": an override the codec can no longer read does
* not win, and it is exactly the one a tester needs to remove.
* @param hostOwned `true` for a key that describes the build itself (`host.*`). Overriding one of
* these changes what the app believes it is while its DI graph stays as compiled — useful for QA,
* but not the same kind of switch as a feature flag, so the panel says so.
*/
data class ConfigDebugRow(
val name: String,
val effectiveValue: String,
val winner: String,
val kind: ConfigDebugRowKind,
val overridden: Boolean,
val hostOwned: Boolean = false,
val layers: List<ConfigDebugLayerCell>,
)
sealed interface ConfigDebugRowKind {
data object Bool : ConfigDebugRowKind
data class Choice(val choices: List<String>) : ConfigDebugRowKind
data object FreeText : ConfigDebugRowKind
}
/**
* One layer's contribution. [value] is `null` when the layer does not hold the key; [undecodable]
* distinguishes "stored but rejected by the codec" from that, which is the whole reason the panel
* shows per-layer cells at all.
*/
data class ConfigDebugLayerCell(
val layer: String,
val value: String?,
val undecodable: Boolean,
)