From 9d0c3be028032c37632ca3869a902082ada17667 Mon Sep 17 00:00:00 2001 From: Dmitriy Zhavoronkov Date: Mon, 22 Jun 2026 17:41:53 +0200 Subject: [PATCH] fix: change Account fields to var for XStream serialization XStream can only deserialize var fields. val fields (private final in Java bytecode) cannot be set during deserialization, causing all accounts to revert to default values (CLINE_API) on restart. Changed all Account data class fields from val to var: - id, name, connectionType, authType, keyPreview Added tests to verify fields are mutable and preserved after copy. --- .../tokenpulse/settings/Account.kt | 10 ++-- .../tokenpulse/settings/AccountTest.kt | 60 +++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/org/zhavoronkov/tokenpulse/settings/Account.kt b/src/main/kotlin/org/zhavoronkov/tokenpulse/settings/Account.kt index 65a66b4..8a57235 100644 --- a/src/main/kotlin/org/zhavoronkov/tokenpulse/settings/Account.kt +++ b/src/main/kotlin/org/zhavoronkov/tokenpulse/settings/Account.kt @@ -73,13 +73,13 @@ enum class AuthType(val displayName: String) { * @property keyPreview Masked preview of the API key for display purposes. */ data class Account( - val id: String = UUID.randomUUID().toString(), - val name: String = "", - val connectionType: ConnectionType = ConnectionType.CLINE_API, - val authType: AuthType = AuthType.CLINE_API_KEY, + var id: String = UUID.randomUUID().toString(), + var name: String = "", + var connectionType: ConnectionType = ConnectionType.CLINE_API, + var authType: AuthType = AuthType.CLINE_API_KEY, var isEnabled: Boolean = true, /** Masked preview of the API key, e.g. "sk-or-…91bc". Stored for display only, not sensitive. */ - val keyPreview: String = "" + var keyPreview: String = "" ) { /** Human-readable label shown in the accounts table. */ fun displayLabel(): String { diff --git a/src/test/kotlin/org/zhavoronkov/tokenpulse/settings/AccountTest.kt b/src/test/kotlin/org/zhavoronkov/tokenpulse/settings/AccountTest.kt index 3600e74..955e723 100644 --- a/src/test/kotlin/org/zhavoronkov/tokenpulse/settings/AccountTest.kt +++ b/src/test/kotlin/org/zhavoronkov/tokenpulse/settings/AccountTest.kt @@ -63,6 +63,66 @@ class AccountTest { assertEquals("OpenAI: Codex CLI", label) } + + @Test + fun `Account fields are mutable (var not val) for XStream serialization`() { + val account = Account( + id = "test-id", + name = "Test", + connectionType = ConnectionType.OPENROUTER_PROVISIONING, + authType = AuthType.OPENROUTER_PROVISIONING_KEY, + keyPreview = "sk-or-…91bc" + ) + + // Verify all fields can be modified (var not val) + account.id = "new-id" + account.name = "New Name" + account.connectionType = ConnectionType.XIAOMI_API + account.authType = AuthType.XIAOMI_API_KEY + account.isEnabled = false + account.keyPreview = "new-preview" + + assertEquals("new-id", account.id) + assertEquals("New Name", account.name) + assertEquals(ConnectionType.XIAOMI_API, account.connectionType) + assertEquals(AuthType.XIAOMI_API_KEY, account.authType) + assertFalse(account.isEnabled) + assertEquals("new-preview", account.keyPreview) + } + + @Test + fun `Account copy preserves all fields`() { + val original = Account( + id = "test-id", + name = "Test", + connectionType = ConnectionType.XIAOMI_TOKEN_PLAN, + authType = AuthType.XIAOMI_TOKEN_PLAN_KEY, + isEnabled = false, + keyPreview = "tp-…1234" + ) + + val copy = original.copy() + + assertEquals(original.id, copy.id) + assertEquals(original.name, copy.name) + assertEquals(original.connectionType, copy.connectionType) + assertEquals(original.authType, copy.authType) + assertEquals(original.isEnabled, copy.isEnabled) + assertEquals(original.keyPreview, copy.keyPreview) + } + + @Test + fun `Account connectionType and authType are preserved after copy`() { + val original = Account( + connectionType = ConnectionType.NEBIUS_BILLING, + authType = AuthType.NEBIUS_BILLING_SESSION + ) + + val copy = original.copy(name = "Updated") + + assertEquals(ConnectionType.NEBIUS_BILLING, copy.connectionType) + assertEquals(AuthType.NEBIUS_BILLING_SESSION, copy.authType) + } } /**