diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt b/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt index 3bb3cad40c..97d075dba4 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt +++ b/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordFlowAndroidTest.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.test.waitUntilAtLeastOneExists import androidx.lifecycle.SavedStateHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import com.afternote.afternote_fe.test.FailureArtifactRule +import com.afternote.afternote_fe.test.FakeDailyQuestionRepository import com.afternote.afternote_fe.test.FakeDiaryRepository import com.afternote.afternote_fe.test.FakeUserRepository import com.afternote.core.domain.repository.PhotoUploadRepository @@ -22,6 +23,7 @@ import com.afternote.feature.mindrecord.domain.model.TodayMood import com.afternote.feature.mindrecord.presentation.screen.sender.DiaryWriteScreen import com.afternote.feature.mindrecord.presentation.viewmodel.DailyQuestionWriteViewModel import com.afternote.feature.mindrecord.presentation.viewmodel.DiaryWriteViewModel +import com.afternote.feature.mindrecord.presentation.viewmodel.MindRecordDraftLoader import com.afternote.feature.mindrecord.presentation.viewmodel.SubmitState import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -102,15 +104,14 @@ class MindRecordFlowAndroidTest { @Test fun dailyQuestion_failureThenRetry_keepsAnswerAndAvoidsDuplicateSuccess() { - val repository = - com.afternote.afternote_fe.test - .FakeDailyQuestionRepository() + val repository = FakeDailyQuestionRepository() repository.createResults.addLast(Result.failure(IllegalStateException("offline"))) - repository.createResults.addLast(Result.success(Unit)) + repository.createResults.addLast(Result.success(1L)) val viewModel = DailyQuestionWriteViewModel( repository = repository, photoUploadRepository = PhotoUploadRepository { _, _ -> Result.success("https://cdn.test/question.jpg") }, + draftLoader = MindRecordDraftLoader(FakeDiaryRepository(), repository), ) composeRule.setContent { AfternoteTheme {} } composeRule.waitUntil(timeoutMillis = 5_000) { !viewModel.uiState.value.isQuestionLoading } @@ -150,5 +151,6 @@ class MindRecordFlowAndroidTest { repository = repository, photoUploadRepository = PhotoUploadRepository { _, _ -> Result.success("https://cdn.test/image.jpg") }, userRepository = FakeUserRepository(), + draftLoader = MindRecordDraftLoader(repository, FakeDailyQuestionRepository()), ) } diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt b/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt index 716a82fbbc..777f6c7457 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt +++ b/app/src/androidTest/java/com/afternote/afternote_fe/MindRecordLifecycleAndroidTest.kt @@ -44,6 +44,7 @@ import com.afternote.feature.mindrecord.presentation.viewmodel.DiaryListUiState import com.afternote.feature.mindrecord.presentation.viewmodel.DiaryListViewModel import com.afternote.feature.mindrecord.presentation.viewmodel.DraftListUiState import com.afternote.feature.mindrecord.presentation.viewmodel.DraftListViewModel +import com.afternote.feature.mindrecord.presentation.viewmodel.MindRecordDraftLoader import com.afternote.feature.mindrecord.presentation.viewmodel.ReceiverMindRecordFilter import com.afternote.feature.mindrecord.presentation.viewmodel.ReceiverMindRecordUiState import com.afternote.feature.mindrecord.presentation.viewmodel.ReceiverMindRecordViewModel @@ -149,7 +150,12 @@ class MindRecordLifecycleAndroidTest { ), ), ) - val viewModel = DraftListViewModel(diaryRepository, dailyQuestionRepository) + val viewModel = + DraftListViewModel( + loader = MindRecordDraftLoader(diaryRepository, dailyQuestionRepository), + diaryRepository = diaryRepository, + dailyQuestionRepository = dailyQuestionRepository, + ) composeRule.setContent { AfternoteTheme { @@ -174,7 +180,7 @@ class MindRecordLifecycleAndroidTest { assertTrue(diaryRepository.deleteCalls.isEmpty()) composeRule.waitUntil(timeoutMillis = TIMEOUT) { - (viewModel.uiState.value as? DraftListUiState.Success)?.deleteCompleted == false + (viewModel.uiState.value as? DraftListUiState.Success)?.deleteOutcome == null } composeRule.onNodeWithText("선택").performClick() composeRule.onNodeWithText("전체 삭제").performClick() @@ -216,12 +222,14 @@ class MindRecordLifecycleAndroidTest { diaryId = 301L, day = wednesday.dayOfMonth, isDiary = true, + countsAsRecord = true, emotion = TodayMood.HAPPY, ), WeeklyReportDay( diaryId = 302L, day = wednesday.dayOfMonth, isDiary = false, + countsAsRecord = true, emotion = null, ), ), @@ -240,7 +248,7 @@ class MindRecordLifecycleAndroidTest { val success = viewModel.uiState.value as WeeklyReportUiState.Success assertEquals(7, success.weekDays.size) assertEquals(DayOfWeek.entries.toList(), success.weekDays.map { it.dayOfWeek }) - assertEquals(DayContent.EmojiWithDot("😊"), success.weekDays[2].content) + assertEquals(DayContent.EmojiOnly("😊"), success.weekDays[2].content) assertEquals(DayBackground.Green, success.weekDays[2].background) assertEquals(2, success.recordedDays) assertEquals(emptyList(), success.emotionKeywords) @@ -379,12 +387,12 @@ private class PrivateDailyQuestionRepository( override suspend fun getToday(): Result = error("Unexpected today question load") - override suspend fun create(payload: DailyQuestionCreatePayload): Result = error("Unexpected question create") + override suspend fun create(payload: DailyQuestionCreatePayload): Result = error("Unexpected question create") override suspend fun update( id: Long, payload: DailyQuestionUpdatePayload, - ): Result = error("Unexpected question update") + ): Result = error("Unexpected question update") override suspend fun delete(id: Long): Result { deleteCalls += id diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/TimeLetterMindRecordCompletionAndroidTest.kt b/app/src/androidTest/java/com/afternote/afternote_fe/TimeLetterMindRecordCompletionAndroidTest.kt index d48fda550a..3fde0c2765 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/TimeLetterMindRecordCompletionAndroidTest.kt +++ b/app/src/androidTest/java/com/afternote/afternote_fe/TimeLetterMindRecordCompletionAndroidTest.kt @@ -61,7 +61,9 @@ import com.afternote.feature.mindrecord.presentation.viewmodel.DailyQuestionList import com.afternote.feature.mindrecord.presentation.viewmodel.DailyQuestionWriteViewModel import com.afternote.feature.mindrecord.presentation.viewmodel.DiaryWriteViewModel import com.afternote.feature.mindrecord.presentation.viewmodel.DraftListViewModel +import com.afternote.feature.mindrecord.presentation.viewmodel.MemorySpaceUiState import com.afternote.feature.mindrecord.presentation.viewmodel.MemorySpaceViewModel +import com.afternote.feature.mindrecord.presentation.viewmodel.MindRecordDraftLoader import com.afternote.feature.mindrecord.presentation.viewmodel.SubmitState import com.afternote.feature.mindrecord.presentation.viewmodel.WeeklyReportUiState import com.afternote.feature.mindrecord.presentation.viewmodel.WeeklyReportViewModel @@ -414,6 +416,7 @@ class TimeLetterMindRecordCompletionAndroidTest { DailyQuestionWriteViewModel( repository = repository, photoUploadRepository = CompletionPhotoUploadRepository, + draftLoader = MindRecordDraftLoader(CompletionDiaryRepository(mutableListOf()), repository), ) } composeRule.waitUntil(timeoutMillis = TIMEOUT) { @@ -458,10 +461,12 @@ class TimeLetterMindRecordCompletionAndroidTest { ), ), ) + val draftDailyQuestionRepository = CompletionDailyQuestionRepository() val draftListViewModel = DraftListViewModel( + loader = MindRecordDraftLoader(repository, draftDailyQuestionRepository), diaryRepository = repository, - dailyQuestionRepository = CompletionDailyQuestionRepository(), + dailyQuestionRepository = draftDailyQuestionRepository, ) var routedArguments: Pair? = null var writeViewModel by mutableStateOf(null) @@ -487,6 +492,8 @@ class TimeLetterMindRecordCompletionAndroidTest { repository = repository, photoUploadRepository = CompletionPhotoUploadRepository, userRepository = FakeUserRepository(), + draftLoader = + MindRecordDraftLoader(repository, draftDailyQuestionRepository), ) }, ) @@ -534,7 +541,24 @@ class TimeLetterMindRecordCompletionAndroidTest { @Test fun memorySpace_supportedSuccess_opensAndClosesDetailThenNavigatesBack() { - val viewModel = MemorySpaceViewModel() + val memoryDate = LocalDate.now() + val diaryRepository = + CompletionDiaryRepository( + drafts = + mutableListOf( + Diary( + diaryId = 501L, + title = "추억이 된 하루", + content = "이 순간은 나에게 특별한 의미가 있었습니다.", + date = memoryDate.toString(), + createdAt = memoryDate.toString(), + todayMood = TodayMood.HAPPY, + imageUrl = "https://afternote.test/memory.jpg", + isDraft = false, + ), + ), + ) + val viewModel = MemorySpaceViewModel(diaryRepository, CompletionDailyQuestionRepository()) var backCalls = 0 composeRule.setContent { @@ -547,11 +571,15 @@ class TimeLetterMindRecordCompletionAndroidTest { } composeRule.onNodeWithText("MEMORY SPACE").assertIsDisplayed() - composeRule.onNodeWithContentDescription("기억 1").performClick() + composeRule.waitUntil(timeoutMillis = TIMEOUT) { + viewModel.uiState.value is MemorySpaceUiState.Success + } + composeRule.onNodeWithContentDescription("추억이 된 하루").performClick() composeRule .onNodeWithText("이 순간은 나에게 특별한 의미가 있었습니다.", substring = true) .assertIsDisplayed() - composeRule.onNodeWithText("#평온").assertIsDisplayed() + // 태그는 사용자가 고른 오늘의 기분 이모지다 — 종전 더미의 `#평온` 은 출처가 없었다 (#559). + composeRule.onNodeWithText("#😊").assertIsDisplayed() composeRule.onNodeWithContentDescription("닫기").performClick() composeRule .onNodeWithText("이 순간은 나에게 특별한 의미가 있었습니다.", substring = true) @@ -763,11 +791,12 @@ private class CompletionDailyQuestionRepository( return Result.success(today) } - override suspend fun create(payload: DailyQuestionCreatePayload): Result { + override suspend fun create(payload: DailyQuestionCreatePayload): Result { createCalls += payload + val createdId = 800L + createCalls.size storedAnswers += DailyQuestion( - dailyQuestionId = 800L + createCalls.size, + dailyQuestionId = createdId, title = today.content, content = payload.content, createdAt = "2026-08-22", @@ -775,13 +804,13 @@ private class CompletionDailyQuestionRepository( isDraft = payload.isDraft, ) today = today.copy(isAnswered = !payload.isDraft, isDraft = payload.isDraft) - return Result.success(Unit) + return Result.success(createdId) } override suspend fun update( id: Long, payload: DailyQuestionUpdatePayload, - ): Result { + ): Result { updateCalls += id to payload storedAnswers.replaceAll { answer -> if (answer.dailyQuestionId == id) { @@ -794,7 +823,7 @@ private class CompletionDailyQuestionRepository( answer } } - return Result.success(Unit) + return Result.success(id) } override suspend fun delete(id: Long): Result { @@ -931,6 +960,7 @@ private fun completeWeeklyReport(monday: LocalDate): WeeklyReport = diaryId = 91L, day = monday.dayOfMonth, isDiary = true, + countsAsRecord = true, emotion = TodayMood.HAPPY, ), ), diff --git a/app/src/androidTest/java/com/afternote/afternote_fe/test/TestFakes.kt b/app/src/androidTest/java/com/afternote/afternote_fe/test/TestFakes.kt index 98cc07325c..ba27408d8d 100644 --- a/app/src/androidTest/java/com/afternote/afternote_fe/test/TestFakes.kt +++ b/app/src/androidTest/java/com/afternote/afternote_fe/test/TestFakes.kt @@ -262,7 +262,7 @@ class FakeDailyQuestionRepository( Result.success(TodayDailyQuestion(1L, 1, "오늘의 질문", false)), ) : DailyQuestionRepository { val createdPayloads = mutableListOf() - val createResults = ArrayDeque>() + val createResults = ArrayDeque>() override suspend fun getList( date: String?, @@ -271,17 +271,22 @@ class FakeDailyQuestionRepository( override suspend fun getToday(): Result = todayResult - override suspend fun create(payload: DailyQuestionCreatePayload): Result { + override suspend fun create(payload: DailyQuestionCreatePayload): Result { createdPayloads += payload - return createResults.removeFirstOrNull() ?: Result.success(Unit) + return createResults.removeFirstOrNull() ?: Result.success(CREATED_DAILY_QUESTION_ID) } override suspend fun update( id: Long, payload: DailyQuestionUpdatePayload, - ): Result = Result.success(Unit) + ): Result = Result.success(id) override suspend fun delete(id: Long): Result = Result.success(Unit) + + private companion object { + /** 서버가 돌려주는 `userDailyQuestionId` 자리 (#573). 값 자체에 의미는 없다. */ + const val CREATED_DAILY_QUESTION_ID = 1L + } } class FakeErrorReporter : ErrorReporter {