feat: add AuthViewModel, ScheduleViewModel, and EventDetailViewModel with StateFlow
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package ch.parano.myice.auth
|
package ch.parano.myice.auth
|
||||||
|
|
||||||
interface OAuthClient {
|
interface OAuthClient {
|
||||||
|
fun redirectUri(): String
|
||||||
suspend fun authenticate(loginUrl: String): String?
|
suspend fun authenticate(loginUrl: String): String?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package ch.parano.myice.viewmodel
|
||||||
|
|
||||||
|
import ch.parano.myice.auth.AuthSettings
|
||||||
|
import ch.parano.myice.auth.OAuthClient
|
||||||
|
import ch.parano.myice.network.ApiConfig
|
||||||
|
import ch.parano.myice.network.ApiService
|
||||||
|
import ch.parano.myice.network.AuthTokenHolder
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
sealed class AuthState {
|
||||||
|
data object Loading : AuthState()
|
||||||
|
data class Authenticated(val email: String) : AuthState()
|
||||||
|
data object Unauthenticated : AuthState()
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthViewModel(
|
||||||
|
private val apiService: ApiService,
|
||||||
|
private val authSettings: AuthSettings,
|
||||||
|
private val oauthClient: OAuthClient,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow<AuthState>(AuthState.Loading)
|
||||||
|
val state: StateFlow<AuthState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val token = authSettings.getStoredToken()
|
||||||
|
if (token != null) {
|
||||||
|
AuthTokenHolder.token = token
|
||||||
|
try {
|
||||||
|
val userInfo = apiService.getUserInfo()
|
||||||
|
_state.value = AuthState.Authenticated(userInfo.email)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
authSettings.clear()
|
||||||
|
_state.value = AuthState.Unauthenticated
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_state.value = AuthState.Unauthenticated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun login() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_state.value = AuthState.Loading
|
||||||
|
try {
|
||||||
|
val loginUrl = "${ApiConfig.baseUrl}/login?redirect_uri=" +
|
||||||
|
oauthClient.redirectUri()
|
||||||
|
val token = oauthClient.authenticate(loginUrl)
|
||||||
|
if (token != null) {
|
||||||
|
authSettings.setToken(token)
|
||||||
|
val userInfo = apiService.getUserInfo()
|
||||||
|
_state.value = AuthState.Authenticated(userInfo.email)
|
||||||
|
} else {
|
||||||
|
_state.value = AuthState.Unauthenticated
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_state.value = AuthState.Unauthenticated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logout() {
|
||||||
|
authSettings.clear()
|
||||||
|
_state.value = AuthState.Unauthenticated
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package ch.parano.myice.viewmodel
|
||||||
|
|
||||||
|
import ch.parano.myice.models.EventDetail
|
||||||
|
import ch.parano.myice.network.ApiService
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
data class EventDetailState(
|
||||||
|
val eventDetail: EventDetail? = null,
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val error: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
class EventDetailViewModel(
|
||||||
|
private val apiService: ApiService,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(EventDetailState(isLoading = false))
|
||||||
|
val state: StateFlow<EventDetailState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
fun loadEventDetail(gameId: String, account: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_state.value = EventDetailState(isLoading = true)
|
||||||
|
try {
|
||||||
|
val detail = apiService.getGameDetail(gameId, account)
|
||||||
|
_state.value = EventDetailState(eventDetail = detail, isLoading = false)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_state.value = EventDetailState(
|
||||||
|
error = "Failed to load event detail: ${e.message}",
|
||||||
|
isLoading = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package ch.parano.myice.viewmodel
|
||||||
|
|
||||||
|
import ch.parano.myice.auth.AuthSettings
|
||||||
|
import ch.parano.myice.cache.ScheduleCache
|
||||||
|
import ch.parano.myice.filter.TypeFilter
|
||||||
|
import ch.parano.myice.filter.distinctAgegroups
|
||||||
|
import ch.parano.myice.filter.distinctSubgroups
|
||||||
|
import ch.parano.myice.filter.filterEvents
|
||||||
|
import ch.parano.myice.models.Account
|
||||||
|
import ch.parano.myice.models.Event
|
||||||
|
import ch.parano.myice.network.ApiService
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
data class ScheduleState(
|
||||||
|
val accounts: List<Account> = emptyList(),
|
||||||
|
val events: List<Event> = emptyList(),
|
||||||
|
val selectedAccount: String? = null,
|
||||||
|
val selectedAgegroup: String? = null,
|
||||||
|
val selectedSubgroup: String? = null,
|
||||||
|
val typeFilter: TypeFilter = TypeFilter.DEFAULT,
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val isRefreshing: Boolean = false,
|
||||||
|
val error: String? = null,
|
||||||
|
val lastUpdated: Long? = null,
|
||||||
|
) {
|
||||||
|
val filteredEvents: List<Event>
|
||||||
|
get() = filterEvents(events, selectedAgegroup, selectedSubgroup, typeFilter)
|
||||||
|
|
||||||
|
val agegroups: List<String>
|
||||||
|
get() = events.distinctAgegroups()
|
||||||
|
|
||||||
|
val subgroups: List<String>
|
||||||
|
get() = if (selectedAgegroup == null) emptyList()
|
||||||
|
else events.filter { it.agegroup == selectedAgegroup }.distinctSubgroups()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScheduleViewModel(
|
||||||
|
private val apiService: ApiService,
|
||||||
|
private val cache: ScheduleCache,
|
||||||
|
private val authSettings: AuthSettings? = null,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(ScheduleState())
|
||||||
|
val state: StateFlow<ScheduleState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
fun loadAccounts() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val accounts = apiService.getAccounts()
|
||||||
|
val storedAccount = authSettings?.getStoredAccount()
|
||||||
|
var selected = storedAccount
|
||||||
|
|
||||||
|
if (selected == null || accounts.none { it.name == selected }) {
|
||||||
|
selected = accounts.firstOrNull()?.name
|
||||||
|
}
|
||||||
|
|
||||||
|
val (cachedAgegroup, cachedSubgroup) = if (selected != null) {
|
||||||
|
cache.loadFilters(selected)
|
||||||
|
} else null to null
|
||||||
|
|
||||||
|
val cachedTypeFilter = if (selected != null) {
|
||||||
|
cache.loadTypeFilter(selected)?.let { name ->
|
||||||
|
runCatching { TypeFilter.valueOf(name) }.getOrNull()
|
||||||
|
} ?: TypeFilter.DEFAULT
|
||||||
|
} else TypeFilter.DEFAULT
|
||||||
|
|
||||||
|
_state.value = _state.value.copy(
|
||||||
|
accounts = accounts,
|
||||||
|
selectedAccount = selected,
|
||||||
|
selectedAgegroup = cachedAgegroup,
|
||||||
|
selectedSubgroup = cachedSubgroup,
|
||||||
|
typeFilter = cachedTypeFilter,
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_state.value = _state.value.copy(error = "Failed to load accounts: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadCachedSchedule() {
|
||||||
|
val account = _state.value.selectedAccount ?: return
|
||||||
|
|
||||||
|
_state.value = _state.value.copy(isLoading = true, error = null)
|
||||||
|
|
||||||
|
val cachedEvents = cache.loadEvents(account)
|
||||||
|
if (cachedEvents != null) {
|
||||||
|
_state.value = _state.value.copy(
|
||||||
|
events = cachedEvents,
|
||||||
|
lastUpdated = cache.getLastUpdated(account),
|
||||||
|
isLoading = false,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
_state.value = _state.value.copy(events = emptyList(), isLoading = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_state.value.events.isEmpty()) {
|
||||||
|
refreshSchedule()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshSchedule() {
|
||||||
|
val account = _state.value.selectedAccount ?: return
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
_state.value = _state.value.copy(isRefreshing = true, error = null)
|
||||||
|
try {
|
||||||
|
val allEvents = apiService.getSchedule(account)
|
||||||
|
cache.saveEvents(account, allEvents)
|
||||||
|
_state.value = _state.value.copy(
|
||||||
|
events = allEvents,
|
||||||
|
isRefreshing = false,
|
||||||
|
lastUpdated = cache.getLastUpdated(account),
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
_state.value = _state.value.copy(
|
||||||
|
isRefreshing = false,
|
||||||
|
error = "Failed to refresh schedule: ${e.message}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAccount(account: String) {
|
||||||
|
authSettings?.setAccount(account)
|
||||||
|
val (cachedAgegroup, cachedSubgroup) = cache.loadFilters(account)
|
||||||
|
val cachedTypeFilter = cache.loadTypeFilter(account)?.let { name ->
|
||||||
|
runCatching { TypeFilter.valueOf(name) }.getOrNull()
|
||||||
|
} ?: TypeFilter.DEFAULT
|
||||||
|
|
||||||
|
_state.value = _state.value.copy(
|
||||||
|
selectedAccount = account,
|
||||||
|
selectedAgegroup = cachedAgegroup,
|
||||||
|
selectedSubgroup = cachedSubgroup,
|
||||||
|
typeFilter = cachedTypeFilter,
|
||||||
|
)
|
||||||
|
loadCachedSchedule()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setAgegroup(agegroup: String?) {
|
||||||
|
val account = _state.value.selectedAccount
|
||||||
|
if (account != null) {
|
||||||
|
cache.saveFilters(account, agegroup, null)
|
||||||
|
}
|
||||||
|
_state.value = _state.value.copy(selectedAgegroup = agegroup, selectedSubgroup = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSubgroup(subgroup: String?) {
|
||||||
|
val account = _state.value.selectedAccount
|
||||||
|
if (account != null) {
|
||||||
|
cache.saveFilters(account, _state.value.selectedAgegroup, subgroup)
|
||||||
|
}
|
||||||
|
_state.value = _state.value.copy(selectedSubgroup = subgroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTypeFilter(filter: TypeFilter) {
|
||||||
|
val account = _state.value.selectedAccount
|
||||||
|
if (account != null) {
|
||||||
|
cache.saveTypeFilter(account, filter.name)
|
||||||
|
}
|
||||||
|
_state.value = _state.value.copy(typeFilter = filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearCache() {
|
||||||
|
cache.clearAllCache()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
package ch.parano.myice.viewmodel
|
||||||
|
|
||||||
|
import ch.parano.myice.cache.ScheduleCache
|
||||||
|
import ch.parano.myice.filter.TypeFilter
|
||||||
|
import ch.parano.myice.models.Account
|
||||||
|
import ch.parano.myice.models.Event
|
||||||
|
import ch.parano.myice.network.ApiService
|
||||||
|
import com.russhwolf.settings.MapSettings
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.engine.mock.MockEngine
|
||||||
|
import io.ktor.client.engine.mock.MockEngineConfig
|
||||||
|
import io.ktor.client.engine.mock.respond
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.http.headersOf
|
||||||
|
import io.ktor.serialization.kotlinx.json.json
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.resetMain
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import kotlinx.coroutines.test.setMain
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlin.test.AfterTest
|
||||||
|
import kotlin.test.BeforeTest
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
|
||||||
|
class ScheduleViewModelTest {
|
||||||
|
|
||||||
|
@BeforeTest
|
||||||
|
fun setUpDispatcher() {
|
||||||
|
Dispatchers.setMain(UnconfinedTestDispatcher())
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterTest
|
||||||
|
fun tearDownDispatcher() {
|
||||||
|
Dispatchers.resetMain()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
|
private fun mockApi(accountsJson: String, scheduleJson: String): ApiService {
|
||||||
|
val config = MockEngineConfig().apply {
|
||||||
|
addHandler { request ->
|
||||||
|
val path = request.url.encodedPath
|
||||||
|
val response = when {
|
||||||
|
path == "/accounts" -> accountsJson
|
||||||
|
path == "/schedule" -> scheduleJson
|
||||||
|
else -> "[]"
|
||||||
|
}
|
||||||
|
respond(
|
||||||
|
content = response,
|
||||||
|
status = HttpStatusCode.OK,
|
||||||
|
headers = headersOf(HttpHeaders.ContentType, "application/json"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dispatcher = Dispatchers.Unconfined
|
||||||
|
}
|
||||||
|
val client = HttpClient(MockEngine(config)) {
|
||||||
|
install(ContentNegotiation) { json(json) }
|
||||||
|
}
|
||||||
|
return ApiService(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sampleEvent(
|
||||||
|
id: String = "1",
|
||||||
|
agegroup: String = "U13 (Elite)",
|
||||||
|
name: String = "U13 Elite - HC Ajoie",
|
||||||
|
eventType: String = "Jeu",
|
||||||
|
) = Event(
|
||||||
|
idEvent = id, agegroup = agegroup, name = name, title = "Title",
|
||||||
|
opponent = "Opp", place = "Place", start = "2024-11-10T14:00:00",
|
||||||
|
end = "2024-11-10T16:15:00", color = "#e4222e", eventType = eventType,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLoadAccounts() = runTest {
|
||||||
|
val api = mockApi(
|
||||||
|
"""[{"name":"default","label":"Default"}]""",
|
||||||
|
"[]",
|
||||||
|
)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
|
||||||
|
assertEquals(1, vm.state.value.accounts.size)
|
||||||
|
assertEquals("default", vm.state.value.selectedAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRefreshScheduleStoresEvents() = runTest {
|
||||||
|
val scheduleJson = """
|
||||||
|
[{"id_event":"1","agegroup":"U13 (Elite)","name":"U13 Elite - HC Ajoie","title":"T","opponent":"O","place":"P","start":"2024-11-10T14:00:00","end":"2024-11-10T16:15:00","color":"#e4222e","event":"Jeu"}]
|
||||||
|
""".trimIndent()
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", scheduleJson)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.refreshSchedule()
|
||||||
|
|
||||||
|
assertEquals(1, vm.state.value.events.size)
|
||||||
|
assertEquals("1", vm.state.value.events[0].idEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFilteredEventsDefaultGamesOnly() = runTest {
|
||||||
|
val scheduleJson = """
|
||||||
|
[{"id_event":"1","agegroup":"U13","name":"U13 - A","title":"T","opponent":"O","place":"P","start":"s","end":"e","event":"Jeu"},
|
||||||
|
{"id_event":"2","agegroup":"U13","name":"U13 - B","title":"T","opponent":"","place":"P","start":"s","end":"e"}]
|
||||||
|
""".trimIndent()
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", scheduleJson)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.refreshSchedule()
|
||||||
|
|
||||||
|
assertEquals(2, vm.state.value.events.size)
|
||||||
|
assertEquals(1, vm.state.value.filteredEvents.size)
|
||||||
|
assertEquals("1", vm.state.value.filteredEvents[0].idEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetTypeFilterAll() = runTest {
|
||||||
|
val scheduleJson = """
|
||||||
|
[{"id_event":"1","agegroup":"U13","name":"U13 - A","title":"T","opponent":"O","place":"P","start":"s","end":"e","event":"Jeu"},
|
||||||
|
{"id_event":"2","agegroup":"U13","name":"U13 - B","title":"T","opponent":"","place":"P","start":"s","end":"e"}]
|
||||||
|
""".trimIndent()
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", scheduleJson)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.refreshSchedule()
|
||||||
|
vm.setTypeFilter(TypeFilter.ALL)
|
||||||
|
|
||||||
|
assertEquals(2, vm.state.value.filteredEvents.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetAgegroup() = runTest {
|
||||||
|
val scheduleJson = """
|
||||||
|
[{"id_event":"1","agegroup":"U13","name":"U13 - A","title":"T","opponent":"O","place":"P","start":"s","end":"e","event":"Jeu"},
|
||||||
|
{"id_event":"2","agegroup":"U15","name":"U15 - B","title":"T","opponent":"O","place":"P","start":"s","end":"e","event":"Jeu"}]
|
||||||
|
""".trimIndent()
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", scheduleJson)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.refreshSchedule()
|
||||||
|
vm.setAgegroup("U15")
|
||||||
|
|
||||||
|
assertEquals(1, vm.state.value.filteredEvents.size)
|
||||||
|
assertEquals("2", vm.state.value.filteredEvents[0].idEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetSubgroup() = runTest {
|
||||||
|
val scheduleJson = """
|
||||||
|
[{"id_event":"1","agegroup":"U13","name":"U13 Elite - HC Ajoie","title":"T","opponent":"O","place":"P","start":"s","end":"e","event":"Jeu"},
|
||||||
|
{"id_event":"2","agegroup":"U13","name":"U13 Top - HC Geneva","title":"T","opponent":"O","place":"P","start":"s","end":"e","event":"Jeu"}]
|
||||||
|
""".trimIndent()
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", scheduleJson)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.refreshSchedule()
|
||||||
|
vm.setSubgroup("U13 Elite")
|
||||||
|
|
||||||
|
assertEquals(1, vm.state.value.filteredEvents.size)
|
||||||
|
assertEquals("1", vm.state.value.filteredEvents[0].idEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCacheLoadOnLoadCachedSchedule() = runTest {
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", "[]")
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
cache.saveEvents("default", listOf(sampleEvent("1")))
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.loadCachedSchedule()
|
||||||
|
|
||||||
|
assertEquals(1, vm.state.value.events.size)
|
||||||
|
assertFalse(vm.state.value.isLoading)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAgegroupsComputed() = runTest {
|
||||||
|
val scheduleJson = """
|
||||||
|
[{"id_event":"1","agegroup":"U15","name":"N","title":"T","opponent":"","place":"P","start":"s","end":"e","event":"Jeu"},
|
||||||
|
{"id_event":"2","agegroup":"U13","name":"N","title":"T","opponent":"","place":"P","start":"s","end":"e","event":"Jeu"}]
|
||||||
|
""".trimIndent()
|
||||||
|
val api = mockApi("""[{"name":"default","label":"Default"}]""", scheduleJson)
|
||||||
|
val cache = ScheduleCache(MapSettings())
|
||||||
|
val vm = ScheduleViewModel(api, cache)
|
||||||
|
|
||||||
|
vm.loadAccounts()
|
||||||
|
vm.refreshSchedule()
|
||||||
|
|
||||||
|
assertEquals(listOf("U13", "U15"), vm.state.value.agegroups)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user