fix: handle workspace-switch unauthorized and rollback edge case
This commit is contained in:
@@ -64,6 +64,7 @@ class BoardsRepository(
|
||||
is BoardsApiResult.Failure -> return sessionResult
|
||||
}
|
||||
|
||||
val previousWorkspaceId = sessionStore.getWorkspaceId()?.takeIf { it.isNotBlank() }
|
||||
sessionStore.saveWorkspaceId(normalizedWorkspaceId)
|
||||
val listBoardsResult = apiClient.listBoards(
|
||||
baseUrl = session.baseUrl,
|
||||
@@ -72,7 +73,14 @@ class BoardsRepository(
|
||||
)
|
||||
return when (listBoardsResult) {
|
||||
is BoardsApiResult.Success -> BoardsApiResult.Success(Unit)
|
||||
is BoardsApiResult.Failure -> listBoardsResult
|
||||
is BoardsApiResult.Failure -> {
|
||||
if (previousWorkspaceId != null) {
|
||||
sessionStore.saveWorkspaceId(previousWorkspaceId)
|
||||
} else {
|
||||
sessionStore.clearWorkspaceId()
|
||||
}
|
||||
listBoardsResult
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,11 +101,15 @@ class BoardsViewModel(
|
||||
),
|
||||
)
|
||||
}
|
||||
if (result.message.isUnauthorizedFailureMessage()) {
|
||||
_events.emit(BoardsUiEvent.ForceSignOut)
|
||||
} else {
|
||||
_events.emit(BoardsUiEvent.ShowServerError(result.message))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshBoards() {
|
||||
fetchBoards(initial = false, refresh = true)
|
||||
@@ -288,3 +292,8 @@ class BoardsViewModel(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.isUnauthorizedFailureMessage(): Boolean {
|
||||
val normalized = lowercase()
|
||||
return "401" in normalized || "403" in normalized || "authentication" in normalized || "unauthorized" in normalized
|
||||
}
|
||||
|
||||
@@ -318,6 +318,54 @@ class BoardsViewModelTest {
|
||||
assertEquals("ws-1", sessionStore.getWorkspaceId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun workspaceSwitchUnauthorizedEmitsForceSignOut() = runTest {
|
||||
val api = FakeBoardsApiClient().apply {
|
||||
usersMeResult = BoardsApiResult.Success(DrawerProfile(displayName = "Alice", email = null))
|
||||
workspacesResult = BoardsApiResult.Success(
|
||||
listOf(
|
||||
WorkspaceSummary("ws-1", "Main"),
|
||||
WorkspaceSummary("ws-2", "Platform"),
|
||||
),
|
||||
)
|
||||
listBoardsResults.addLast(BoardsApiResult.Failure("Server error: 401"))
|
||||
}
|
||||
val sessionStore = InMemorySessionStore("https://kan.bn/", workspaceId = "ws-1")
|
||||
val viewModel = newViewModel(api, sessionStore = sessionStore)
|
||||
viewModel.loadDrawerData()
|
||||
advanceUntilIdle()
|
||||
|
||||
val eventDeferred = async { viewModel.events.first() }
|
||||
viewModel.onWorkspaceSelected("ws-2")
|
||||
advanceUntilIdle()
|
||||
|
||||
val event = eventDeferred.await()
|
||||
assertTrue(event is BoardsUiEvent.ForceSignOut)
|
||||
assertEquals("ws-1", sessionStore.getWorkspaceId())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun workspaceSwitchFailureWithNullPreviousRollsBackPersistedWorkspaceId() = runTest {
|
||||
val api = FakeBoardsApiClient().apply {
|
||||
usersMeResult = BoardsApiResult.Success(DrawerProfile(displayName = "Alice", email = null))
|
||||
workspacesResult = BoardsApiResult.Failure("Server error: 500")
|
||||
listBoardsResults.addLast(BoardsApiResult.Failure("Server error: 500"))
|
||||
}
|
||||
val sessionStore = InMemorySessionStore("https://kan.bn/", workspaceId = null)
|
||||
val viewModel = newViewModel(api, sessionStore = sessionStore)
|
||||
viewModel.loadDrawerData()
|
||||
advanceUntilIdle()
|
||||
|
||||
assertEquals(null, viewModel.uiState.value.drawer.activeWorkspaceId)
|
||||
assertEquals(null, sessionStore.getWorkspaceId())
|
||||
|
||||
viewModel.onWorkspaceSelected("ws-2")
|
||||
advanceUntilIdle()
|
||||
|
||||
assertEquals(null, sessionStore.getWorkspaceId())
|
||||
assertEquals(null, viewModel.uiState.value.drawer.activeWorkspaceId)
|
||||
}
|
||||
|
||||
private fun newViewModel(
|
||||
apiClient: FakeBoardsApiClient,
|
||||
sessionStore: InMemorySessionStore = InMemorySessionStore("https://kan.bn/", workspaceId = "ws-1"),
|
||||
|
||||
Reference in New Issue
Block a user