refactor: decouple card detail viewmodel datasource contracts

This commit is contained in:
2026-03-16 21:00:51 -04:00
parent beab9006a3
commit dfcdc79856
2 changed files with 133 additions and 69 deletions

View File

@@ -1,6 +1,7 @@
package space.hackenslacker.kanbn4droid.app.carddetail package space.hackenslacker.kanbn4droid.app.carddetail
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import java.time.LocalDate import java.time.LocalDate
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -45,13 +46,55 @@ sealed interface CardDetailUiEvent {
data class ShowSnackbar(val message: String) : CardDetailUiEvent data class ShowSnackbar(val message: String) : CardDetailUiEvent
} }
sealed interface DataSourceResult<out T> {
data class Success<T>(val value: T) : DataSourceResult<T>
data class GenericError(val message: String) : DataSourceResult<Nothing>
data object SessionExpired : DataSourceResult<Nothing>
}
interface CardDetailDataSource { interface CardDetailDataSource {
suspend fun loadCard(cardId: String): CardDetailRepository.Result<CardDetail> suspend fun loadCard(cardId: String): DataSourceResult<CardDetail>
suspend fun updateTitle(cardId: String, title: String): CardDetailRepository.Result<Unit> suspend fun updateTitle(cardId: String, title: String): DataSourceResult<Unit>
suspend fun updateDescription(cardId: String, description: String): CardDetailRepository.Result<Unit> suspend fun updateDescription(cardId: String, description: String): DataSourceResult<Unit>
suspend fun updateDueDate(cardId: String, dueDate: LocalDate?): CardDetailRepository.Result<Unit> suspend fun updateDueDate(cardId: String, dueDate: LocalDate?): DataSourceResult<Unit>
suspend fun listActivities(cardId: String): CardDetailRepository.Result<List<CardActivity>> suspend fun listActivities(cardId: String): DataSourceResult<List<CardActivity>>
suspend fun addComment(cardId: String, comment: String): CardDetailRepository.Result<Unit> suspend fun addComment(cardId: String, comment: String): DataSourceResult<Unit>
}
internal class CardDetailRepositoryDataSource(
private val repository: CardDetailRepository,
private val loadCardCall: suspend (String) -> CardDetailRepository.Result<CardDetail>,
) : CardDetailDataSource {
override suspend fun loadCard(cardId: String): DataSourceResult<CardDetail> {
return loadCardCall(cardId).toDataSourceResult()
}
override suspend fun updateTitle(cardId: String, title: String): DataSourceResult<Unit> {
return repository.updateTitle(cardId, title).toDataSourceResult()
}
override suspend fun updateDescription(cardId: String, description: String): DataSourceResult<Unit> {
return repository.updateDescription(cardId, description).toDataSourceResult()
}
override suspend fun updateDueDate(cardId: String, dueDate: LocalDate?): DataSourceResult<Unit> {
return repository.updateDueDate(cardId, dueDate).toDataSourceResult()
}
override suspend fun listActivities(cardId: String): DataSourceResult<List<CardActivity>> {
return repository.listActivities(cardId).toDataSourceResult()
}
override suspend fun addComment(cardId: String, comment: String): DataSourceResult<Unit> {
val result = repository.addComment(cardId, comment)
return when (result) {
is CardDetailRepository.Result.Success -> DataSourceResult.Success(Unit)
is CardDetailRepository.Result.Failure.SessionExpired -> DataSourceResult.SessionExpired
is CardDetailRepository.Result.Failure.Generic -> {
DataSourceResult.GenericError(result.message)
}
}
}
} }
class CardDetailViewModel( class CardDetailViewModel(
@@ -93,7 +136,7 @@ class CardDetailViewModel(
} }
when (val result = repository.loadCard(cardId)) { when (val result = repository.loadCard(cardId)) {
is CardDetailRepository.Result.Success -> { is DataSourceResult.Success -> {
val detail = result.value val detail = result.value
persistedTitle = detail.title persistedTitle = detail.title
persistedDescription = detail.description persistedDescription = detail.description
@@ -114,7 +157,7 @@ class CardDetailViewModel(
loadActivities() loadActivities()
} }
is CardDetailRepository.Result.Failure.SessionExpired -> { is DataSourceResult.SessionExpired -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isInitialLoading = false, isInitialLoading = false,
@@ -124,7 +167,7 @@ class CardDetailViewModel(
_events.emit(CardDetailUiEvent.SessionExpired) _events.emit(CardDetailUiEvent.SessionExpired)
} }
is CardDetailRepository.Result.Failure.Generic -> { is DataSourceResult.GenericError -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isInitialLoading = false, isInitialLoading = false,
@@ -249,7 +292,7 @@ class CardDetailViewModel(
viewModelScope.launch { viewModelScope.launch {
_uiState.update { it.copy(isCommentSubmitting = true, commentErrorMessage = null) } _uiState.update { it.copy(isCommentSubmitting = true, commentErrorMessage = null) }
when (val result = repository.addComment(cardId, payload)) { when (val result = repository.addComment(cardId, payload)) {
is CardDetailRepository.Result.Success -> { is DataSourceResult.Success -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isCommentSubmitting = false, isCommentSubmitting = false,
@@ -262,7 +305,7 @@ class CardDetailViewModel(
loadActivities() loadActivities()
} }
is CardDetailRepository.Result.Failure.SessionExpired -> { is DataSourceResult.SessionExpired -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isCommentSubmitting = false, isCommentSubmitting = false,
@@ -272,7 +315,7 @@ class CardDetailViewModel(
_events.emit(CardDetailUiEvent.SessionExpired) _events.emit(CardDetailUiEvent.SessionExpired)
} }
is CardDetailRepository.Result.Failure.Generic -> { is DataSourceResult.GenericError -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isCommentSubmitting = false, isCommentSubmitting = false,
@@ -310,7 +353,7 @@ class CardDetailViewModel(
viewModelScope.launch { viewModelScope.launch {
_uiState.update { it.copy(isTitleSaving = true, titleErrorMessage = null) } _uiState.update { it.copy(isTitleSaving = true, titleErrorMessage = null) }
when (val result = repository.updateTitle(cardId, normalized)) { when (val result = repository.updateTitle(cardId, normalized)) {
is CardDetailRepository.Result.Success -> { is DataSourceResult.Success -> {
persistedTitle = normalized persistedTitle = normalized
_uiState.update { _uiState.update {
it.copy( it.copy(
@@ -321,7 +364,7 @@ class CardDetailViewModel(
} }
} }
is CardDetailRepository.Result.Failure.SessionExpired -> { is DataSourceResult.SessionExpired -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isTitleSaving = false, isTitleSaving = false,
@@ -331,7 +374,7 @@ class CardDetailViewModel(
_events.emit(CardDetailUiEvent.SessionExpired) _events.emit(CardDetailUiEvent.SessionExpired)
} }
is CardDetailRepository.Result.Failure.Generic -> { is DataSourceResult.GenericError -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isTitleSaving = false, isTitleSaving = false,
@@ -387,7 +430,7 @@ class CardDetailViewModel(
inFlightDescriptionPayload = normalized inFlightDescriptionPayload = normalized
_uiState.update { it.copy(isDescriptionSaving = true, descriptionErrorMessage = null) } _uiState.update { it.copy(isDescriptionSaving = true, descriptionErrorMessage = null) }
when (val result = repository.updateDescription(cardId, normalized)) { when (val result = repository.updateDescription(cardId, normalized)) {
is CardDetailRepository.Result.Success -> { is DataSourceResult.Success -> {
persistedDescription = normalized persistedDescription = normalized
_uiState.update { _uiState.update {
val stillDirty = it.description != persistedDescription val stillDirty = it.description != persistedDescription
@@ -399,7 +442,7 @@ class CardDetailViewModel(
} }
} }
is CardDetailRepository.Result.Failure.SessionExpired -> { is DataSourceResult.SessionExpired -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isDescriptionSaving = false, isDescriptionSaving = false,
@@ -409,7 +452,7 @@ class CardDetailViewModel(
_events.emit(CardDetailUiEvent.SessionExpired) _events.emit(CardDetailUiEvent.SessionExpired)
} }
is CardDetailRepository.Result.Failure.Generic -> { is DataSourceResult.GenericError -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isDescriptionSaving = false, isDescriptionSaving = false,
@@ -450,7 +493,7 @@ class CardDetailViewModel(
} }
when (val result = repository.updateDueDate(cardId, dueDate)) { when (val result = repository.updateDueDate(cardId, dueDate)) {
is CardDetailRepository.Result.Success -> { is DataSourceResult.Success -> {
persistedDueDate = dueDate persistedDueDate = dueDate
_uiState.update { _uiState.update {
it.copy( it.copy(
@@ -460,7 +503,7 @@ class CardDetailViewModel(
} }
} }
is CardDetailRepository.Result.Failure.SessionExpired -> { is DataSourceResult.SessionExpired -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isDueDateSaving = false, isDueDateSaving = false,
@@ -470,7 +513,7 @@ class CardDetailViewModel(
_events.emit(CardDetailUiEvent.SessionExpired) _events.emit(CardDetailUiEvent.SessionExpired)
} }
is CardDetailRepository.Result.Failure.Generic -> { is DataSourceResult.GenericError -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isDueDateSaving = false, isDueDateSaving = false,
@@ -489,17 +532,17 @@ class CardDetailViewModel(
viewModelScope.launch { viewModelScope.launch {
_uiState.update { it.copy(isActivitiesLoading = true, activitiesErrorMessage = null) } _uiState.update { it.copy(isActivitiesLoading = true, activitiesErrorMessage = null) }
when (val result = repository.listActivities(cardId)) { when (val result = repository.listActivities(cardId)) {
is CardDetailRepository.Result.Success -> { is DataSourceResult.Success -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isActivitiesLoading = false, isActivitiesLoading = false,
activities = result.value.sortedByDescending { activity -> activity.createdAtEpochMillis }, activities = result.value,
activitiesErrorMessage = null, activitiesErrorMessage = null,
) )
} }
} }
is CardDetailRepository.Result.Failure.SessionExpired -> { is DataSourceResult.SessionExpired -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isActivitiesLoading = false, isActivitiesLoading = false,
@@ -509,7 +552,7 @@ class CardDetailViewModel(
_events.emit(CardDetailUiEvent.SessionExpired) _events.emit(CardDetailUiEvent.SessionExpired)
} }
is CardDetailRepository.Result.Failure.Generic -> { is DataSourceResult.GenericError -> {
_uiState.update { _uiState.update {
it.copy( it.copy(
isActivitiesLoading = false, isActivitiesLoading = false,
@@ -520,4 +563,25 @@ class CardDetailViewModel(
} }
} }
} }
class Factory(
private val cardId: String,
private val dataSource: CardDetailDataSource,
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(CardDetailViewModel::class.java)) {
return CardDetailViewModel(cardId = cardId, repository = dataSource) as T
}
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
}
}
private fun <T> CardDetailRepository.Result<T>.toDataSourceResult(): DataSourceResult<T> {
return when (this) {
is CardDetailRepository.Result.Success -> DataSourceResult.Success(value)
is CardDetailRepository.Result.Failure.Generic -> DataSourceResult.GenericError(message)
is CardDetailRepository.Result.Failure.SessionExpired -> DataSourceResult.SessionExpired
}
} }

View File

@@ -37,8 +37,8 @@ class CardDetailViewModelTest {
@Test @Test
fun initialLoad_fillsEditableFieldsAndActivities() = runTest { fun initialLoad_fillsEditableFieldsAndActivities() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(sampleActivitiesShuffled()), listActivitiesResult = DataSourceResult.Success(sampleActivitiesShuffled()),
) )
val viewModel = CardDetailViewModel( val viewModel = CardDetailViewModel(
cardId = "card-1", cardId = "card-1",
@@ -61,7 +61,7 @@ class CardDetailViewModelTest {
@Test @Test
fun loadSessionExpired_emitsEvent_andRetryPathsDoNotRun() = runTest { fun loadSessionExpired_emitsEvent_andRetryPathsDoNotRun() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Failure.SessionExpired(), loadCardResult = DataSourceResult.SessionExpired,
) )
val viewModel = CardDetailViewModel(cardId = "card-1", repository = repository) val viewModel = CardDetailViewModel(cardId = "card-1", repository = repository)
val eventDeferred = async { viewModel.events.first() } val eventDeferred = async { viewModel.events.first() }
@@ -90,8 +90,8 @@ class CardDetailViewModelTest {
@Test @Test
fun titleTrimRejectsBlank_andIsolatedFromDescription() = runTest { fun titleTrimRejectsBlank_andIsolatedFromDescription() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -109,8 +109,8 @@ class CardDetailViewModelTest {
@Test @Test
fun dueDateSetAndClear_areIndependentAndSaved() = runTest { fun dueDateSetAndClear_areIndependentAndSaved() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -130,8 +130,8 @@ class CardDetailViewModelTest {
fun descriptionDebounce_savesLatestOnly_andSuppressesDuplicateInflight() = runTest { fun descriptionDebounce_savesLatestOnly_andSuppressesDuplicateInflight() = runTest {
val gate = CompletableDeferred<Unit>() val gate = CompletableDeferred<Unit>()
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
updateDescriptionGate = gate, updateDescriptionGate = gate,
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -164,8 +164,8 @@ class CardDetailViewModelTest {
@Test @Test
fun descriptionFocusLossAndOnStop_flushLatestDirtyImmediately() = runTest { fun descriptionFocusLossAndOnStop_flushLatestDirtyImmediately() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -186,8 +186,8 @@ class CardDetailViewModelTest {
val firstGate = CompletableDeferred<Unit>() val firstGate = CompletableDeferred<Unit>()
val secondGate = CompletableDeferred<Unit>() val secondGate = CompletableDeferred<Unit>()
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
updateDescriptionGates = ArrayDeque(listOf(firstGate, secondGate)), updateDescriptionGates = ArrayDeque(listOf(firstGate, secondGate)),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -222,14 +222,14 @@ class CardDetailViewModelTest {
@Test @Test
fun addComment_success_closesDialog_showsSnackbar_refreshesActivities() = runTest { fun addComment_success_closesDialog_showsSnackbar_refreshesActivities() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResults = ArrayDeque( listActivitiesResults = ArrayDeque(
listOf( listOf(
CardDetailRepository.Result.Success(emptyList()), DataSourceResult.Success(emptyList()),
CardDetailRepository.Result.Success(sampleActivitiesShuffled()), DataSourceResult.Success(sampleActivitiesShuffled()),
), ),
), ),
addCommentResult = CardDetailRepository.Result.Success(Unit), addCommentResult = DataSourceResult.Success(Unit),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
val eventDeferred = async { viewModel.events.first { it is CardDetailUiEvent.ShowSnackbar } } val eventDeferred = async { viewModel.events.first { it is CardDetailUiEvent.ShowSnackbar } }
@@ -250,9 +250,9 @@ class CardDetailViewModelTest {
@Test @Test
fun addComment_failure_keepsDialogOpen_andStoresRetryPayload() = runTest { fun addComment_failure_keepsDialogOpen_andStoresRetryPayload() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
addCommentResult = CardDetailRepository.Result.Failure.Generic("Cannot add comment"), addCommentResult = DataSourceResult.GenericError("Cannot add comment"),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -272,11 +272,11 @@ class CardDetailViewModelTest {
@Test @Test
fun activitiesRetry_recoversFromFailure() = runTest { fun activitiesRetry_recoversFromFailure() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResults = ArrayDeque( listActivitiesResults = ArrayDeque(
listOf( listOf(
CardDetailRepository.Result.Failure.Generic("Network error"), DataSourceResult.GenericError("Network error"),
CardDetailRepository.Result.Success(sampleActivitiesShuffled()), DataSourceResult.Success(sampleActivitiesShuffled()),
), ),
), ),
) )
@@ -296,8 +296,8 @@ class CardDetailViewModelTest {
val viewModel = loadedViewModel( val viewModel = loadedViewModel(
this, this,
FakeCardDetailDataSource( FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
), ),
) )
@@ -312,11 +312,11 @@ class CardDetailViewModelTest {
@Test @Test
fun fieldSpecificRetry_usesLastAttemptedPayload() = runTest { fun fieldSpecificRetry_usesLastAttemptedPayload() = runTest {
val repository = FakeCardDetailDataSource( val repository = FakeCardDetailDataSource(
loadCardResult = CardDetailRepository.Result.Success(sampleCardDetail()), loadCardResult = DataSourceResult.Success(sampleCardDetail()),
listActivitiesResult = CardDetailRepository.Result.Success(emptyList()), listActivitiesResult = DataSourceResult.Success(emptyList()),
updateTitleResult = CardDetailRepository.Result.Failure.Generic("title failed"), updateTitleResult = DataSourceResult.GenericError("title failed"),
updateDueDateResult = CardDetailRepository.Result.Failure.Generic("due failed"), updateDueDateResult = DataSourceResult.GenericError("due failed"),
updateDescriptionResult = CardDetailRepository.Result.Failure.Generic("desc failed"), updateDescriptionResult = DataSourceResult.GenericError("desc failed"),
) )
val viewModel = loadedViewModel(this, repository) val viewModel = loadedViewModel(this, repository)
@@ -358,13 +358,13 @@ class CardDetailViewModelTest {
} }
private class FakeCardDetailDataSource( private class FakeCardDetailDataSource(
var loadCardResult: CardDetailRepository.Result<CardDetail> = CardDetailRepository.Result.Success(sampleCardDetail()), var loadCardResult: DataSourceResult<CardDetail> = DataSourceResult.Success(sampleCardDetail()),
var listActivitiesResult: CardDetailRepository.Result<List<CardActivity>> = CardDetailRepository.Result.Success(emptyList()), var listActivitiesResult: DataSourceResult<List<CardActivity>> = DataSourceResult.Success(emptyList()),
var listActivitiesResults: ArrayDeque<CardDetailRepository.Result<List<CardActivity>>> = ArrayDeque(), var listActivitiesResults: ArrayDeque<DataSourceResult<List<CardActivity>>> = ArrayDeque(),
var updateTitleResult: CardDetailRepository.Result<Unit> = CardDetailRepository.Result.Success(Unit), var updateTitleResult: DataSourceResult<Unit> = DataSourceResult.Success(Unit),
var updateDescriptionResult: CardDetailRepository.Result<Unit> = CardDetailRepository.Result.Success(Unit), var updateDescriptionResult: DataSourceResult<Unit> = DataSourceResult.Success(Unit),
var updateDueDateResult: CardDetailRepository.Result<Unit> = CardDetailRepository.Result.Success(Unit), var updateDueDateResult: DataSourceResult<Unit> = DataSourceResult.Success(Unit),
var addCommentResult: CardDetailRepository.Result<Unit> = CardDetailRepository.Result.Success(Unit), var addCommentResult: DataSourceResult<Unit> = DataSourceResult.Success(Unit),
var updateDescriptionGate: CompletableDeferred<Unit>? = null, var updateDescriptionGate: CompletableDeferred<Unit>? = null,
var updateDescriptionGates: ArrayDeque<CompletableDeferred<Unit>> = ArrayDeque(), var updateDescriptionGates: ArrayDeque<CompletableDeferred<Unit>> = ArrayDeque(),
) : CardDetailDataSource { ) : CardDetailDataSource {
@@ -380,18 +380,18 @@ class CardDetailViewModelTest {
val updateDueDatePayloads: MutableList<LocalDate?> = mutableListOf() val updateDueDatePayloads: MutableList<LocalDate?> = mutableListOf()
val addCommentPayloads: MutableList<String> = mutableListOf() val addCommentPayloads: MutableList<String> = mutableListOf()
override suspend fun loadCard(cardId: String): CardDetailRepository.Result<CardDetail> { override suspend fun loadCard(cardId: String): DataSourceResult<CardDetail> {
loadCalls += 1 loadCalls += 1
return loadCardResult return loadCardResult
} }
override suspend fun updateTitle(cardId: String, title: String): CardDetailRepository.Result<Unit> { override suspend fun updateTitle(cardId: String, title: String): DataSourceResult<Unit> {
updateTitleCalls += 1 updateTitleCalls += 1
updateTitlePayloads += title updateTitlePayloads += title
return updateTitleResult return updateTitleResult
} }
override suspend fun updateDescription(cardId: String, description: String): CardDetailRepository.Result<Unit> { override suspend fun updateDescription(cardId: String, description: String): DataSourceResult<Unit> {
updateDescriptionCalls += 1 updateDescriptionCalls += 1
updateDescriptionPayloads += description updateDescriptionPayloads += description
if (updateDescriptionGates.isNotEmpty()) { if (updateDescriptionGates.isNotEmpty()) {
@@ -401,13 +401,13 @@ class CardDetailViewModelTest {
return updateDescriptionResult return updateDescriptionResult
} }
override suspend fun updateDueDate(cardId: String, dueDate: LocalDate?): CardDetailRepository.Result<Unit> { override suspend fun updateDueDate(cardId: String, dueDate: LocalDate?): DataSourceResult<Unit> {
updateDueDateCalls += 1 updateDueDateCalls += 1
updateDueDatePayloads += dueDate updateDueDatePayloads += dueDate
return updateDueDateResult return updateDueDateResult
} }
override suspend fun listActivities(cardId: String): CardDetailRepository.Result<List<CardActivity>> { override suspend fun listActivities(cardId: String): DataSourceResult<List<CardActivity>> {
listActivitiesCalls += 1 listActivitiesCalls += 1
if (listActivitiesResults.isNotEmpty()) { if (listActivitiesResults.isNotEmpty()) {
return listActivitiesResults.removeFirst() return listActivitiesResults.removeFirst()
@@ -415,7 +415,7 @@ class CardDetailViewModelTest {
return listActivitiesResult return listActivitiesResult
} }
override suspend fun addComment(cardId: String, comment: String): CardDetailRepository.Result<Unit> { override suspend fun addComment(cardId: String, comment: String): DataSourceResult<Unit> {
addCommentCalls += 1 addCommentCalls += 1
addCommentPayloads += comment addCommentPayloads += comment
return addCommentResult return addCommentResult
@@ -440,9 +440,9 @@ class CardDetailViewModelTest {
fun sampleActivitiesShuffled(): List<CardActivity> { fun sampleActivitiesShuffled(): List<CardActivity> {
return listOf( return listOf(
CardActivity(id = "a-new", type = "comment", text = "new", createdAtEpochMillis = 3L),
CardActivity(id = "a-mid", type = "comment", text = "mid", createdAtEpochMillis = 2L), CardActivity(id = "a-mid", type = "comment", text = "mid", createdAtEpochMillis = 2L),
CardActivity(id = "a-old", type = "comment", text = "old", createdAtEpochMillis = 1L), CardActivity(id = "a-old", type = "comment", text = "old", createdAtEpochMillis = 1L),
CardActivity(id = "a-new", type = "comment", text = "new", createdAtEpochMillis = 3L),
) )
} }
} }