From 2df899aacab18961aa4fa2ea1b2b233c638c9575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=BC=ED=98=81?= Date: Sat, 29 Aug 2026 22:22:25 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor(core):=20=EC=A0=84=EC=97=AD=20coer?= =?UTF-8?q?ceInputValues=20=EB=A5=BC=20=EA=B1=B7=EC=96=B4=EB=82=B8?= =?UTF-8?q?=EB=8B=A4=20(#1494)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit non-null 프로퍼티에 null 이 와도 기본값이 있으면 조용히 치환해, 계약 위반이 텔레메트리 없이 삼켜졌다. 이 팀 기준은 «제외하더라도 세어서 보고» 다(#1010). 응답 DTO 중 실제 coercion 대상은 ReceivedTimeLetterDto.blocks 하나뿐이고, BE ReceivedTimeLetterResponse 가 미공개 구간에 List.of() 를 채우므로 서버는 이 키를 null 로 보내지 않는다. 나머지 5곳은 요청 DTO 라 디코딩과 무관하다. konsist 허용목록에서 이미 해소된 13건을 지우고, 프로덕션 설정을 복제하던 core/network 테스트 픽스처 5곳도 같이 맞춘다. --- .../core/network/di/NetworkModule.kt | 5 ++- .../network/dto/AppVersionContractTest.kt | 1 - .../dto/AuthDtoExpiresInContractTest.kt | 1 - .../dto/AuthDtoSocialLoginContractTest.kt | 6 +-- .../core/network/dto/AuthFindContractTest.kt | 1 - .../service/ApiWireContractSmokeTest.kt | 1 - .../konsist/ResponseDtoContractKonsistTest.kt | 41 ++++++------------- 7 files changed, 18 insertions(+), 38 deletions(-) diff --git a/core/network/src/main/kotlin/com/afternote/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/afternote/core/network/di/NetworkModule.kt index e12841edd1..f489f93942 100644 --- a/core/network/src/main/kotlin/com/afternote/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/afternote/core/network/di/NetworkModule.kt @@ -98,8 +98,9 @@ object NetworkModule { // 이 모듈은 오브젝트 클래스 선언해서 딱 fun provideJson(): Json = Json { ignoreUnknownKeys = true // dto로 정의되지 않은 키가 있어도 크래시 나지 않게 함 - coerceInputValues = - true // 응답 json으로 유효하지 않은 필드 값이 오더라도 대응되는 dto 변수의 디폴트 값이 있다면 그 값으로 치환 + // coerceInputValues 는 두지 않는다. non-null 프로퍼티에 null 이 와도 기본값이 있으면 조용히 + // 치환해 계약 위반을 삼킨다 — 이 팀 기준은 «제외하더라도 세어서 보고» 다(#1010). + // 응답 DTO 에 보정형 기본값을 새로 두는 것은 ResponseDtoContractKonsistTest 가 막는다. // useAlternativeNames 는 기본 true — DTO 프로퍼티에 @JsonNames("대체키") 를 달면 // 서버가 같은 필드를 다른 키로 내려줘도(명세·실서버 불일치, 키 리네임 과도기) 한 프로퍼티로 파싱된다. // 디코딩 전용이라 요청 직렬화 출력 키는 항상 @SerialName 을 따름. 사용 예: mindrecord DailyQuestionListItem diff --git a/core/network/src/test/kotlin/com/afternote/core/network/dto/AppVersionContractTest.kt b/core/network/src/test/kotlin/com/afternote/core/network/dto/AppVersionContractTest.kt index e2ec618e75..9f40398b7d 100644 --- a/core/network/src/test/kotlin/com/afternote/core/network/dto/AppVersionContractTest.kt +++ b/core/network/src/test/kotlin/com/afternote/core/network/dto/AppVersionContractTest.kt @@ -16,7 +16,6 @@ class AppVersionContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoExpiresInContractTest.kt b/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoExpiresInContractTest.kt index 6846063311..12d4574626 100644 --- a/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoExpiresInContractTest.kt +++ b/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoExpiresInContractTest.kt @@ -19,7 +19,6 @@ class AuthDtoExpiresInContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoSocialLoginContractTest.kt b/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoSocialLoginContractTest.kt index c5a97840d8..89d5d94b86 100644 --- a/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoSocialLoginContractTest.kt +++ b/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthDtoSocialLoginContractTest.kt @@ -26,7 +26,6 @@ class AuthDtoSocialLoginContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } private fun payload(newUserField: String) = @@ -64,8 +63,9 @@ class AuthDtoSocialLoginContractTest { } @Test - fun `isNewUser 가 null - coerceInputValues 로 흡수되지 않고 실패한다`() { - // 기본값이 없어야 coerce 대상에서 빠진다. 기본값을 되살리면 이 테스트가 먼저 깨진다. + fun `isNewUser 가 null 이면 흡수하지 않고 실패한다`() { + // 전역 coerceInputValues 를 걷어냈으므로(#1494) 기본값 유무와 무관하게 null 은 실패한다. + // 이 단언은 그 «null 을 조용히 삼키지 않는다» 를 고정한다. assertThrows(SerializationException::class.java) { json.decodeFromString>(payload(""","isNewUser":null""")) } diff --git a/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthFindContractTest.kt b/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthFindContractTest.kt index 8cbdb3df53..2fc0af7b2e 100644 --- a/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthFindContractTest.kt +++ b/core/network/src/test/kotlin/com/afternote/core/network/dto/AuthFindContractTest.kt @@ -14,7 +14,6 @@ class AuthFindContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/core/network/src/test/kotlin/com/afternote/core/network/service/ApiWireContractSmokeTest.kt b/core/network/src/test/kotlin/com/afternote/core/network/service/ApiWireContractSmokeTest.kt index 2ab1861134..6e2612efe9 100644 --- a/core/network/src/test/kotlin/com/afternote/core/network/service/ApiWireContractSmokeTest.kt +++ b/core/network/src/test/kotlin/com/afternote/core/network/service/ApiWireContractSmokeTest.kt @@ -524,7 +524,6 @@ class ApiWireContractSmokeTest { private val wireJson = Json { ignoreUnknownKeys = true - coerceInputValues = true } private lateinit var mockServer: MockServerContainer diff --git a/konsist/src/test/kotlin/com/afternote/konsist/ResponseDtoContractKonsistTest.kt b/konsist/src/test/kotlin/com/afternote/konsist/ResponseDtoContractKonsistTest.kt index fa451a4c54..8b5a4c3a36 100644 --- a/konsist/src/test/kotlin/com/afternote/konsist/ResponseDtoContractKonsistTest.kt +++ b/konsist/src/test/kotlin/com/afternote/konsist/ResponseDtoContractKonsistTest.kt @@ -16,9 +16,11 @@ import org.junit.Test * - `hasNext = false` → 페이지네이션이 조용히 멈춤 * - `diaries = emptyList()` → 목록 소실이 "기록 없음" 으로 보임 * - * `coerceInputValues` 는 **기본값이 있어야** 동작하므로, 기본값을 두는 순간 "키 누락" 과 - * "서버가 보낸 잘못된 값" 이 같은 결과로 수렴한다. 둘은 성격이 다르다 — - * 키 누락은 계약이 바뀐 것이라 실패해야 드러나고, 값 확장은 관대해도 된다(전용 직렬화기). + * 기본값을 두는 순간 "키 누락" 과 "서버가 보낸 정상 빈 값" 이 같은 결과로 수렴한다. 둘은 성격이 + * 다르다 — 키 누락은 계약이 바뀐 것이라 실패해야 드러나고, 값 확장은 관대해도 된다(전용 직렬화기). + * + * 전역 `coerceInputValues` 는 #1494 에서 걷어냈다. 그래서 이제 `null` 이 온 경우는 기본값 유무와 + * 무관하게 실패하고, 이 가드가 막는 것은 **키 누락**이 기본값으로 성공해 버리는 쪽이다. * * ### 검사 대상 * `..dto..` 패키지의 `@Serializable data class` 중 **이름에 `Request` 가 없는 것**(= 응답). @@ -101,34 +103,15 @@ class ResponseDtoContractKonsistTest { const val REQUEST_DTO = "Request" /** - * #789 — mindrecord (2건). #933 이 26건을 해소한 뒤 #573 이 머지되며 남은 잔여다. - * BE `DailyQuestionAnswerResponse` record 에 두 키가 다 있어 서버는 항상 보낸다. + * 남은 잔여는 timeletter 1건뿐이다 — #790 이 추적하고, `ci-expected-failures.json` 에 + * 의도된 실패로 등록돼 있다. mindrecord 2건(#789)·afternote 11건(#957)은 해소돼 빠졌다. + * + * 서버는 이 키를 `null` 로 보내지 않는다(BE `ReceivedTimeLetterResponse` 가 미공개 구간에 + * `List.of()` 를 채운다). 그래서 `coerceInputValues` 없이도 파싱이 깨지지 않는다 — 이 + * 기본값이 실제로 가리는 것은 «키 자체가 빠지는» 경우다. */ - private val MINDRECORD = - setOf( - "DailyQuestionAnswerResponseDto.content", - "DailyQuestionAnswerResponseDto.isDraft", - ) - - /** #957 — afternote (11건). `AfternoteDetailDto.createdAt` 은 #676 에서 해소(BE 응답에 없는 필드라 삭제). */ - private val AFTERNOTE = - setOf( - "AfternoteDetailDto.updatedAt", - "AfternotePlaylistDto.songs", - "AfternotePageDto.content", - "AfternotePageDto.page", - "AfternotePageDto.size", - "AfternotePageDto.hasNext", - "ReceivedAfternoteListDto.afternotes", - "ReceivedAfternoteListDto.totalCount", - "ReceivedAfternoteDetailDto.processingMethods", - "ReceivedPlaylistDto.songs", - "MusicSearchResponseDto.tracks", - ) - - /** #790 — timeletter (1건) */ private val TIMELETTER = setOf("ReceivedTimeLetterDto.blocks") - val KNOWN_COERCING_DEFAULTS = MINDRECORD + AFTERNOTE + TIMELETTER + val KNOWN_COERCING_DEFAULTS = TIMELETTER } } From cedca723b7e91a97e18e34d83c6fefdea264ba52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=BC=ED=98=81?= Date: Sun, 30 Aug 2026 02:49:56 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(core):=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=8F=84=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=8D=95=EC=85=98=20Json=20=EC=84=A4=EC=A0=95=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EC=B6=98=EB=8B=A4=20(#1494)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 프로덕션에서 `coerceInputValues` 를 걷어내고도 계약 테스트 15곳이 그대로 켜 두고 있었다. 「프로덕션과 같은 설정에서 계약이 지켜지는가」를 보는 테스트가 **더 관대한 설정에서** 통과해, 서버가 non-null 자리에 null 을 보내면 프로덕션은 던지는데 테스트는 초록인 드리프트였다. - 테스트 Json 15곳의 `coerceInputValues = true` 제거 — 리뷰 시점 14곳에 develop 병합으로 들어온 `ReceiverAfternotePagingSourceTest` 1곳을 더했다. - 「provideJson 과 동일 (ignoreUnknownKeys + coerceInputValues)」 주석 7곳을 사실에 맞게 정정. 걷어낸 지금 테스트 설정은 `provideJson` 과 실제로 동일해졌다. - `ReceivedAfternoteDetailTitleContractTest` 의 사라진 기제를 가리키던 KDoc·테스트명 정정. 걷어낸 뒤 깨지는 테스트는 없다. `:feature:timeletter:data` 의 4건은 제거 전후가 동일한 #790·#873 의도된 실패(`ci-expected-failures.json`)로, 같은 태스크를 제거 전 트리에서 돌려 대조했다. --- .../afternote/data/dto/AfternoteResponseDtoContractTest.kt | 1 - .../afternote/data/mapper/LeaveMessageBlockContractTest.kt | 1 - .../mindrecord/data/dto/DailyQuestionImageContractTest.kt | 1 - .../feature/mindrecord/data/dto/DiaryListContractTest.kt | 3 +-- .../mindrecord/data/dto/MindRecordDtoContractTest.kt | 3 +-- .../mindrecord/data/dto/TodayDailyQuestionContractTest.kt | 3 +-- .../mindrecord/data/dto/WeeklyReportContractTest.kt | 3 +-- .../mindrecord/data/mapper/DiaryUpdateRequestTest.kt | 1 - .../receiver/data/dto/DeliveryVerificationContractTest.kt | 3 +-- .../data/dto/ReceivedAfternoteDetailTitleContractTest.kt | 7 +++---- .../data/dto/ReceiverAfternoteResponseDtoContractTest.kt | 1 - .../receiver/data/dto/ReceiverEmailAuthContractTest.kt | 3 +-- .../receiver/data/dto/ReceiverMessageContractTest.kt | 3 +-- .../data/paging/ReceiverAfternotePagingSourceTest.kt | 1 - .../timeletter/data/dto/TimeLetterDtoContractTest.kt | 1 - 15 files changed, 10 insertions(+), 25 deletions(-) diff --git a/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/dto/AfternoteResponseDtoContractTest.kt b/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/dto/AfternoteResponseDtoContractTest.kt index fb595b8fae..089a7225ef 100644 --- a/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/dto/AfternoteResponseDtoContractTest.kt +++ b/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/dto/AfternoteResponseDtoContractTest.kt @@ -20,7 +20,6 @@ class AfternoteResponseDtoContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/mapper/LeaveMessageBlockContractTest.kt b/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/mapper/LeaveMessageBlockContractTest.kt index 5a6403a7ff..ef555ac44a 100644 --- a/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/mapper/LeaveMessageBlockContractTest.kt +++ b/feature/afternote/data/src/test/java/com/afternote/feature/afternote/data/mapper/LeaveMessageBlockContractTest.kt @@ -29,7 +29,6 @@ class LeaveMessageBlockContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } /** diff --git a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DailyQuestionImageContractTest.kt b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DailyQuestionImageContractTest.kt index 888a086794..29a9834fb3 100644 --- a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DailyQuestionImageContractTest.kt +++ b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DailyQuestionImageContractTest.kt @@ -21,7 +21,6 @@ class DailyQuestionImageContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DiaryListContractTest.kt b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DiaryListContractTest.kt index ea8a79ec23..9226e0ae0e 100644 --- a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DiaryListContractTest.kt +++ b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/DiaryListContractTest.kt @@ -19,13 +19,12 @@ import org.junit.Test * `date`(사용자가 고른 일기 날짜)와 `createdAt`(레코드 생성 시각)은 **별개 필드**다. * 한 프로퍼티에 묶으면 서버의 키 순서에 따라 값이 뒤바뀌므로 분리 상태를 함께 고정한다. * - * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys + coerceInputValues). + * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys). */ class DiaryListContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/MindRecordDtoContractTest.kt b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/MindRecordDtoContractTest.kt index de8cde2b99..840941f427 100644 --- a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/MindRecordDtoContractTest.kt +++ b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/MindRecordDtoContractTest.kt @@ -18,13 +18,12 @@ import org.junit.Test * - 실제 의미가 있는 명시적 `null` → 정상 파싱 (값이 없는 상태) * - 클라가 모르는 기분 **값** → 그 칸만 비움 (표기가 늘어난 것) * - * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys + coerceInputValues). + * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys). */ class MindRecordDtoContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } // ---------- 데일리질문 목록 ---------- diff --git a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/TodayDailyQuestionContractTest.kt b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/TodayDailyQuestionContractTest.kt index 639ee859eb..888e32808f 100644 --- a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/TodayDailyQuestionContractTest.kt +++ b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/TodayDailyQuestionContractTest.kt @@ -17,14 +17,13 @@ import org.junit.Test * `isAnswered`/`isDraft` 는 사정이 다르다 — 종전 키(`answered`/`draft`)로 잡혀 있어 * `getToday()` 가 항상 실패했던 이력(#548)이 있어 과도기 대비로 구 키를 함께 받는다. * - * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys + coerceInputValues). + * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys). */ @OptIn(ExperimentalSerializationApi::class) class TodayDailyQuestionContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } private fun decode(body: String): TodayDailyQuestionDto = diff --git a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/WeeklyReportContractTest.kt b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/WeeklyReportContractTest.kt index 6d53e5c34e..2ecd1ce16b 100644 --- a/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/WeeklyReportContractTest.kt +++ b/feature/mindrecord/data/src/test/java/com/afternote/feature/mindrecord/data/dto/WeeklyReportContractTest.kt @@ -23,14 +23,13 @@ import org.junit.Test * 항상 `false` 가 됐고, 그래서 모든 일기가 캘린더 점과 기록일수에서 빠졌다 — 이 파일이 * 그 회귀를 막는다. * - * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys + coerceInputValues). + * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys). */ @OptIn(ExperimentalSerializationApi::class) class WeeklyReportContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } // 카운트·요약·목록 키는 모두 계약이라 기본값이 없다 (#789) — 주(week) 형태만 바꿔가며 diff --git a/feature/mindrecord/data/src/test/kotlin/com/afternote/feature/mindrecord/data/mapper/DiaryUpdateRequestTest.kt b/feature/mindrecord/data/src/test/kotlin/com/afternote/feature/mindrecord/data/mapper/DiaryUpdateRequestTest.kt index 7b0091c902..a555494f5d 100644 --- a/feature/mindrecord/data/src/test/kotlin/com/afternote/feature/mindrecord/data/mapper/DiaryUpdateRequestTest.kt +++ b/feature/mindrecord/data/src/test/kotlin/com/afternote/feature/mindrecord/data/mapper/DiaryUpdateRequestTest.kt @@ -22,7 +22,6 @@ class DiaryUpdateRequestTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } private fun bodyOf(receiverIds: List?) = diff --git a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/DeliveryVerificationContractTest.kt b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/DeliveryVerificationContractTest.kt index b27ed524bb..ab362ccfd0 100644 --- a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/DeliveryVerificationContractTest.kt +++ b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/DeliveryVerificationContractTest.kt @@ -11,7 +11,7 @@ import org.junit.Test * (`afternote.kro.kr/v3/api-docs`) 실측: `DeliveryVerificationRequestDto` 스키마에서 required 제거, * 두 필드 모두 nullable, description "두 서류 중 하나 이상 필수". * - * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys + coerceInputValues). + * Json 설정은 `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys). * `encodeDefaults` 미설정(기본 false) + DTO 의 `= null` default 조합이라, 제출하지 않은 슬롯은 * 페이로드에서 **키 자체가 생략**된다 — null 을 명시 전송하지 않아도 서버가 미제출로 해석하는 * 형태를 이 테스트가 고정한다. @@ -20,7 +20,6 @@ class DeliveryVerificationContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceivedAfternoteDetailTitleContractTest.kt b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceivedAfternoteDetailTitleContractTest.kt index e41c8cc251..a05a7f0e11 100644 --- a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceivedAfternoteDetailTitleContractTest.kt +++ b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceivedAfternoteDetailTitleContractTest.kt @@ -20,14 +20,13 @@ import org.junit.Test * * 이 가드가 지키는 것은 그 계약이 깨졌을 때의 **실패 방향**이다. 예전처럼 `String? = null` 이면 * 키가 빠져도 파싱이 조용히 통과하고 화면이 제목 없이 열려, 서버 결함이 «제목이 비어 보이는» - * 정상 화면으로 위장된다. Json 설정은 `NetworkModule.provideJson` 과 동일 — - * `coerceInputValues` 는 **기본값이 있어야** 동작하므로 이 필드를 구해 주지 않는다. + * 정상 화면으로 위장된다. Json 설정은 `NetworkModule.provideJson` 과 동일 — 전역 + * `coerceInputValues` 를 걷어냈으므로(#1494) 기본값 유무와 무관하게 `null` 은 실패로 남는다. */ class ReceivedAfternoteDetailTitleContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test @@ -49,7 +48,7 @@ class ReceivedAfternoteDetailTitleContractTest { } @Test - fun `제목이 명시적 null 인 응답 - coerceInputValues 가 구해 주지 않는다`() { + fun `제목이 명시적 null 인 응답 - 기본값으로 접히지 않고 실패한다`() { val payload = """{"status":200,"code":200,"message":"성공","data":{"id":1,"category":"SOCIAL","title":null}}""" diff --git a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverAfternoteResponseDtoContractTest.kt b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverAfternoteResponseDtoContractTest.kt index 3111566051..c4a63d51b7 100644 --- a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverAfternoteResponseDtoContractTest.kt +++ b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverAfternoteResponseDtoContractTest.kt @@ -18,7 +18,6 @@ class ReceiverAfternoteResponseDtoContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverEmailAuthContractTest.kt b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverEmailAuthContractTest.kt index f7369f72f6..5fcb4c4500 100644 --- a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverEmailAuthContractTest.kt +++ b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverEmailAuthContractTest.kt @@ -15,7 +15,7 @@ import org.junit.Test * (필드 구성·타입은 Swagger 와 BE 소스로 확정, 이슈 #407 본문). * 프로덕션 경로(`ReceiverAuthRepositoryImpl`)와 동일하게 Json 디코드 → `requireData()`/`requireStatus()` * → `toDomain()` 을 통과시킨다 — Json 설정은 `NetworkModule.provideJson` 과 동일 - * (ignoreUnknownKeys + coerceInputValues). + * (ignoreUnknownKeys). * * verify 성공 응답을 **accessCode 동봉·제거 두 형태 모두** 가드하는 이유: 서버가 마스터 키와 동일한 * `accessCode` 를 응답에서 제거할 예정이라([ReceiverEmailAuthVerifyDto] 참고), 제거 배포가 언제 나가든 @@ -28,7 +28,6 @@ class ReceiverEmailAuthContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverMessageContractTest.kt b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverMessageContractTest.kt index 8025c47165..7f90bec3af 100644 --- a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverMessageContractTest.kt +++ b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/dto/ReceiverMessageContractTest.kt @@ -14,13 +14,12 @@ import org.junit.Test * BE `LocalDateTime` 직렬화라 타임존 표기 없는 마이크로초 포함 형태로 온다 (BE 커밋 5af499c8). * 프로덕션 경로(`ReceiverAuthRepositoryImpl.getSenderMessage`)와 동일하게 * Json 디코드 → `requireData()` → `toDomain()` 을 통과시킨다 — Json 설정은 - * `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys + coerceInputValues). + * `NetworkModule.provideJson` 과 동일 (ignoreUnknownKeys). */ class ReceiverMessageContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test diff --git a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/paging/ReceiverAfternotePagingSourceTest.kt b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/paging/ReceiverAfternotePagingSourceTest.kt index d2781c5678..1002b81478 100644 --- a/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/paging/ReceiverAfternotePagingSourceTest.kt +++ b/feature/receiver/data/src/test/kotlin/com/afternote/feature/receiver/data/paging/ReceiverAfternotePagingSourceTest.kt @@ -37,7 +37,6 @@ class ReceiverAfternotePagingSourceTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } /** diff --git a/feature/timeletter/data/src/test/kotlin/com/afternote/feature/timeletter/data/dto/TimeLetterDtoContractTest.kt b/feature/timeletter/data/src/test/kotlin/com/afternote/feature/timeletter/data/dto/TimeLetterDtoContractTest.kt index a5889cc69b..1565d1f54b 100644 --- a/feature/timeletter/data/src/test/kotlin/com/afternote/feature/timeletter/data/dto/TimeLetterDtoContractTest.kt +++ b/feature/timeletter/data/src/test/kotlin/com/afternote/feature/timeletter/data/dto/TimeLetterDtoContractTest.kt @@ -25,7 +25,6 @@ class TimeLetterDtoContractTest { private val json = Json { ignoreUnknownKeys = true - coerceInputValues = true } @Test