refactor: migrate Gson serialization to kotlinx.serialization (#475)
* refactor: migrate Gson serialization to kotlinx.serialization * fix(dto): resolve serializing Any? and Map<String, Any?> with JsonElement * test(refactor): fix unit test issues under kotlinx.serialization and update nullable DTO defaults * fix(chat): support flexible numeric and string decoding for message timestamps * fix(chat): sequentialize cache loading to avoid overwriting network state
This commit is contained in:
64 files changed
+863
-577
No files matched your search
+4
-3
@@ -3,6 +3,7 @@ package com.m57.hermescontrol.data.remote
|
||||
import com.m57.hermescontrol.data.model.SessionMessage
|
||||
import com.m57.hermescontrol.data.model.StatusResponse
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
@@ -13,7 +14,7 @@ import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
|
||||
/**
|
||||
* MockWebServer-based tests for [HermesApiService].
|
||||
@@ -35,7 +36,7 @@ class HermesApiServiceMockWebServerTest {
|
||||
Retrofit
|
||||
.Builder()
|
||||
.baseUrl(mockServer.url("/"))
|
||||
.addConverterFactory(GsonConverterFactory.create(OkHttpProvider.gson))
|
||||
.addConverterFactory(OkHttpProvider.json.asConverterFactory("application/json".toMediaType()))
|
||||
.build()
|
||||
.create(HermesApiService::class.java)
|
||||
}
|
||||
@@ -244,7 +245,7 @@ class HermesApiServiceMockWebServerTest {
|
||||
|
||||
assertEquals("user", messages[0].role)
|
||||
assertEquals("Hello Hermes", messages[0].content)
|
||||
assertEquals("1718000000", messages[0].timestamp)
|
||||
assertEquals("1718000000", messages[0].timestampText)
|
||||
|
||||
assertEquals("assistant", messages[1].role)
|
||||
assertEquals("Hi! How can I help?", messages[1].content)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.m57.hermescontrol.data.remote
|
||||
|
||||
import com.m57.hermescontrol.data.model.ToggleSkillRequest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
@@ -13,7 +14,7 @@ import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
|
||||
class HermesApiServiceTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
@@ -28,7 +29,7 @@ class HermesApiServiceTest {
|
||||
Retrofit
|
||||
.Builder()
|
||||
.baseUrl(mockWebServer.url("/"))
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.addConverterFactory(OkHttpProvider.json.asConverterFactory("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
apiService = retrofit.create(HermesApiService::class.java)
|
||||
@@ -151,58 +152,49 @@ class HermesApiServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSkills_missingFieldsInResponse() =
|
||||
runTest {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""[{"description":"info"}]"""),
|
||||
)
|
||||
fun testGetSkills_missingFieldsInResponse() {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""[{"description":"info"}]"""),
|
||||
)
|
||||
|
||||
val response = apiService.getSkills()
|
||||
assertTrue(response.isSuccessful)
|
||||
val skills = response.body()
|
||||
assertEquals(1, skills?.size)
|
||||
val skill = skills?.get(0)
|
||||
org.junit.jupiter.api.Assertions
|
||||
.assertNull(skill?.name)
|
||||
assertEquals(false, skill?.enabled)
|
||||
assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
runTest {
|
||||
apiService.getSkills()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCronJobs_missingFieldsInResponse() =
|
||||
runTest {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""[{"schedule":"* * * * *"}]"""),
|
||||
)
|
||||
fun testGetCronJobs_missingFieldsInResponse() {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""[{"schedule":"* * * * *"}]"""),
|
||||
)
|
||||
|
||||
val response = apiService.getCronJobs()
|
||||
assertTrue(response.isSuccessful)
|
||||
val jobs = response.body()
|
||||
assertEquals(1, jobs?.size)
|
||||
val job = jobs?.get(0)
|
||||
org.junit.jupiter.api.Assertions
|
||||
.assertNull(job?.id)
|
||||
org.junit.jupiter.api.Assertions
|
||||
.assertNull(job?.name)
|
||||
assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
runTest {
|
||||
apiService.getCronJobs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSessions_missingSessionsField() =
|
||||
runTest {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""{}"""),
|
||||
)
|
||||
fun testGetSessions_missingSessionsField() {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""{}"""),
|
||||
)
|
||||
|
||||
val response = apiService.getSessions()
|
||||
assertTrue(response.isSuccessful)
|
||||
org.junit.jupiter.api.Assertions
|
||||
.assertNull(response.body()?.sessions)
|
||||
assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
runTest {
|
||||
apiService.getSessions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSessionMessages_apiReturnsMalformedJson_throwsException() {
|
||||
@@ -212,7 +204,7 @@ class HermesApiServiceTest {
|
||||
.setBody("""{"messages": ["""),
|
||||
)
|
||||
|
||||
assertThrows(java.io.EOFException::class.java) {
|
||||
assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
runTest {
|
||||
apiService.getSessionMessages("session_1")
|
||||
}
|
||||
@@ -220,19 +212,19 @@ class HermesApiServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSessionMessages_missingMessagesField() =
|
||||
runTest {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""{}"""),
|
||||
)
|
||||
fun testGetSessionMessages_missingMessagesField() {
|
||||
mockWebServer.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(200)
|
||||
.setBody("""{}"""),
|
||||
)
|
||||
|
||||
val response = apiService.getSessionMessages("session_1")
|
||||
assertTrue(response.isSuccessful)
|
||||
org.junit.jupiter.api.Assertions
|
||||
.assertNull(response.body()?.messages)
|
||||
assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
runTest {
|
||||
apiService.getSessionMessages("session_1")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSkills_malformedJsonResponse_throwsException() =
|
||||
@@ -243,7 +235,7 @@ class HermesApiServiceTest {
|
||||
.setBody("""[{"name":"weather""""),
|
||||
)
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertThrows(java.io.EOFException::class.java) {
|
||||
org.junit.jupiter.api.Assertions.assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
kotlinx.coroutines.runBlocking {
|
||||
apiService.getSkills()
|
||||
}
|
||||
@@ -259,7 +251,7 @@ class HermesApiServiceTest {
|
||||
.setBody("""[{"name":"weather","enabled":{}}]"""),
|
||||
)
|
||||
|
||||
org.junit.jupiter.api.Assertions.assertThrows(com.google.gson.JsonSyntaxException::class.java) {
|
||||
org.junit.jupiter.api.Assertions.assertThrows(kotlinx.serialization.SerializationException::class.java) {
|
||||
kotlinx.coroutines.runBlocking {
|
||||
apiService.getSkills()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user