test(json): 난독화 안정성 검증을 추가한다

This commit is contained in:
2026-07-09 17:39:07 +09:00
parent 33f42e62b6
commit 79a6ecdc0b
2 changed files with 150 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
package kr.co.vividnext.sodalive.json
import com.google.gson.annotations.SerializedName
import java.io.File
import kr.co.vividnext.sodalive.audio_content.playlist.modify.UpdatePlaylistRequest
import kr.co.vividnext.sodalive.chat.original.OriginalWorkListItemResponse
import kr.co.vividnext.sodalive.chat.original.OriginalWorkListResponse
import kr.co.vividnext.sodalive.mypage.auth.AuthResponse
import kr.co.vividnext.sodalive.mypage.can.payment.payverse.PayverseVerifyRequest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
class DtoObfuscationStabilityTest {
@Test
fun `확정 DTO class는 release minify 보존 annotation을 가진다`() {
assertSourceContains("app/src/main/java/kr/co/vividnext/sodalive/mypage/auth/AuthResponse.kt", "@Keep")
assertSourceContains(
"app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/modify/UpdatePlaylistRequest.kt",
"@Keep"
)
assertSourceContains(
"app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/payverse/PayverseChargeDto.kt",
"@Keep\ndata class PayverseVerifyRequest"
)
assertSourceContains(
"app/src/main/java/kr/co/vividnext/sodalive/chat/original/OriginalWorkListResponse.kt",
"@Keep\ndata class OriginalWorkListResponse"
)
assertSourceContains(
"app/src/main/java/kr/co/vividnext/sodalive/chat/original/OriginalWorkListResponse.kt",
"@Keep\ndata class OriginalWorkListItemResponse"
)
}
@Test
fun `확정 DTO field는 SerializedName으로 JSON 이름을 고정한다`() {
assertSerializedName(OriginalWorkListResponse::class.java, "totalCount", "totalCount")
assertSerializedName(OriginalWorkListResponse::class.java, "content", "content")
assertSerializedName(OriginalWorkListItemResponse::class.java, "id", "id")
assertSerializedName(OriginalWorkListItemResponse::class.java, "imageUrl", "imageUrl")
assertSerializedName(OriginalWorkListItemResponse::class.java, "title", "title")
assertSerializedName(OriginalWorkListItemResponse::class.java, "contentType", "contentType")
assertSerializedName(AuthResponse::class.java, "gender", "gender")
assertSerializedName(UpdatePlaylistRequest::class.java, "title", "title")
assertSerializedName(UpdatePlaylistRequest::class.java, "desc", "desc")
assertSerializedName(UpdatePlaylistRequest::class.java, "contentIdAndOrderList", "contentIdAndOrderList")
assertSerializedName(PayverseVerifyRequest::class.java, "transactionId", "transactionId")
assertSerializedName(PayverseVerifyRequest::class.java, "orderId", "orderId")
}
private fun assertSourceContains(relativePath: String, expected: String) {
val source = File(projectRoot(), relativePath).readText()
assertTrue("$relativePath must contain $expected", source.contains(expected))
}
private fun projectRoot(): File {
val workingDirectory = File(System.getProperty("user.dir"))
return if (File(workingDirectory, "settings.gradle").exists()) {
workingDirectory
} else {
checkNotNull(workingDirectory.parentFile) { "Project root not found from $workingDirectory" }
}
}
private fun assertSerializedName(clazz: Class<*>, fieldName: String, expectedValue: String) {
val field = clazz.getDeclaredField(fieldName)
val annotation = field.getAnnotation(SerializedName::class.java)
assertTrue("${clazz.simpleName}.$fieldName must have @SerializedName", annotation != null)
assertEquals(expectedValue, annotation?.value)
}
}

View File

@@ -0,0 +1,78 @@
package kr.co.vividnext.sodalive.json
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.home.ContentRankingSortType
import kr.co.vividnext.sodalive.home.SeriesPublishedDaysOfWeek
import kr.co.vividnext.sodalive.v2.common.data.ContentSort
import kr.co.vividnext.sodalive.v2.main.content.data.AudioRankingType
import kr.co.vividnext.sodalive.v2.main.content.data.MainContentAllType
import kr.co.vividnext.sodalive.v2.main.content.overview.data.ContentOverviewType
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
class EnumObfuscationStabilityTest {
@Test
fun `AudioRankingType은 JSON 이름과 query 값을 고정한다`() {
assertEnumWireValues(
AudioRankingType::class.java,
listOf("WEEKLY_POPULAR", "RISING", "REVENUE", "SALES_COUNT", "COMMENT_COUNT", "LIKE_COUNT")
) { it.queryValue }
}
@Test
fun `MainContentAllType은 JSON 이름과 query 값을 고정한다`() {
assertEnumWireValues(
MainContentAllType::class.java,
listOf("AUDIO", "SERIES", "ORIGINAL", "FREE", "POINT")
) { it.queryValue }
}
@Test
fun `ContentSort는 JSON 이름과 query 값을 고정한다`() {
assertEnumWireValues(
ContentSort::class.java,
listOf("LATEST", "POPULAR", "OWNED", "PRICE_HIGH", "PRICE_LOW")
) { it.queryValue }
}
@Test
fun `ContentOverviewType은 JSON 이름과 query 값을 고정한다`() {
assertEnumWireValues(
ContentOverviewType::class.java,
listOf("NEW_AND_HOT_AUDIO")
) { it.queryValue }
}
@Test
fun `SeriesPublishedDaysOfWeek는 JSON 이름과 query 값을 고정한다`() {
assertEnumWireValues(
SeriesPublishedDaysOfWeek::class.java,
listOf("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT", "RANDOM")
) { it.queryValue }
}
@Test
fun `ContentRankingSortType은 JSON 이름과 query 값을 고정한다`() {
assertEnumWireValues(
ContentRankingSortType::class.java,
listOf("REVENUE", "SALES_COUNT", "COMMENT_COUNT", "LIKE_COUNT")
) { it.queryValue }
}
private fun <T : Enum<T>> assertEnumWireValues(
enumClass: Class<T>,
expectedValues: List<String>,
queryValue: (T) -> String
) {
val constants = enumClass.enumConstants?.toList().orEmpty()
assertEquals(expectedValues, constants.map { it.name })
constants.zip(expectedValues).forEach { (constant, expectedValue) ->
val field = enumClass.getField(constant.name)
val annotation = field.getAnnotation(SerializedName::class.java)
assertTrue("${enumClass.simpleName}.${constant.name} must have @SerializedName", annotation != null)
assertEquals(expectedValue, annotation?.value)
assertEquals(expectedValue, queryValue(constant))
}
}
}