feat(rust): coin balances on the Rust account screen (PLAN_REDESIGNS §11.2)
All checks were successful
PR Checks / android-build (pull_request) Successful in 11m28s
All checks were successful
PR Checks / android-build (pull_request) Successful in 11m28s
The app half of the module's Economics (protocol 14): a Coins section on the Rust account screen, from GET /player/rust/economics — one card per server, each linked account's balance, "last known" when the server did not answer, and "Economics is not installed on this server" where it is absent (D199). A third independent read beside the links and the entitlements; a link re-reads it, and a 404 (a module older than protocol 14) leaves the section out rather than showing an error the player can do nothing about. A balance never reported shows a dash, not zero. testDebugUnitTest, lintDebug and assembleDebug pass locally; four new tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.runicgateway.app.data.api
|
package com.runicgateway.app.data.api
|
||||||
|
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkListDto
|
import com.runicgateway.app.data.api.dto.RustLinkListDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkRequest
|
import com.runicgateway.app.data.api.dto.RustLinkRequest
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
||||||
@@ -72,4 +73,13 @@ interface PlayerRustApi {
|
|||||||
*/
|
*/
|
||||||
@GET("api/v1/player/rust/permissions")
|
@GET("api/v1/player/rust/permissions")
|
||||||
suspend fun permissions(): RustPlayerPermissionsDto
|
suspend fun permissions(): RustPlayerPermissionsDto
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The caller's coin balances on every server, per linked account
|
||||||
|
* (`docs/modules/rust/PLAN_REDESIGNS.md` §11.2.2). Live where a server
|
||||||
|
* answers, the last balance it reported where it does not. A module older
|
||||||
|
* than protocol 14 answers 404, and the screen then shows no coins at all.
|
||||||
|
*/
|
||||||
|
@GET("api/v1/player/rust/economics")
|
||||||
|
suspend fun economics(): RustEconomicsDto
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,3 +125,43 @@ data class RustReachDto(
|
|||||||
val name: String = "",
|
val name: String = "",
|
||||||
val live: Boolean = false,
|
val live: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `GET /player/rust/economics` — the caller's coin balances
|
||||||
|
* (`docs/modules/rust/PLAN_REDESIGNS.md` §11.2.2): every server, each linked
|
||||||
|
* Steam account. Economics is optional on a server, so one without it is listed
|
||||||
|
* with [RustEconomicsServerDto.economics] false rather than left out.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class RustEconomicsDto(
|
||||||
|
val servers: List<RustEconomicsServerDto> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One server's balances. [startingKnown] false means the server's Economics
|
||||||
|
* config could not be read, and [startingBalance] is Economics' default.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class RustEconomicsServerDto(
|
||||||
|
val serverId: String = "",
|
||||||
|
val server: String = "",
|
||||||
|
val economics: Boolean = false,
|
||||||
|
val reason: String? = null,
|
||||||
|
val startingBalance: Double? = null,
|
||||||
|
val startingKnown: Boolean = true,
|
||||||
|
val live: Boolean = false,
|
||||||
|
val accounts: List<RustBalanceDto> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One linked account's balance on one server. [source] is `live` (the server
|
||||||
|
* answered just now), `last-known` (it did not, and this is the last balance it
|
||||||
|
* reported), or null with [balance] null when neither knows.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class RustBalanceDto(
|
||||||
|
val steamId: String = "",
|
||||||
|
val name: String? = null,
|
||||||
|
val balance: Double? = null,
|
||||||
|
val source: String? = null,
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.runicgateway.app.core.result.ApiResult
|
|||||||
import com.runicgateway.app.core.result.map
|
import com.runicgateway.app.core.result.map
|
||||||
import com.runicgateway.app.core.result.safeApiCall
|
import com.runicgateway.app.core.result.safeApiCall
|
||||||
import com.runicgateway.app.data.api.PlayerRustApi
|
import com.runicgateway.app.data.api.PlayerRustApi
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsServerDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkDto
|
import com.runicgateway.app.data.api.dto.RustLinkDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkRequest
|
import com.runicgateway.app.data.api.dto.RustLinkRequest
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
||||||
@@ -48,4 +49,8 @@ class PlayerRustRepository @Inject constructor(
|
|||||||
/** Ranks and grants the site holds for the caller, resolved per server. */
|
/** Ranks and grants the site holds for the caller, resolved per server. */
|
||||||
suspend fun permissions(): ApiResult<RustPlayerPermissionsDto> =
|
suspend fun permissions(): ApiResult<RustPlayerPermissionsDto> =
|
||||||
safeApiCall { api.permissions() }
|
safeApiCall { api.permissions() }
|
||||||
|
|
||||||
|
/** The caller's coin balances per server and linked account (§11.2.2). */
|
||||||
|
suspend fun economics(): ApiResult<List<RustEconomicsServerDto>> =
|
||||||
|
safeApiCall { api.economics() }.map { it.servers }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.runicgateway.app.R
|
import com.runicgateway.app.R
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsServerDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkDto
|
import com.runicgateway.app.data.api.dto.RustLinkDto
|
||||||
import com.runicgateway.app.data.api.dto.RustPlayerPermissionsDto
|
import com.runicgateway.app.data.api.dto.RustPlayerPermissionsDto
|
||||||
import com.runicgateway.app.data.api.dto.RustReachDto
|
import com.runicgateway.app.data.api.dto.RustReachDto
|
||||||
@@ -106,6 +107,73 @@ fun RustAccountScreen(
|
|||||||
is UiState.Error -> ErrorView(held.kind, onRetry = viewModel::load)
|
is UiState.Error -> ErrorView(held.kind, onRetry = viewModel::load)
|
||||||
is UiState.Success -> Held(held.data)
|
is UiState.Success -> Held(held.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coins (PLAN_REDESIGNS §11.2.2). A 404 is a module older than protocol
|
||||||
|
// 14, which has no balances to give: the section is left out rather than
|
||||||
|
// shown as an error the player can do nothing about.
|
||||||
|
val coins = state.coins
|
||||||
|
if (!(coins is UiState.Error && coins.httpStatus == 404)) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.rust_coins_title),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
modifier = Modifier.padding(top = 8.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
when (coins) {
|
||||||
|
is UiState.Loading -> LoadingView()
|
||||||
|
is UiState.Error -> ErrorView(coins.kind, onRetry = viewModel::load)
|
||||||
|
is UiState.Success -> Coins(coins.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One card per server: each linked account's balance where the server runs
|
||||||
|
* Economics, and the reason where it does not (D199) — a player asking where
|
||||||
|
* their coins are is owed the answer, not an empty space.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun Coins(servers: List<RustEconomicsServerDto>) {
|
||||||
|
if (servers.none { it.accounts.isNotEmpty() }) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.rust_coins_unlinked),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
servers.forEach { server ->
|
||||||
|
ShardCard(Modifier.fillMaxWidth()) {
|
||||||
|
Column(Modifier.padding(16.dp)) {
|
||||||
|
Text(server.server.ifBlank { server.serverId }, style = MaterialTheme.typography.titleSmall)
|
||||||
|
|
||||||
|
if (!server.economics) {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.rust_coins_missing),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
server.accounts.forEach { account ->
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.rust_coins_balance, playerLabel(account.name, account.steamId), coinAmount(account.balance)),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
|
)
|
||||||
|
if (account.source == "last-known") {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.rust_coins_last_known),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.runicgateway.app.R
|
import com.runicgateway.app.R
|
||||||
import com.runicgateway.app.core.result.ApiResult
|
import com.runicgateway.app.core.result.ApiResult
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsServerDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkDto
|
import com.runicgateway.app.data.api.dto.RustLinkDto
|
||||||
import com.runicgateway.app.data.api.dto.RustPlayerPermissionsDto
|
import com.runicgateway.app.data.api.dto.RustPlayerPermissionsDto
|
||||||
import com.runicgateway.app.data.repository.PlayerRustRepository
|
import com.runicgateway.app.data.repository.PlayerRustRepository
|
||||||
@@ -50,6 +51,8 @@ class RustAccountViewModel @Inject constructor(
|
|||||||
data class State(
|
data class State(
|
||||||
val links: UiState<List<RustLinkDto>> = UiState.Loading,
|
val links: UiState<List<RustLinkDto>> = UiState.Loading,
|
||||||
val held: UiState<RustPlayerPermissionsDto> = UiState.Loading,
|
val held: UiState<RustPlayerPermissionsDto> = UiState.Loading,
|
||||||
|
/** Coin balances (PLAN_REDESIGNS §11.2.2), a third independent read. */
|
||||||
|
val coins: UiState<List<RustEconomicsServerDto>> = UiState.Loading,
|
||||||
val busy: Boolean = false,
|
val busy: Boolean = false,
|
||||||
val feedback: Feedback? = null,
|
val feedback: Feedback? = null,
|
||||||
/** The Steam id currently being released, so only its own row shows it. */
|
/** The Steam id currently being released, so only its own row shows it. */
|
||||||
@@ -64,7 +67,7 @@ class RustAccountViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun load() {
|
fun load() {
|
||||||
_state.update { it.copy(links = UiState.Loading, held = UiState.Loading) }
|
_state.update { it.copy(links = UiState.Loading, held = UiState.Loading, coins = UiState.Loading) }
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_state.update { it.copy(links = repository.links().toUiState()) }
|
_state.update { it.copy(links = repository.links().toUiState()) }
|
||||||
@@ -73,6 +76,15 @@ class RustAccountViewModel @Inject constructor(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_state.update { it.copy(held = repository.permissions().toUiState()) }
|
_state.update { it.copy(held = repository.permissions().toUiState()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reloadCoins()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The balances follow the links: a new link is a new account to read. */
|
||||||
|
private fun reloadCoins() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_state.update { it.copy(coins = repository.economics().toUiState()) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Re-read what the site holds without blanking the accounts above it. */
|
/** Re-read what the site holds without blanking the accounts above it. */
|
||||||
@@ -146,6 +158,7 @@ class RustAccountViewModel @Inject constructor(
|
|||||||
_state.update { it.copy(links = repository.links().toUiState()) }
|
_state.update { it.copy(links = repository.links().toUiState()) }
|
||||||
}
|
}
|
||||||
reloadHeld()
|
reloadHeld()
|
||||||
|
reloadCoins()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun linkErrorRes(status: Int): Int = when (status) {
|
private fun linkErrorRes(status: Int): Int = when (status) {
|
||||||
|
|||||||
@@ -159,6 +159,17 @@ fun prefabName(name: String?): String {
|
|||||||
return name.replace(Regex("[_.]+"), " ").trim()
|
return name.replace(Regex("[_.]+"), " ").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Economics balance (`docs/modules/rust/PLAN_REDESIGNS.md` §11.2.2): grouped,
|
||||||
|
* at most two places, and a dash for one the server never reported — not "0",
|
||||||
|
* which is a claim about somebody's balance that nobody made.
|
||||||
|
*/
|
||||||
|
fun coinAmount(value: Double?, locale: java.util.Locale = java.util.Locale.getDefault()): String {
|
||||||
|
val n = value ?: return "—"
|
||||||
|
if (!n.isFinite()) return "—"
|
||||||
|
return java.text.NumberFormat.getNumberInstance(locale).apply { maximumFractionDigits = 2 }.format(n)
|
||||||
|
}
|
||||||
|
|
||||||
/** A steam id, shortened for a table cell, without pretending it is a name. */
|
/** A steam id, shortened for a table cell, without pretending it is a name. */
|
||||||
fun shortSteamId(steamId: String?): String {
|
fun shortSteamId(steamId: String?): String {
|
||||||
val id = steamId.orEmpty()
|
val id = steamId.orEmpty()
|
||||||
|
|||||||
@@ -697,6 +697,11 @@
|
|||||||
<string name="rust_held_unlinked">None of this reaches the game yet — link a Steam account above and the site pushes it across on its next sync.</string>
|
<string name="rust_held_unlinked">None of this reaches the game yet — link a Steam account above and the site pushes it across on its next sync.</string>
|
||||||
<string name="rust_held_waiting_note">A server marked waiting has not confirmed it yet. One that is offline catches up when it comes back.</string>
|
<string name="rust_held_waiting_note">A server marked waiting has not confirmed it yet. One that is offline catches up when it comes back.</string>
|
||||||
<string name="rust_held_no_servers">No servers are configured yet.</string>
|
<string name="rust_held_no_servers">No servers are configured yet.</string>
|
||||||
|
<string name="rust_coins_title">Coins</string>
|
||||||
|
<string name="rust_coins_unlinked">Link a Steam account to see your balance.</string>
|
||||||
|
<string name="rust_coins_missing">Economics is not installed on this server.</string>
|
||||||
|
<string name="rust_coins_balance">%1$s · %2$s coins</string>
|
||||||
|
<string name="rust_coins_last_known">Last known — the server did not answer just now.</string>
|
||||||
<string name="rust_reach_live">%1$s · has it</string>
|
<string name="rust_reach_live">%1$s · has it</string>
|
||||||
<string name="rust_reach_waiting">%1$s · waiting</string>
|
<string name="rust_reach_waiting">%1$s · waiting</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package com.runicgateway.app.data.api.fake
|
package com.runicgateway.app.data.api.fake
|
||||||
|
|
||||||
import com.runicgateway.app.data.api.PlayerRustApi
|
import com.runicgateway.app.data.api.PlayerRustApi
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkListDto
|
import com.runicgateway.app.data.api.dto.RustLinkListDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkRequest
|
import com.runicgateway.app.data.api.dto.RustLinkRequest
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
||||||
@@ -67,4 +68,16 @@ class FakePlayerRustApi : PlayerRustApi {
|
|||||||
error?.let { throw it }
|
error?.let { throw it }
|
||||||
return permissions
|
return permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The coin balances (§11.2.2), and a throw of their own: a 404 is an older module. */
|
||||||
|
var economics: RustEconomicsDto = RustEconomicsDto()
|
||||||
|
var economicsError: Throwable? = null
|
||||||
|
var economicsCalls: Int = 0
|
||||||
|
|
||||||
|
override suspend fun economics(): RustEconomicsDto {
|
||||||
|
economicsCalls++
|
||||||
|
economicsError?.let { throw it }
|
||||||
|
error?.let { throw it }
|
||||||
|
return economics
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
package com.runicgateway.app.ui.rust
|
package com.runicgateway.app.ui.rust
|
||||||
|
|
||||||
import com.runicgateway.app.R
|
import com.runicgateway.app.R
|
||||||
|
import com.runicgateway.app.data.api.dto.RustBalanceDto
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsDto
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsServerDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkDto
|
import com.runicgateway.app.data.api.dto.RustLinkDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkListDto
|
import com.runicgateway.app.data.api.dto.RustLinkListDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
import com.runicgateway.app.data.api.dto.RustLinkResultDto
|
||||||
@@ -202,4 +205,38 @@ class RustAccountViewModelTest {
|
|||||||
assertTrue(held.grants.first().reach.first { it.id == "main" }.live)
|
assertTrue(held.grants.first().reach.first { it.id == "main" }.live)
|
||||||
assertFalse(held.grants.first().reach.first { it.id == "creative" }.live)
|
assertFalse(held.grants.first().reach.first { it.id == "creative" }.live)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `coins are a third read, and a link re-reads them`() {
|
||||||
|
api.economics = RustEconomicsDto(
|
||||||
|
servers = listOf(
|
||||||
|
RustEconomicsServerDto(
|
||||||
|
serverId = "main",
|
||||||
|
server = "Main",
|
||||||
|
economics = true,
|
||||||
|
accounts = listOf(RustBalanceDto(steamId = "7656119", balance = 1500.0, source = "live")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val vm = viewModel()
|
||||||
|
val coins = vm.state.value.coins
|
||||||
|
assertTrue(coins is UiState.Success)
|
||||||
|
assertEquals(1500.0, (coins as UiState.Success).data.single().accounts.single().balance)
|
||||||
|
|
||||||
|
val before = api.economicsCalls
|
||||||
|
vm.link("K7M2PQ")
|
||||||
|
assertEquals("a new link is a new account to read", before + 1, api.economicsCalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `an older module's missing route leaves the rest of the screen alone`() {
|
||||||
|
api.economicsError = httpError(404)
|
||||||
|
api.permissions = RustPlayerPermissionsDto(accounts = 1)
|
||||||
|
|
||||||
|
val state = viewModel().state.value
|
||||||
|
|
||||||
|
assertTrue(state.held is UiState.Success)
|
||||||
|
assertEquals(404, (state.coins as UiState.Error).httpStatus)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.runicgateway.app.ui.rust
|
package com.runicgateway.app.ui.rust
|
||||||
|
|
||||||
|
import com.runicgateway.app.data.api.dto.RustEconomicsDto
|
||||||
import com.runicgateway.app.data.api.dto.RustEventDto
|
import com.runicgateway.app.data.api.dto.RustEventDto
|
||||||
import com.runicgateway.app.data.api.dto.RustLeaderboardDto
|
import com.runicgateway.app.data.api.dto.RustLeaderboardDto
|
||||||
import com.runicgateway.app.data.api.dto.RustServerListDto
|
import com.runicgateway.app.data.api.dto.RustServerListDto
|
||||||
@@ -146,4 +147,27 @@ class RustDtoTest {
|
|||||||
)
|
)
|
||||||
assertTrue(older.leaderboard.single().titles.isEmpty())
|
assertTrue(older.leaderboard.single().titles.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `coin balances decode, a server without Economics included`() {
|
||||||
|
// The shape of module-rust's economics.model balancesOf().
|
||||||
|
val coins = json.decodeFromString<RustEconomicsDto>(
|
||||||
|
"""
|
||||||
|
{"servers":[
|
||||||
|
{"serverId":"main","server":"Main","economics":true,"startingBalance":1000,"startingKnown":false,"live":false,
|
||||||
|
"accounts":[{"steamId":"76561198000000001","name":"Wanderer","balance":1500.5,"source":"last-known"},
|
||||||
|
{"steamId":"76561198000000002","name":null,"balance":null,"source":null}]},
|
||||||
|
{"serverId":"pvp","server":"PvP","economics":false,"reason":"Economics is not installed on this server","accounts":[]}
|
||||||
|
]}
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
val main = coins.servers.first()
|
||||||
|
assertTrue(main.economics)
|
||||||
|
assertFalse(main.startingKnown)
|
||||||
|
assertEquals(1500.5, main.accounts.first().balance!!, 0.0)
|
||||||
|
assertEquals("last-known", main.accounts.first().source)
|
||||||
|
assertNull(main.accounts[1].balance)
|
||||||
|
assertFalse(coins.servers[1].economics)
|
||||||
|
assertEquals("Economics is not installed on this server", coins.servers[1].reason)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,4 +169,12 @@ class RustFormatTest {
|
|||||||
assertTrue(titleInkIsDark(titleArgb("#ff0000")!!))
|
assertTrue(titleInkIsDark(titleArgb("#ff0000")!!))
|
||||||
assertFalse(titleInkIsDark(titleArgb("#1a1a8c")!!))
|
assertFalse(titleInkIsDark(titleArgb("#1a1a8c")!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `a balance never reported is a dash, not zero coins`() {
|
||||||
|
assertEquals("—", coinAmount(null, Locale.UK))
|
||||||
|
assertEquals("—", coinAmount(Double.NaN, Locale.UK))
|
||||||
|
assertEquals("0", coinAmount(0.0, Locale.UK))
|
||||||
|
assertEquals("1,500.46", coinAmount(1500.456, Locale.UK))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user