Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
e2e4141
Merge branch 'feat/725-emotion-analysis-status' into fix/562-weekly-g…
Sadturtleman Aug 24, 2026
e0a0199
fix(home): 주간 요약 그리드에 실제 기록 수를 연결한다
Sadturtleman Aug 24, 2026
818977c
style(mindrecord): 머지에서 겹친 고아 KDoc 을 정리한다
Sadturtleman Aug 24, 2026
0b930df
Merge feat/207-home-summary-usecase into fix/562-weekly-grid-real-data
Sadturtleman Aug 24, 2026
7d6038f
Merge fix/736-request-count into fix/562-weekly-grid-real-data
Sadturtleman Aug 24, 2026
c32d9ad
Merge remote-tracking branch 'origin/fix/736-request-count' into HEAD
OnlyReadOneBook Aug 25, 2026
eea83e5
Merge fix/736-request-count into fix/562-weekly-grid-real-data
Sadturtleman Aug 25, 2026
50d6f53
Merge remote-tracking branch 'origin/fix/562-weekly-grid-real-data' i…
Sadturtleman Aug 25, 2026
5567b38
merge: base(fix/736-request-count) 를 물린다 — 픽스처 도입 import 와 주간 실데이터 im…
Sadturtleman Aug 26, 2026
62c8cde
fix(home): 느린 주간 수 조회가 홈 전체를 붙잡지 못하게 한다
Sadturtleman Aug 26, 2026
e1c3ab9
Merge remote-tracking branch 'origin/fix/736-request-count' into fix/…
OnlyReadOneBook Aug 26, 2026
8a05801
Merge fix/736-request-count into fix/562-weekly-grid-real-data for PR…
OnlyReadOneBook Aug 26, 2026
34104af
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 26, 2026
308ff0d
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 26, 2026
d562e10
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 26, 2026
94d6911
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 26, 2026
023c6f9
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 27, 2026
34516a2
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 27, 2026
8d15687
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 27, 2026
78183ab
Merge fix/736-request-count into fix/562-weekly-grid-real-data for PR…
OnlyReadOneBook Aug 27, 2026
a8ace84
Merge fix/736-request-count into fix/562-weekly-grid-real-data for PR…
OnlyReadOneBook Aug 27, 2026
2166d2e
Merge remote-tracking branch 'origin/develop' into fix/562-weekly-gri…
Sadturtleman Aug 27, 2026
b277c93
test(home): 실제 홈 경로가 주간 기록 수를 그리드까지 전달하는지 계측한다
Sadturtleman Aug 27, 2026
4486365
Merge remote-tracking branch 'origin/fix/736-request-count' into fix/…
Sadturtleman Aug 27, 2026
88ee83d
fix(test): 주간 수 계측을 좁은 화면에서도 통과하게 한다
Sadturtleman Aug 27, 2026
8059da3
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 27, 2026
eed6e99
Merge commit 'b5006fa2316e93d40037a03cbc1c6da38b4d1c32' into HEAD
OnlyReadOneBook Aug 27, 2026
be242a6
Merge commit '46e3ec40bf17c9c012c807d62307ef12161bb366' into HEAD
OnlyReadOneBook Aug 27, 2026
99d6875
test(android): isolate weekly report after login (#1288)
OnlyReadOneBook Aug 27, 2026
c3a5737
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 27, 2026
d7c2fd5
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 27, 2026
af2955d
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 27, 2026
edbd6b2
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 27, 2026
acbefd6
Merge remote-tracking branch 'origin/fix/736-request-count' into fix/…
Sadturtleman Aug 28, 2026
e3f2100
test(app): 계측 테스트의 주간 리포트 조회를 fake 로 격리한다
Sadturtleman Aug 28, 2026
69f5e1f
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 28, 2026
c606293
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 28, 2026
19d97a4
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 28, 2026
df6a625
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 28, 2026
b7719f9
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 28, 2026
51a012f
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 28, 2026
f8e34a9
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 28, 2026
470a019
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
OnlyReadOneBook Aug 28, 2026
c9d2ffe
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 28, 2026
b9c13d6
Merge branch 'develop' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
4d190f9
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 29, 2026
3d6345b
Merge branch 'develop' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
cf94ed9
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
2abd28a
test(app): 주간 리포트 fake 를 정본 strict 판으로 되돌린다
Sadturtleman Aug 29, 2026
69ac3ff
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
ed46431
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
125a340
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
95c0316
Merge branch 'fix/562-weekly-grid-real-data' into test/1288-weekly-re…
OnlyReadOneBook Aug 29, 2026
d4b2eaf
test(android): base 추종 병합 잔재 정리 — 주간 리포트 주입·큐잉 중복 제거 (#1288)
OnlyReadOneBook Aug 29, 2026
5337439
Merge pull request #1316 from Afternote/test/1288-weekly-report-fake
OnlyReadOneBook Aug 29, 2026
4cf0cbd
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
Sadturtleman Aug 29, 2026
364cef5
Merge branch 'fix/736-request-count' into fix/562-weekly-grid-real-data
Sadturtleman Aug 30, 2026
ec1cc8a
fix(test): 주간 리포트 fake 에 fallback 을 열어 계측이 홈에서 죽지 않게 한다
Sadturtleman Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.afternote.afternote_fe.navigation.rememberHomeTabActions
import com.afternote.afternote_fe.navigation.rememberReceiverNavActions
import com.afternote.afternote_fe.test.FailureArtifactRule
import com.afternote.afternote_fe.test.FakeErrorReporter
import com.afternote.afternote_fe.test.emptyWeeklyReport
import com.afternote.core.common.reporting.ErrorReporter
import com.afternote.core.domain.error.CoreAuthFailure
import com.afternote.core.domain.repository.UserProfileRepository
Expand All @@ -49,7 +50,9 @@ import com.afternote.feature.afternote.presentation.receiver.detail.ReceivedAfte
import com.afternote.feature.afternote.presentation.receiver.detail.ReceivedAfternoteDetailViewModel
import com.afternote.feature.home.presentation.HomeTabActions
import com.afternote.feature.mindrecord.domain.model.ReceiverMindRecords
import com.afternote.feature.mindrecord.domain.repository.WeeklyReportRepository
import com.afternote.feature.mindrecord.domain.testing.FakeMindRecordReceiverRepository
import com.afternote.feature.mindrecord.domain.testing.FakeWeeklyReportRepository
import com.afternote.feature.mindrecord.presentation.model.MindRecordCategory
import com.afternote.feature.receiver.domain.error.ReceiverFailure
import com.afternote.feature.receiver.domain.error.ReceiverRejectionReason
Expand Down Expand Up @@ -115,6 +118,9 @@ class AppAndReceiverCompletionAndroidTest {
@Inject
lateinit var authRepository: AuthRepository

@Inject
lateinit var weeklyReportRepository: WeeklyReportRepository

@Inject
lateinit var errorReporter: ErrorReporter

Expand All @@ -123,6 +129,7 @@ class AppAndReceiverCompletionAndroidTest {

private val fakeAuth get() = authRepository as FakeAuthRepository
private val fakeErrorReporter get() = errorReporter as FakeErrorReporter
private val fakeWeeklyReport get() = weeklyReportRepository as FakeWeeklyReportRepository

@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
Expand All @@ -141,6 +148,12 @@ class AppAndReceiverCompletionAndroidTest {
@Before
fun inject() {
hiltRule.inject()
// 홈이 진입 시 주간 기록 수를 부른다 (#562). 정본 fake 는 큐가 비면 터뜨리므로,
// 주간 수에 관심이 없는 이 테스트도 기대하는 응답을 명시적으로 넣는다 — 조용히 접으면
// 요청 횟수가 어긋난 것을 놓친다.
(weeklyReportRepository as FakeWeeklyReportRepository).results.addLast(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[minor] 재승인을 막지 않습니다 — 다만 큐에 정확히 한 건이라, 홈을 두 번 부르는 테스트가 이 클래스에 들어오는 순간 무관한 이유로 터집니다.

앱 셸은 홈에 복귀할 때마다 요약을 다시 부릅니다(AppNavigation 의 LifecycleEventEffect(ON_RESUME) → refreshOnReturn(), 첫 resume 만 스킵). 지금 이 클래스의 홈 경유 테스트는 전부 «한 번 들어가고 끝» 이라 1건으로 충분하고 CI 도 green 입니다만, 홈 → 다른 화면 → 홈 시나리오를 하나 더하면 두 번째 호출에서 results 가 비어 requireNotNull 이 터집니다. 그때 실패 메시지는 «주간 리포트 응답이 큐에 없다» 라, 새 테스트를 쓰는 사람은 자기 시나리오와 무관한 곳을 보게 됩니다.

TestFakes 에 헬퍼를 하나 더 두면 그 자리에서 바로 읽힙니다.

/** 홈 복귀마다 요약을 다시 부른다 — 진입 횟수만큼 넣는다. */
fun FakeWeeklyReportRepository.enqueueEmptyWeeklyReports(times: Int = 1) {
    repeat(times) { results.addLast(Result.success(emptyWeeklyReport())) }
}

«왜 하필 1건인가» 를 주석 한 줄로 남겨 주는 것만으로도 충분합니다. 큐잉을 택한 판단(조용히 접으면 요청 횟수 어긋남을 놓친다) 자체는 맞습니다.

Result.success(emptyWeeklyReport()),
)
}

@Test
Expand Down Expand Up @@ -207,6 +220,8 @@ class AppAndReceiverCompletionAndroidTest {
fakeAuth.attemptedEmailLogins,
)
assertEquals(1, fakeAuth.saveSessionCalls)
assertEquals(0, fakeAuth.rotateTokenCalls)
assertEquals(1, fakeWeeklyReport.requestedDates.size)
assertTrue(fakeErrorReporter.failures.isEmpty())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ import androidx.compose.ui.test.waitUntilAtLeastOneExists
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.afternote.afternote_fe.test.FailureArtifactRule
import com.afternote.afternote_fe.test.emptyWeeklyReport
import com.afternote.core.domain.error.CoreAuthFailure
import com.afternote.core.domain.repository.UserRepository
import com.afternote.core.domain.repository.auth.AuthRepository
import com.afternote.core.domain.testing.FakeAuthRepository
import com.afternote.core.domain.testing.FakeUserRepository
import com.afternote.core.model.Session
import com.afternote.feature.mindrecord.domain.repository.WeeklyReportRepository
import com.afternote.feature.mindrecord.domain.testing.FakeWeeklyReportRepository
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Assert.assertEquals
Expand All @@ -41,11 +44,15 @@ class AppOnboardingCanaryTest {
@Inject
lateinit var authRepository: AuthRepository

@Inject
lateinit var weeklyReportRepository: WeeklyReportRepository

@Inject
lateinit var userRepository: UserRepository

private val fakeAuth get() = authRepository as FakeAuthRepository
private val fakeUser get() = userRepository as FakeUserRepository
private val fakeWeeklyReport get() = weeklyReportRepository as FakeWeeklyReportRepository

@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
Expand All @@ -64,6 +71,12 @@ class AppOnboardingCanaryTest {
@Before
fun inject() {
hiltRule.inject()
// 홈이 진입 시 주간 기록 수를 부른다 (#562). 정본 fake 는 큐가 비면 터뜨리므로,
// 주간 수에 관심이 없는 이 테스트도 기대하는 응답을 명시적으로 넣는다 — 조용히 접으면
// 요청 횟수가 어긋난 것을 놓친다.
(weeklyReportRepository as FakeWeeklyReportRepository).results.addLast(
Result.success(emptyWeeklyReport()),
)
}

@Test
Expand Down Expand Up @@ -123,6 +136,9 @@ class AppOnboardingCanaryTest {
)
assertEquals(2, fakeAuth.attemptedEmailLogins.size)
assertEquals(1, fakeAuth.saveSessionCalls)
assertEquals(0, fakeAuth.rotateTokenCalls)
assertEquals(1, fakeWeeklyReport.requestedDates.size)
// `logActivityCalls` 단언은 걷는다 — #1413 이 활동 ping 자체를 제거해 develop 에 그 API 가 없다.
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package com.afternote.afternote_fe

import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.hasScrollToNodeAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performScrollToNode
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.afternote.afternote_fe.test.FailureArtifactRule
import com.afternote.afternote_fe.test.HiltTestActivity
import com.afternote.afternote_fe.test.appTestUserRepository
import com.afternote.core.common.reporting.ErrorReporter
import com.afternote.core.domain.testing.FakeUserProfileRepository
import com.afternote.core.ui.theme.AfternoteTheme
import com.afternote.feature.home.presentation.HomeTabScreen
import com.afternote.feature.home.presentation.HomeTabUiState
import com.afternote.feature.home.presentation.HomeTabViewModel
import com.afternote.feature.home.presentation.usecase.GetHomeSummaryUseCase
import com.afternote.feature.mindrecord.domain.model.WeeklyReport
import com.afternote.feature.mindrecord.domain.repository.WeeklyReportRepository
import com.afternote.feature.mindrecord.domain.testing.FakeDailyQuestionRepository
import com.afternote.feature.mindrecord.domain.testing.FakeDiaryRepository
import com.afternote.feature.mindrecord.domain.usecase.GetWeeklyRecordCountUseCase
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* 실제 홈 경로가 주간 기록 수를 그리드까지 **전달하는지** (#562).
*
* 원래 결함은 그리드가 값을 못 그린 것이 아니라 **호출부가 값을 넘기지 않은 것**이었다.
* 컴포넌트에 숫자를 직접 넣는 테스트는 그 자리를 지나지 않으므로, ViewModel 이 만든 상태를
* 화면에 물려 숫자가 실제로 보이는 데까지를 여기서 고정한다.
*/
@HiltAndroidTest
@RunWith(AndroidJUnit4::class)
class HomeWeeklyCountAndroidTest {
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)

// 홈은 MEMORIES 섹션에서 hiltViewModel() 을 부른다. 좁은 화면(Pixel 2 API 30)에서 주간
// 카드까지 스크롤하면 그 항목도 composition 되므로 Hilt 가 붙은 Activity 여야 한다 —
// 맨 ComponentActivity 로는 "does not implement GeneratedComponent" 로 죽는다.
// MainActivity 는 이미 콘텐츠를 세팅해 setContent 가 거부되므로 빈 테스트 Activity 를 쓴다.
@get:Rule(order = 1)
val composeRule = createAndroidComposeRule<HiltTestActivity>()

@get:Rule(order = 2)
val failureArtifactRule =
FailureArtifactRule {
composeRule.onRoot().captureToImage().asAndroidBitmap()
}

@Before
fun inject() {
hiltRule.inject()
}

@Test
fun homeScreen_showsWeeklyRecordCountFromViewModel() {
val viewModel = homeViewModel(dailyQuestionAmount = 3, diaryAmount = 4)

composeRule.setContent {
AfternoteTheme {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
HomeTabScreen(uiState = uiState, todayDateText = "2026.04.10")
}
}

// 3 + 4 = 7. 이 숫자가 보이려면 UseCase → UiState → HomeTabScreen → WeeklySummaryGrid
// 배선이 전부 살아 있어야 한다.
awaitSuccess(viewModel)
composeRule.scrollToWeeklyCard()
composeRule.onNodeWithText(EXPECTED_COUNT).assertIsDisplayed()
}

@Test
fun homeScreen_showsPlaceholderWhenWeeklyCountUnavailable() {
// 조회 실패를 0 으로 접으면 «기록이 없음» 을 확정한다 — 미상은 대시로 남아야 한다 (#562).
val viewModel = homeViewModel(weeklyFailure = IllegalStateException("주간 조회 실패"))

composeRule.setContent {
AfternoteTheme {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
HomeTabScreen(uiState = uiState, todayDateText = "2026.04.10")
}
}

awaitSuccess(viewModel)
composeRule.scrollToWeeklyCard()
composeRule.onNodeWithText(UNKNOWN_COUNT).assertIsDisplayed()
}

/**
* 상태가 Success 가 될 때까지 기다린다. 화면 단언보다 먼저 두는 이유는, 조회가 끝나기 전에
* 스크롤하면 주간 카드가 아직 목록에 없어 스크롤 자체가 실패하기 때문이다.
*/
private fun awaitSuccess(viewModel: HomeTabViewModel) {
composeRule.waitUntil(timeoutMillis = TIMEOUT) {
viewModel.uiState.value is HomeTabUiState.Success
}
}

/**
* 주간 카드까지 스크롤한다.
*
* `WeeklySummaryGrid` 는 `LazyColumn` 의 뒤쪽 item 이라 좁은 화면(Pixel 2 API 30)에서는
* 아직 composition 되지 않는다. 노드가 «생기기만» 기다리면 스스로 노출되지 않아 timeout
* 이다 — 내 에뮬레이터(화면이 더 큼)에서만 통과했던 이유다 (#562 리뷰).
*/
private fun ComposeContentTestRule.scrollToWeeklyCard() {
onNode(hasScrollToNodeAction()).performScrollToNode(hasText(WEEKLY_CARD_LABEL))
waitForIdle()
}

private fun homeViewModel(
dailyQuestionAmount: Int = 0,
diaryAmount: Int = 0,
weeklyFailure: Throwable? = null,
): HomeTabViewModel =
HomeTabViewModel(
getHomeSummary =
GetHomeSummaryUseCase(
userRepository = appTestUserRepository(),
diaryRepository = FakeDiaryRepository(),
dailyQuestionRepository = FakeDailyQuestionRepository(),
getWeeklyRecordCount =
GetWeeklyRecordCountUseCase(
FakeWeeklyReportRepository(dailyQuestionAmount, diaryAmount, weeklyFailure),
),
),
userProfileRepository = FakeUserProfileRepository(),
errorReporter = SilentErrorReporter,
)

private companion object {
const val TIMEOUT = 5_000L
const val EXPECTED_COUNT = "7"
const val WEEKLY_CARD_LABEL = "THIS WEEK"
const val UNKNOWN_COUNT = "–"
}
}

/** 이 테스트는 보고 여부를 보지 않는다 — 조회 실패 경로가 리포터를 부르므로 자리만 채운다. */
private object SilentErrorReporter : ErrorReporter {
override fun writeFailure(
throwable: Throwable,
attributes: Map<String, String>,
) = Unit
}

private class FakeWeeklyReportRepository(
private val dailyQuestionAmount: Int,
private val diaryAmount: Int,
private val failure: Throwable?,
) : WeeklyReportRepository {
override suspend fun getWeeklyReport(date: String): Result<WeeklyReport> {
failure?.let { return Result.failure(it) }
return Result.success(
WeeklyReport(
dailyQuestionAmount = dailyQuestionAmount,
diaryAmount = diaryAmount,
summaryText = "",
week = emptyList(),
dailyQuestions = emptyList(),
emotions = emptyList(),
emotionAnalysis = null,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.afternote.core.model.user.User
import com.afternote.core.model.user.UserConnectedAccount
import com.afternote.core.model.user.UserPushSetting
import com.afternote.feature.afternote.domain.AfternoteType
import com.afternote.feature.mindrecord.domain.model.EmotionAnalysis
import com.afternote.feature.mindrecord.domain.model.WeeklyReport

fun afternoteEditorSavedStateHandle(
initialType: AfternoteType,
Expand Down Expand Up @@ -88,3 +90,22 @@ private val DEFAULT_TEST_PUSH_SETTING = UserPushSetting(true, true, true)

private fun defaultConnectedAccounts(email: String): UserConnectedAccount =
UserConnectedAccount(true, false, false, false, false, email, null, null, null, null)

/**
* 홈이 진입 시 부르는 주간 리포트의 «빈 응답» (#562).
*
* 정본 [com.afternote.feature.mindrecord.domain.testing.FakeWeeklyReportRepository] 는 큐가 비면
* 터뜨린다 — 조용히 빈 리포트를 돌려주면 요청 횟수가 어긋난 것을 놓치기 때문이다. 그래서 홈에
* 닿는 계측 테스트는 주간 수에 관심이 없더라도 **자기가 기대하는 응답을 명시적으로 큐에 넣는다.**
*/
fun emptyWeeklyReport(): WeeklyReport =
WeeklyReport(
dailyQuestionAmount = 0,
diaryAmount = 0,
summaryText = "",
week = emptyList(),
dailyQuestions = emptyList(),
emotions = emptyList(),
// 분석 상태는 이 테스트들의 관심사가 아니다 — 완료로 고정해 폴링이 끼어들지 않게 한다.
emotionAnalysis = EmotionAnalysis(total = 0, succeeded = 0, pending = 0, failed = 0),
)
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,28 @@ object TestMindRecordRepositoryModule {
fun provideMindRecordReceiverRepository(impl: MindRecordReceiverRepositoryImpl): MindRecordReceiverRepository = impl

/**
* 실제 구현은 네트워크를 탄다 — 주간리포트 탭이 합성되기만 해도 계측이 실서버에 붙는다.
* 요청한 주차를 기록하는 fake 로 바꿔, 「열지 않은 탭은 부르지 않는다」(#736) 를 **횟수로**
* 단언할 수 있게 한다.
* 주간 리포트도 fake 로 격리한다.
*
* 실제 구현은 네트워크를 탄다 — 홈이 진입 시 주간 기록 수를 부르고(#562) 주간리포트 탭도
* 합성되기만 하면 이 경로를 지나므로, 실제 구현으로 두면 계측이 실서버에 붙는다. CI stub
* 응답이 TokenAuthenticator 를 타고 FakeAuthRepository 로 흘러들어 인증 테스트가 깨졌다.
*
* 정본 fixture 를 쓴다 — 시나리오마다 클래스를 새로 만들면 계약이 바뀔 때 고칠 곳이 갈라진다
* (#936 · #1022 · #1030 의 androidTest 컴파일 파손 원인). 요청한 주차가 `requestedDates` 에
* 남아 「열지 않은 탭은 부르지 않는다」(#736) 를 횟수로 단언할 수 있다.
*
* **`fallback` 을 준다 — 큐가 비어도 터뜨리지 않는다.** 정본 fixture 의 기본은 「큐가 비면 실패」
* 이고 화면 단위 테스트는 그게 맞다. 그런데 앱 전체를 띄우는 계측은 **홈을 지나가기만 해도** 이
* 저장소를 부르고, 그 횟수는 화면 전환·`ON_RESUME` 에 따라 달라져 `@Before` 에서 미리 셀 수 없다.
* 큐만 두면 두 번째 호출에서 터져 **홈이 못 뜨고, 실패는 「인사말이 안 보인다」 같은 엉뚱한
* 자리에서 나타난다** — 실제로 `mode: full` 계측에서 그렇게 나왔다 (#562 · #1288).
*
* **세는 단언은 그대로다.** `fallback` 을 줘도 `requestedDates` 는 계속 쌓이므로 「열지 않은 탭은
* 부르지 않는다」(#736) 를 횟수로 단언할 수 있고, 특정 응답이 필요한 테스트는 종전대로 큐에 넣는다.
*/
@Provides
@Singleton
fun provideWeeklyReportRepository(): WeeklyReportRepository = FakeWeeklyReportRepository()
fun provideWeeklyReportRepository(): WeeklyReportRepository = FakeWeeklyReportRepository(fallback = Result.success(emptyWeeklyReport()))
}

@Module
Expand Down
8 changes: 8 additions & 0 deletions app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
android:taskAffinity=".devsettings"
android:excludeFromRecents="true"
android:theme="@style/Theme.Afternotefe.Main" />
<!--
계측 테스트 전용 빈 Activity. setContent 와 hiltViewModel() 을 함께 쓰기 위한
자리라 exported 하지 않고 런처에도 올리지 않는다.
-->
<activity
android:name="com.afternote.afternote_fe.test.HiltTestActivity"
android:exported="false"
android:theme="@style/Theme.Afternotefe.Main" />
<!--
LAUNCHER intent-filter를 의도적으로 두지 않습니다.
런처 아이콘 중복(.MainActivity와 2개) 방지 — 디버그 설정 화면은
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.afternote.afternote_fe.test

import androidx.activity.ComponentActivity
import dagger.hilt.android.AndroidEntryPoint

/**
* `setContent` 로 화면 일부만 띄우면서 `hiltViewModel()` 도 써야 하는 계측 테스트용 빈 Activity.
*
* `createComposeRule()` 의 기본 Activity 는 Hilt 컴포넌트를 갖지 않아, 화면 안에서
* `hiltViewModel()` 을 부르는 자리(예: 홈의 MEMORIES 섹션)가 composition 되는 순간
* "does not implement GeneratedComponent" 로 죽는다. 그렇다고 `MainActivity` 를 쓰면
* 그쪽이 이미 콘텐츠를 세팅해 `setContent` 가 거부된다.
*
* debug 소스셋에 두는 이유는 androidTest 가 앱 APK 의 Activity 만 띄울 수 있어서다 —
* 릴리스 빌드에는 들어가지 않는다.
*/
@AndroidEntryPoint
class HiltTestActivity : ComponentActivity()
Loading
Loading