feat: hydrate board label chip colors from API
This commit is contained in:
@@ -25,6 +25,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.chip.Chip
|
||||
import java.text.DateFormat
|
||||
import java.util.ArrayDeque
|
||||
import java.util.Date
|
||||
@@ -148,6 +149,22 @@ class BoardDetailFlowTest {
|
||||
onView(withId(R.id.cardDueDateText)).check(matches(withCurrentTextColor(expectedColor ?: Color.BLACK)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun invalidTagColorFallsBackToOnSurfaceColor() {
|
||||
defaultDataSource.currentDetail = detailWithInvalidTagColor()
|
||||
val scenario = launchBoardDetail()
|
||||
|
||||
var expectedColor: Int? = null
|
||||
scenario.onActivity { activity ->
|
||||
expectedColor = MaterialColors.getColor(
|
||||
activity.findViewById(android.R.id.content),
|
||||
com.google.android.material.R.attr.colorOnSurface,
|
||||
)
|
||||
}
|
||||
|
||||
onView(withText("Backend")).check(matches(withChipStrokeColor(expectedColor ?: Color.DKGRAY)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dueDateUsesSystemLocaleFormatting() {
|
||||
Locale.setDefault(Locale.FRANCE)
|
||||
@@ -571,6 +588,21 @@ class BoardDetailFlowTest {
|
||||
}
|
||||
}
|
||||
|
||||
private fun withChipStrokeColor(expectedColor: Int): Matcher<View> {
|
||||
return object : TypeSafeMatcher<View>() {
|
||||
override fun describeTo(description: Description) {
|
||||
description.appendText("with chip stroke color: $expectedColor")
|
||||
}
|
||||
|
||||
override fun matchesSafely(item: View): Boolean {
|
||||
if (item !is Chip) {
|
||||
return false
|
||||
}
|
||||
return item.chipStrokeColor?.defaultColor == expectedColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun awaitCondition(timeoutMs: Long = 4_000L, condition: () -> Boolean) {
|
||||
val instrumentation = androidx.test.platform.app.InstrumentationRegistry.getInstrumentation()
|
||||
val start = System.currentTimeMillis()
|
||||
@@ -757,6 +789,25 @@ class BoardDetailFlowTest {
|
||||
)
|
||||
}
|
||||
|
||||
fun detailWithInvalidTagColor(): BoardDetail {
|
||||
return detailOneList().copy(
|
||||
lists = listOf(
|
||||
BoardListDetail(
|
||||
id = "list-1",
|
||||
title = "To Do",
|
||||
cards = listOf(
|
||||
BoardCardSummary(
|
||||
id = "card-1",
|
||||
title = "Card 1",
|
||||
tags = listOf(BoardTagSummary("tag-1", "Backend", "bad-color")),
|
||||
dueAtEpochMillis = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun detailWithCardTitle(title: String): BoardDetail {
|
||||
return detailOneList().copy(
|
||||
lists = listOf(
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.junit.runner.RunWith
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardSummary
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardTemplate
|
||||
@@ -199,5 +200,13 @@ class BoardsFlowTest {
|
||||
boards.removeAll { it.id == boardId }
|
||||
return BoardsApiResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): BoardsApiResult<LabelDetail> {
|
||||
return BoardsApiResult.Failure("Not needed in boards flow tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import space.hackenslacker.kanbn4droid.app.auth.AuthFailureReason
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@@ -200,5 +201,13 @@ class LoginFlowTest {
|
||||
private val result: AuthResult,
|
||||
) : KanbnApiClient {
|
||||
override suspend fun healthCheck(baseUrl: String, apiKey: String): AuthResult = result
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): space.hackenslacker.kanbn4droid.app.boards.BoardsApiResult<LabelDetail> {
|
||||
return space.hackenslacker.kanbn4droid.app.boards.BoardsApiResult.Failure("Not needed in login tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,17 @@ interface KanbnApiClient {
|
||||
suspend fun deleteCard(baseUrl: String, apiKey: String, cardId: String): BoardsApiResult<Unit> {
|
||||
return BoardsApiResult.Failure("Card deletion is not implemented.")
|
||||
}
|
||||
|
||||
suspend fun getLabelByPublicId(baseUrl: String, apiKey: String, labelId: String): BoardsApiResult<LabelDetail> {
|
||||
return BoardsApiResult.Failure("Label detail is not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
data class LabelDetail(
|
||||
val id: String,
|
||||
val colorHex: String,
|
||||
)
|
||||
|
||||
class HttpKanbnApiClient : KanbnApiClient {
|
||||
|
||||
override suspend fun listWorkspaces(baseUrl: String, apiKey: String): BoardsApiResult<List<WorkspaceSummary>> {
|
||||
@@ -304,6 +313,29 @@ class HttpKanbnApiClient : KanbnApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): BoardsApiResult<LabelDetail> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
request(
|
||||
baseUrl = baseUrl,
|
||||
path = "/api/v1/labels/$labelId",
|
||||
method = "GET",
|
||||
apiKey = apiKey,
|
||||
) { code, body ->
|
||||
if (code in 200..299) {
|
||||
parseLabelDetail(body, labelId)
|
||||
?.let { BoardsApiResult.Success(it) }
|
||||
?: BoardsApiResult.Failure("Malformed label detail response.")
|
||||
} else {
|
||||
BoardsApiResult.Failure(serverMessage(body, code))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> request(
|
||||
baseUrl: String,
|
||||
path: String,
|
||||
@@ -504,6 +536,23 @@ class HttpKanbnApiClient : KanbnApiClient {
|
||||
return BoardDetail(id = boardId, title = boardTitle, lists = lists)
|
||||
}
|
||||
|
||||
private fun parseLabelDetail(body: String, fallbackId: String): LabelDetail? {
|
||||
if (body.isBlank()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val root = parseJsonObject(body) ?: return null
|
||||
val data = root["data"] as? Map<*, *>
|
||||
val label = (data?.get("label") as? Map<*, *>)
|
||||
?: data
|
||||
?: (root["label"] as? Map<*, *>)
|
||||
?: root
|
||||
|
||||
val id = extractId(label).ifBlank { fallbackId }
|
||||
val colorHex = extractString(label, "colourCode", "colorCode", "colorHex", "color", "hex")
|
||||
return LabelDetail(id = id, colorHex = colorHex)
|
||||
}
|
||||
|
||||
private fun parseLists(board: Map<*, *>): List<BoardListDetail> {
|
||||
return extractObjectArray(board, "lists", "items", "data").mapNotNull { rawList ->
|
||||
val id = extractId(rawList)
|
||||
|
||||
@@ -5,6 +5,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardsApiResult
|
||||
|
||||
@@ -14,6 +15,8 @@ class BoardDetailRepository(
|
||||
private val apiClient: KanbnApiClient,
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
) {
|
||||
private val labelColorCache = mutableMapOf<String, String>()
|
||||
|
||||
companion object {
|
||||
const val MISSING_SESSION_MESSAGE = "Missing session. Please sign in again."
|
||||
}
|
||||
@@ -33,7 +36,13 @@ class BoardDetailRepository(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
boardId = normalizedBoardId,
|
||||
)
|
||||
).mapSuccess { detail ->
|
||||
hydrateLabelColors(
|
||||
detail = detail,
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun renameList(listId: String, newTitle: String): BoardsApiResult<Unit> {
|
||||
@@ -192,6 +201,63 @@ class BoardDetailRepository(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun hydrateLabelColors(
|
||||
detail: BoardDetail,
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
): BoardDetail {
|
||||
val missingColorIds = detail.lists
|
||||
.asSequence()
|
||||
.flatMap { list -> list.cards.asSequence() }
|
||||
.flatMap { card -> card.tags.asSequence() }
|
||||
.map { it.id.trim() }
|
||||
.filter { it.isNotBlank() }
|
||||
.distinct()
|
||||
.filter { !labelColorCache.containsKey(it) }
|
||||
.toList()
|
||||
|
||||
missingColorIds.forEach { labelId ->
|
||||
val colorHex = when (val labelResult = apiClient.getLabelByPublicId(baseUrl, apiKey, labelId)) {
|
||||
is BoardsApiResult.Success -> normalizeColorHex(labelResult.value)
|
||||
is BoardsApiResult.Failure -> ""
|
||||
}
|
||||
if (colorHex.isNotBlank()) {
|
||||
labelColorCache[labelId] = colorHex
|
||||
}
|
||||
}
|
||||
|
||||
val hydratedLists = detail.lists.map { list ->
|
||||
list.copy(
|
||||
cards = list.cards.map { card ->
|
||||
card.copy(
|
||||
tags = card.tags.map { tag ->
|
||||
val cached = labelColorCache[tag.id.trim()].orEmpty()
|
||||
val merged = if (cached.isNotBlank()) cached else tag.colorHex
|
||||
if (merged == tag.colorHex) {
|
||||
tag
|
||||
} else {
|
||||
tag.copy(colorHex = merged)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return if (hydratedLists == detail.lists) detail else detail.copy(lists = hydratedLists)
|
||||
}
|
||||
|
||||
private fun normalizeColorHex(label: LabelDetail): String {
|
||||
return label.colorHex.trim()
|
||||
}
|
||||
|
||||
private suspend fun <T, R> BoardsApiResult<T>.mapSuccess(transform: suspend (T) -> R): BoardsApiResult<R> {
|
||||
return when (this) {
|
||||
is BoardsApiResult.Success -> BoardsApiResult.Success(transform(this.value))
|
||||
is BoardsApiResult.Failure -> BoardsApiResult.Failure(this.message)
|
||||
}
|
||||
}
|
||||
|
||||
private data class SessionSnapshot(
|
||||
val baseUrl: String,
|
||||
val apiKey: String,
|
||||
|
||||
@@ -341,6 +341,84 @@ class HttpKanbnApiClientBoardDetailParsingTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLabelByPublicIdParsesColourCodeFromWrappedPayload() = runTest {
|
||||
TestServer().use { server ->
|
||||
server.register(
|
||||
path = "/api/v1/labels/label-1",
|
||||
status = 200,
|
||||
responseBody =
|
||||
"""
|
||||
{
|
||||
"data": {
|
||||
"label": {
|
||||
"public_id": "label-1",
|
||||
"colourCode": "#A1B2C3"
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
val result = HttpKanbnApiClient().getLabelByPublicId(server.baseUrl, "api-key", "label-1")
|
||||
|
||||
assertTrue(result is BoardsApiResult.Success<*>)
|
||||
val detail = (result as BoardsApiResult.Success<*>).value as LabelDetail
|
||||
assertEquals("label-1", detail.id)
|
||||
assertEquals("#A1B2C3", detail.colorHex)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLabelByPublicIdUsesFallbackIdWhenPayloadHasNoId() = runTest {
|
||||
TestServer().use { server ->
|
||||
server.register(
|
||||
path = "/api/v1/labels/label-fallback",
|
||||
status = 200,
|
||||
responseBody =
|
||||
"""
|
||||
{
|
||||
"label": {
|
||||
"colorCode": "#ABCDEF"
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
)
|
||||
|
||||
val result = HttpKanbnApiClient().getLabelByPublicId(server.baseUrl, "api-key", "label-fallback")
|
||||
|
||||
assertTrue(result is BoardsApiResult.Success<*>)
|
||||
val detail = (result as BoardsApiResult.Success<*>).value as LabelDetail
|
||||
assertEquals("label-fallback", detail.id)
|
||||
assertEquals("#ABCDEF", detail.colorHex)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLabelByPublicIdFailureUsesServerMessageAndFallback() = runTest {
|
||||
TestServer().use { server ->
|
||||
server.register(
|
||||
path = "/api/v1/labels/label-msg",
|
||||
status = 404,
|
||||
responseBody = """{"message":"Label not found"}""",
|
||||
)
|
||||
server.register(
|
||||
path = "/api/v1/labels/label-fallback",
|
||||
status = 500,
|
||||
responseBody = "{}",
|
||||
)
|
||||
|
||||
val client = HttpKanbnApiClient()
|
||||
val withMessage = client.getLabelByPublicId(server.baseUrl, "api", "label-msg")
|
||||
val fallback = client.getLabelByPublicId(server.baseUrl, "api", "label-fallback")
|
||||
|
||||
assertTrue(withMessage is BoardsApiResult.Failure)
|
||||
assertEquals("Label not found", (withMessage as BoardsApiResult.Failure).message)
|
||||
assertTrue(fallback is BoardsApiResult.Failure)
|
||||
assertEquals("Server error: 500", (fallback as BoardsApiResult.Failure).message)
|
||||
}
|
||||
}
|
||||
|
||||
private data class CapturedRequest(
|
||||
val method: String,
|
||||
val path: String,
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.junit.Test
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardSummary
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardTemplate
|
||||
@@ -99,6 +100,95 @@ class BoardDetailRepositoryTest {
|
||||
assertEquals("Server says no", (result as BoardsApiResult.Failure).message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getBoardDetailFetchesLabelColorCodesAndCachesThem() = runTest {
|
||||
val detailWithTags = BoardDetail(
|
||||
id = "board-1",
|
||||
title = "Board",
|
||||
lists = listOf(
|
||||
BoardListDetail(
|
||||
id = "list-1",
|
||||
title = "To Do",
|
||||
cards = listOf(
|
||||
BoardCardSummary(
|
||||
id = "card-1",
|
||||
title = "Card 1",
|
||||
tags = listOf(
|
||||
BoardTagSummary(id = "label-1", name = "Urgent", colorHex = ""),
|
||||
BoardTagSummary(id = "label-2", name = "Backend", colorHex = "#000000"),
|
||||
),
|
||||
dueAtEpochMillis = null,
|
||||
),
|
||||
BoardCardSummary(
|
||||
id = "card-2",
|
||||
title = "Card 2",
|
||||
tags = listOf(
|
||||
BoardTagSummary(id = "label-1", name = "Urgent", colorHex = ""),
|
||||
),
|
||||
dueAtEpochMillis = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val apiClient = FakeBoardDetailApiClient().apply {
|
||||
boardDetailResult = BoardsApiResult.Success(detailWithTags)
|
||||
labelByIdResults = mapOf(
|
||||
"label-1" to BoardsApiResult.Success(LabelDetail("label-1", "#112233")),
|
||||
"label-2" to BoardsApiResult.Success(LabelDetail("label-2", "#445566")),
|
||||
)
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val first = repository.getBoardDetail("board-1")
|
||||
val second = repository.getBoardDetail("board-1")
|
||||
|
||||
assertTrue(first is BoardsApiResult.Success<*>)
|
||||
assertTrue(second is BoardsApiResult.Success<*>)
|
||||
val firstDetail = (first as BoardsApiResult.Success<BoardDetail>).value
|
||||
val secondDetail = (second as BoardsApiResult.Success<BoardDetail>).value
|
||||
assertEquals("#112233", firstDetail.lists[0].cards[0].tags[0].colorHex)
|
||||
assertEquals("#445566", firstDetail.lists[0].cards[0].tags[1].colorHex)
|
||||
assertEquals("#112233", firstDetail.lists[0].cards[1].tags[0].colorHex)
|
||||
assertEquals("#112233", secondDetail.lists[0].cards[0].tags[0].colorHex)
|
||||
assertEquals("#445566", secondDetail.lists[0].cards[0].tags[1].colorHex)
|
||||
assertEquals(listOf("label-1", "label-2"), apiClient.getLabelByPublicIdCalls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getBoardDetailKeepsOriginalColorWhenLabelLookupFails() = runTest {
|
||||
val detailWithTag = BoardDetail(
|
||||
id = "board-1",
|
||||
title = "Board",
|
||||
lists = listOf(
|
||||
BoardListDetail(
|
||||
id = "list-1",
|
||||
title = "To Do",
|
||||
cards = listOf(
|
||||
BoardCardSummary(
|
||||
id = "card-1",
|
||||
title = "Card 1",
|
||||
tags = listOf(BoardTagSummary(id = "label-1", name = "Urgent", colorHex = "#ABCDEF")),
|
||||
dueAtEpochMillis = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
val apiClient = FakeBoardDetailApiClient().apply {
|
||||
boardDetailResult = BoardsApiResult.Success(detailWithTag)
|
||||
labelByIdResults = mapOf("label-1" to BoardsApiResult.Failure("Server unavailable"))
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.getBoardDetail("board-1")
|
||||
|
||||
assertTrue(result is BoardsApiResult.Success<*>)
|
||||
val detail = (result as BoardsApiResult.Success<BoardDetail>).value
|
||||
assertEquals("#ABCDEF", detail.lists[0].cards[0].tags[0].colorHex)
|
||||
assertEquals(listOf("label-1"), apiClient.getLabelByPublicIdCalls)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renameListPropagatesApiFailureMessageUnchanged() = runTest {
|
||||
val apiClient = FakeBoardDetailApiClient().apply {
|
||||
@@ -366,6 +456,7 @@ class BoardDetailRepositoryTest {
|
||||
var renameListResult: BoardsApiResult<Unit> = BoardsApiResult.Success(Unit)
|
||||
var moveOutcomes: Map<String, BoardsApiResult<Unit>> = emptyMap()
|
||||
var deleteOutcomes: Map<String, BoardsApiResult<Unit>> = emptyMap()
|
||||
var labelByIdResults: Map<String, BoardsApiResult<LabelDetail>> = emptyMap()
|
||||
|
||||
var listWorkspacesCalls: Int = 0
|
||||
var lastBoardId: String? = null
|
||||
@@ -374,6 +465,7 @@ class BoardDetailRepositoryTest {
|
||||
var movedCardIds: MutableList<String> = mutableListOf()
|
||||
var deletedCardIds: MutableList<String> = mutableListOf()
|
||||
var lastMoveTargetListId: String? = null
|
||||
var getLabelByPublicIdCalls: MutableList<String> = mutableListOf()
|
||||
|
||||
override suspend fun healthCheck(baseUrl: String, apiKey: String): AuthResult = AuthResult.Success
|
||||
|
||||
@@ -425,6 +517,15 @@ class BoardDetailRepositoryTest {
|
||||
return deleteOutcomes[cardId] ?: BoardsApiResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): BoardsApiResult<LabelDetail> {
|
||||
getLabelByPublicIdCalls += labelId
|
||||
return labelByIdResults[labelId] ?: BoardsApiResult.Failure("Missing fake label")
|
||||
}
|
||||
|
||||
override suspend fun listBoards(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.junit.Test
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
|
||||
class BoardsRepositoryTest {
|
||||
@@ -250,5 +251,13 @@ class BoardsRepositoryTest {
|
||||
lastDeletedId = boardId
|
||||
return deleteBoardResult
|
||||
}
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): BoardsApiResult<LabelDetail> {
|
||||
return BoardsApiResult.Failure("Not needed in boards tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.junit.Test
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@@ -194,5 +195,13 @@ class BoardsViewModelTest {
|
||||
lastDeletedId = boardId
|
||||
return deleteBoardResult
|
||||
}
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): BoardsApiResult<LabelDetail> {
|
||||
return BoardsApiResult.Failure("Not needed in boards tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user