Repository navigation
refactor(mindrecord): 생성·수정 응답 DTO 의 기본값을 걷는다 (#789 마무리) #1156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,6 +89,9 @@ data class TodayDailyQuestionDto( | |
| @Serializable | ||
| data class DailyQuestionAnswerResponseDto( | ||
| @SerialName("userDailyQuestionId") val userDailyQuestionId: Long, | ||
| @SerialName("content") val content: String = "", | ||
| @SerialName("isDraft") val isDraft: Boolean = false, | ||
| // 기본값을 두지 않는다 — 서버가 항상 채우는 값이라, 기본값이 있으면 키 누락과 계약 | ||
| // 변경이 파싱 실패가 아니라 «정상적인 빈 값» 으로 바뀐다. 특히 isDraft 가 false 로 | ||
| // 접히면 임시저장이 정식 답변으로 보인다 (#789). | ||
| @SerialName("content") val content: String, | ||
| @SerialName("isDraft") val isDraft: Boolean, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [normal] 이 파일의 형제 둘이 막아 둔 키 변형에 여기만 무방비다. 같은 파일의 기본값이 있던 동안에는 키가 현재 계약이 |
||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| package com.afternote.feature.mindrecord.data.dto | ||
|
|
||
| import com.afternote.core.network.model.BaseResponse | ||
| import kotlinx.serialization.MissingFieldException | ||
| import kotlinx.serialization.json.Json | ||
| import org.junit.Assert.assertEquals | ||
| import org.junit.Assert.assertThrows | ||
| import org.junit.Test | ||
|
|
||
| /** | ||
|
|
@@ -80,4 +82,42 @@ class DailyQuestionAnswerResponseContractTest { | |
|
|
||
| assertEquals(true, failed) | ||
| } | ||
|
|
||
| @Test | ||
| fun `isDraft 가 빠지면 정식 답변으로 접히지 않고 실패한다`() { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [normal] 이 가드가 프로덕션 디코더와 다른 설정으로 돈다. 앱이 실제로 쓰는 Json 은 즉 이 PR 이 실제로 닫는 구멍은 본문이 든 «키 누락» 보다 «명시적 null» 쪽이 크다. 그런데 추가된 두 건은 키 누락만 고정한다. 프로덕션과 같은 설정의 Json 으로 (지금 실서버에서 |
||
| // false 로 접히면 임시저장이 정식 답변으로 보인다 — 계약 변경이 «정상적인 빈 값» 으로 | ||
| // 둔갑하는 자리라 기본값을 두지 않는다 (#789). | ||
| val body = | ||
| """ | ||
| { | ||
| "status": 200, "code": 200, "message": "성공", | ||
| "data": { "userDailyQuestionId": 19, "content": "응답 계약 실측" } | ||
| } | ||
| """.trimIndent() | ||
|
|
||
| assertThrows(MissingFieldException::class.java) { | ||
| json.decodeFromString( | ||
| BaseResponse.serializer(DailyQuestionAnswerResponseDto.serializer()), | ||
| body, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
| fun `content 가 빠져도 빈 문자열로 성공하지 않는다`() { | ||
| val body = | ||
| """ | ||
| { | ||
| "status": 200, "code": 200, "message": "성공", | ||
| "data": { "userDailyQuestionId": 19, "isDraft": false } | ||
| } | ||
| """.trimIndent() | ||
|
|
||
| assertThrows(MissingFieldException::class.java) { | ||
| json.decodeFromString( | ||
| BaseResponse.serializer(DailyQuestionAnswerResponseDto.serializer()), | ||
| body, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[normal] 이 주석의 시나리오가 이 DTO 에서는 일어나지 않는다.
DailyQuestionAnswerResponseDto의content·isDraft를 읽는 코드가 없다 — 생성·수정 둘 다DailyQuestionRepositoryImpl에서.requireData().userDailyQuestionId하나만 꺼내고 나머지는 버린다.isDraft가false로 접혀도 «임시저장이 정식 답변으로 보이는» 경로 자체가 없다. 그 오표시는 목록을 그리는DailyQuestionListItemDto.isDraft(이 파일 위쪽)에서 일어나고, 거기는 이미 non-null +@JsonNames("draft")로 막혀 있다.반대로 이 자리에서 기본값을 떼면 실제로 바뀌는 것이 하나 있는데 본문에 없다 — 서버가 저장을 마친 뒤에 앱만 실패로 표시한다. 두 키 중 하나라도 빠지면
MissingFieldException→runCatchingCancellable→SubmitState.Failed(저장 실패)다. 게다가 정식 저장(isDraft=false)의 첫 시도가 이렇게 실패하면draftId가 채워지지 않아 재시도도POST로 나가고, BE 는isAnswered && !isDraft를 보고DAILY_QUESTION_ALREADY_ANSWERED로 막는다(release8bfe43d1DailyQuestionService#createAnswer). 그 답변은 앱에서 영영 «저장 실패» 로 남는다.요청은 둘 중 하나다.
ignoreUnknownKeys = true라 응답에 남아 있어도 무해하고, fix(mindrecord): 데일리질문 생성·수정 응답의 userDailyQuestionId 를 BaseResponse<Unit> 으로 버림 #573 이 살리려던 값은userDailyQuestionId하나였다. 계약 회귀 감시는 실제 소비처인 목록·오늘 질문 DTO 가 이미 맡고 있다.