1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-19 21:39:08 -05:00

Wrap delete and save operations in database transactions (#2450)

This commit is contained in:
Mikołaj Pich 2024-03-02 17:25:27 +01:00 committed by GitHub
parent fb240938ed
commit 333306e7ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 587 additions and 366 deletions

View File

@ -2,24 +2,14 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.AdminMessage
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@Dao
abstract class AdminMessageDao : BaseDao<AdminMessage> {
interface AdminMessageDao : BaseDao<AdminMessage> {
@Query("SELECT * FROM AdminMessages")
abstract fun loadAll(): Flow<List<AdminMessage>>
@Transaction
open suspend fun removeOldAndSaveNew(
oldMessages: List<AdminMessage>,
newMessages: List<AdminMessage>
) {
deleteAll(oldMessages)
insertAll(newMessages)
}
fun loadAll(): Flow<List<AdminMessage>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Transaction
import androidx.room.Update
interface BaseDao<T> {
@ -15,4 +16,10 @@ interface BaseDao<T> {
@Delete
suspend fun deleteAll(items: List<T>)
@Transaction
suspend fun removeOldAndSaveNew(oldItems: List<T>, newItems: List<T>) {
deleteAll(oldItems)
insertAll(newItems)
}
}

View File

@ -65,12 +65,13 @@ class AttendanceRepository @Inject constructor(
.mapToEntities(semester, lessons)
},
saveFetchResult = { old, new ->
attendanceDb.deleteAll(old uniqueSubtract new)
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
newAttendance.apply { if (notify) isNotified = false }
}
attendanceDb.insertAll(attendanceToAdd)
attendanceDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = attendanceToAdd,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.data.repositories
import androidx.room.withTransaction
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
@ -20,6 +22,7 @@ class AttendanceSummaryRepository @Inject constructor(
private val attendanceDb: AttendanceSummaryDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
private val appDatabase: AppDatabase,
) {
private val saveFetchResultMutex = Mutex()
@ -46,8 +49,10 @@ class AttendanceSummaryRepository @Inject constructor(
.mapToEntities(semester, subjectId)
},
saveFetchResult = { old, new ->
attendanceDb.deleteAll(old uniqueSubtract new)
attendanceDb.insertAll(new uniqueSubtract old)
appDatabase.withTransaction {
attendanceDb.deleteAll(old uniqueSubtract new)
attendanceDb.insertAll(new uniqueSubtract old)
}
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -6,7 +6,13 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.switchSemester
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@ -53,8 +59,10 @@ class CompletedLessonsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
completedLessonsDb.deleteAll(old uniqueSubtract new)
completedLessonsDb.insertAll(new uniqueSubtract old)
completedLessonsDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }

View File

@ -53,12 +53,12 @@ class ConferenceRepository @Inject constructor(
.filter { it.date >= startDate }
},
saveFetchResult = { old, new ->
val conferencesToSave = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
}
conferenceDb.deleteAll(old uniqueSubtract new)
conferenceDb.insertAll(conferencesToSave)
conferenceDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
},
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -62,12 +62,12 @@ class ExamRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
val examsToSave = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
}
examDb.deleteAll(old uniqueSubtract new)
examDb.insertAll(examsToSave)
examDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
},
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }

View File

@ -87,10 +87,12 @@ class GradeRepository @Inject constructor(
new: List<GradeDescriptive>,
notify: Boolean
) {
gradeDescriptiveDb.deleteAll(old uniqueSubtract new)
gradeDescriptiveDb.insertAll((new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
})
gradeDescriptiveDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
},
)
}
private suspend fun refreshGradeDetails(
@ -101,13 +103,16 @@ class GradeRepository @Inject constructor(
) {
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
?: student.registrationDate.toLocalDate()
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
isRead = false
if (notify) isNotified = false
}
})
gradeDb.removeOldAndSaveNew(
oldItems = oldGrades uniqueSubtract newDetails,
newItems = (newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
isRead = false
if (notify) isNotified = false
}
},
)
}
private suspend fun refreshGradeSummaries(
@ -115,31 +120,43 @@ class GradeRepository @Inject constructor(
newSummary: List<GradeSummary>,
notify: Boolean
) {
gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary)
gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary ->
val oldSummary = oldSummaries.find { old -> old.subject == summary.subject }
summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
else -> true
}
summary.isFinalGradeNotified = when {
summary.finalGrade.isEmpty() -> true
notify && oldSummary?.finalGrade != summary.finalGrade -> false
else -> true
}
gradeSummaryDb.removeOldAndSaveNew(
oldItems = oldSummaries uniqueSubtract newSummary,
newItems = (newSummary uniqueSubtract oldSummaries).onEach { summary ->
getGradeSummaryWithUpdatedNotificationState(
summary = summary,
oldSummary = oldSummaries.find { it.subject == summary.subject },
notify = notify,
)
},
)
}
summary.predictedGradeLastChange = when {
oldSummary == null -> Instant.now()
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
else -> oldSummary.predictedGradeLastChange
}
summary.finalGradeLastChange = when {
oldSummary == null -> Instant.now()
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
else -> oldSummary.finalGradeLastChange
}
})
private fun getGradeSummaryWithUpdatedNotificationState(
summary: GradeSummary,
oldSummary: GradeSummary?,
notify: Boolean,
) {
summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
else -> true
}
summary.isFinalGradeNotified = when {
summary.finalGrade.isEmpty() -> true
notify && oldSummary?.finalGrade != summary.finalGrade -> false
else -> true
}
summary.predictedGradeLastChange = when {
oldSummary == null -> Instant.now()
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
else -> oldSummary.predictedGradeLastChange
}
summary.finalGradeLastChange = when {
oldSummary == null -> Instant.now()
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
else -> oldSummary.finalGradeLastChange
}
}
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {

View File

@ -19,7 +19,7 @@ import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.switchSemester
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import java.util.*
import java.util.Locale
import javax.inject.Inject
import javax.inject.Singleton
@ -62,8 +62,10 @@ class GradeStatisticsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
gradePartialStatisticsDb.deleteAll(old uniqueSubtract new)
gradePartialStatisticsDb.insertAll(new uniqueSubtract old)
gradePartialStatisticsDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
},
mapResult = { items ->
@ -80,6 +82,7 @@ class GradeStatisticsRepository @Inject constructor(
)
listOf(summaryItem) + items
}
else -> items.filter { it.subject == subjectName }
}.mapPartialToStatisticItems()
}
@ -107,8 +110,10 @@ class GradeStatisticsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new)
gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old)
gradeSemesterStatisticsDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
},
mapResult = { items ->
@ -138,6 +143,7 @@ class GradeStatisticsRepository @Inject constructor(
}
listOf(summaryItem) + itemsWithAverage
}
else -> itemsWithAverage.filter { it.subject == subjectName }
}.mapSemesterToStatisticItems()
}
@ -163,8 +169,10 @@ class GradeStatisticsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
gradePointsStatisticsDb.deleteAll(old uniqueSubtract new)
gradePointsStatisticsDb.insertAll(new uniqueSubtract old)
gradePointsStatisticsDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
},
mapResult = { items ->

View File

@ -61,14 +61,14 @@ class HomeworkRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
val homeWorkToSave = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
}
val filteredOld = old.filterNot { it.isAddedByUser }
homeworkDb.deleteAll(filteredOld uniqueSubtract new)
homeworkDb.insertAll(homeWorkToSave)
homeworkDb.removeOldAndSaveNew(
oldItems = filteredOld uniqueSubtract new,
newItems = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
},
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
}
)

View File

@ -18,7 +18,7 @@ import javax.inject.Singleton
@Singleton
class LuckyNumberRepository @Inject constructor(
private val luckyNumberDb: LuckyNumberDao,
private val sdk: Sdk
private val sdk: Sdk,
) {
private val saveFetchResultMutex = Mutex()
@ -39,11 +39,10 @@ class LuckyNumberRepository @Inject constructor(
newLuckyNumber ?: return@networkBoundResource
if (newLuckyNumber != oldLuckyNumber) {
val updatedLuckNumberList =
listOf(newLuckyNumber.apply { if (notify) isNotified = false })
oldLuckyNumber?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) }
luckyNumberDb.insertAll(updatedLuckNumberList)
luckyNumberDb.removeOldAndSaveNew(
oldItems = listOfNotNull(oldLuckyNumber),
newItems = listOf(newLuckyNumber.apply { if (notify) isNotified = false }),
)
}
}
)

View File

@ -89,12 +89,13 @@ class MessageRepository @Inject constructor(
},
saveFetchResult = { oldWithAuthors, new ->
val old = oldWithAuthors.map { it.message }
messagesDb.deleteAll(old uniqueSubtract new)
messagesDb.insertAll((new uniqueSubtract old).onEach {
val muted = isMuted(it.correspondents)
it.isNotified = !notify || muted
})
messagesDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = (new uniqueSubtract old).onEach {
val muted = isMuted(it.correspondents)
it.isNotified = !notify || muted
},
)
refreshHelper.updateLastRefreshTimestamp(
getRefreshKey(messagesCacheKey, mailbox, folder)
)

View File

@ -48,9 +48,10 @@ class MobileDeviceRepository @Inject constructor(
.mapToEntities(student)
},
saveFetchResult = { old, new ->
mobileDb.deleteAll(old uniqueSubtract new)
mobileDb.insertAll(new uniqueSubtract old)
mobileDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
)

View File

@ -7,7 +7,12 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.switchSemester
import io.github.wulkanowy.utils.toLocalDate
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
@ -46,14 +51,16 @@ class NoteRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
noteDb.deleteAll(old uniqueSubtract new)
noteDb.insertAll((new uniqueSubtract old).onEach {
val notesToAdd = (new uniqueSubtract old).onEach {
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
isRead = false
if (notify) isNotified = false
}
})
}
noteDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = notesToAdd,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -1,7 +1,11 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
@ -25,8 +29,10 @@ class RecipientRepository @Inject constructor(
.mapToEntities(mailbox.globalKey)
val old = recipientDb.loadAll(type, mailbox.globalKey)
recipientDb.deleteAll(old uniqueSubtract new)
recipientDb.insertAll(new uniqueSubtract old)
recipientDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}

View File

@ -47,12 +47,12 @@ class SchoolAnnouncementRepository @Inject constructor(
lastAnnouncements + directorInformation
},
saveFetchResult = { old, new ->
val schoolAnnouncementsToSave = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
}
schoolAnnouncementDb.deleteAll(old uniqueSubtract new)
schoolAnnouncementDb.insertAll(schoolAnnouncementsToSave)
schoolAnnouncementDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = (new uniqueSubtract old).onEach {
if (notify) it.isNotified = false
},
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
)

View File

@ -47,10 +47,10 @@ class SchoolRepository @Inject constructor(
},
saveFetchResult = { old, new ->
if (old != null && new != old) {
with(schoolDb) {
deleteAll(listOf(old))
insertAll(listOf(new))
}
schoolDb.removeOldAndSaveNew(
oldItems = listOf(old),
newItems = listOf(new)
)
} else if (old == null) {
schoolDb.insertAll(listOf(new))
}

View File

@ -5,7 +5,11 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@ -15,7 +19,7 @@ import javax.inject.Singleton
class SemesterRepository @Inject constructor(
private val semesterDb: SemesterDao,
private val sdk: Sdk,
private val dispatchers: DispatchersProvider
private val dispatchers: DispatchersProvider,
) {
suspend fun getSemesters(
@ -45,6 +49,7 @@ class SemesterRepository @Inject constructor(
0 == it.diaryId && 0 == it.kindergartenDiaryId
} == true
}
else -> false
}
@ -59,8 +64,10 @@ class SemesterRepository @Inject constructor(
if (new.isEmpty()) return Timber.i("Empty semester list!")
val old = semesterDb.loadAll(student.studentId, student.classId)
semesterDb.deleteAll(old.uniqueSubtract(new))
semesterDb.insertSemesters(new.uniqueSubtract(old))
semesterDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
}
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =

View File

@ -15,7 +15,7 @@ import javax.inject.Singleton
@Singleton
class StudentInfoRepository @Inject constructor(
private val studentInfoDao: StudentInfoDao,
private val sdk: Sdk
private val sdk: Sdk,
) {
private val saveFetchResultMutex = Mutex()
@ -36,10 +36,10 @@ class StudentInfoRepository @Inject constructor(
},
saveFetchResult = { old, new ->
if (old != null && new != old) {
with(studentInfoDao) {
deleteAll(listOf(old))
insertAll(listOf(new))
}
studentInfoDao.removeOldAndSaveNew(
oldItems = listOf(old),
newItems = listOf(new),
)
} else if (old == null) {
studentInfoDao.insertAll(listOf(new))
}

View File

@ -45,9 +45,10 @@ class SubjectRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
subjectDao.deleteAll(old uniqueSubtract new)
subjectDao.insertAll(new uniqueSubtract old)
subjectDao.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -45,9 +45,10 @@ class TeacherRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
teacherDb.deleteAll(old uniqueSubtract new)
teacherDb.insertAll(new uniqueSubtract old)
teacherDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -154,8 +154,10 @@ class TimetableRepository @Inject constructor(
new.apply { if (notify) isNotified = false }
}
timetableDb.deleteAll(lessonsToRemove)
timetableDb.insertAll(lessonsToAdd)
timetableDb.removeOldAndSaveNew(
oldItems = lessonsToRemove,
newItems = lessonsToAdd,
)
schedulerHelper.cancelScheduled(lessonsToRemove, student)
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
@ -166,13 +168,17 @@ class TimetableRepository @Inject constructor(
new: List<TimetableAdditional>
) {
val oldFiltered = old.filter { !it.isAddedByUser }
timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
timetableAdditionalDb.insertAll(new uniqueSubtract old)
timetableAdditionalDb.removeOldAndSaveNew(
oldItems = oldFiltered uniqueSubtract new,
newItems = new uniqueSubtract old,
)
}
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
timetableHeaderDb.deleteAll(old uniqueSubtract new)
timetableHeaderDb.insertAll(new uniqueSubtract old)
timetableHeaderDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,
)
}
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {

View File

@ -10,11 +10,17 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.mockk.*
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@ -61,26 +67,36 @@ class AttendanceRepositoryTest {
}
@Test
fun `force refresh without difference`() {
fun `force refresh without difference`() = runTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
coEvery { attendanceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
val res = attendanceRepository.getAttendance(
student = student,
semester = semester,
start = startDate,
end = endDate,
forceRefresh = true,
).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
coVerify {
attendanceDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match { it.isEmpty() },
)
}
}
@Test
@ -89,14 +105,23 @@ class AttendanceRepositoryTest {
coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())),
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result
flowOf(
remoteList.dropLast(1).mapToEntities(semester, emptyList())
), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
coEvery { attendanceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
val res = runBlocking {
attendanceRepository.getAttendance(
student,
semester,
startDate,
endDate,
true
).toFirstResult()
}
// verify
assertEquals(null, res.errorOrNull)
@ -104,11 +129,13 @@ class AttendanceRepositoryTest {
coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify {
attendanceDb.insertAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
})
attendanceDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
},
)
}
coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
}
@Test
@ -117,25 +144,39 @@ class AttendanceRepositoryTest {
coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList.dropLast(1)
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result
flowOf(
remoteList.mapToEntities(
semester,
emptyList()
)
), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
coEvery { attendanceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
val res = runBlocking {
attendanceRepository.getAttendance(
student,
semester,
startDate,
endDate,
true
).toFirstResult()
}
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify {
attendanceDb.deleteAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
})
attendanceDb.removeOldAndSaveNew(
oldItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
},
newItems = emptyList(),
)
}
}

View File

@ -9,11 +9,16 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.mockk.*
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@ -52,46 +57,28 @@ class CompletedLessonsRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
completedLessonRepository = CompletedLessonsRepository(completedLessonDb, sdk, refreshHelper)
completedLessonRepository =
CompletedLessonsRepository(completedLessonDb, sdk, refreshHelper)
}
@Test
fun `force refresh without difference`() {
fun `force refresh without difference`() = runTest {
// prepare
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
)
coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { completedLessonDb.deleteAll(any()) } just Runs
coEvery { completedLessonDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify { completedLessonDb.insertAll(match { it.isEmpty() }) }
coVerify { completedLessonDb.deleteAll(match { it.isEmpty() }) }
}
@Test
fun `force refresh with more items in remote`() {
// prepare
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { completedLessonDb.deleteAll(any()) } just Runs
// execute
val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
val res = completedLessonRepository.getCompletedLessons(
student = student,
semester = semester,
start = startDate,
end = endDate,
forceRefresh = true,
).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
@ -99,15 +86,52 @@ class CompletedLessonsRepositoryTest {
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify {
completedLessonDb.insertAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
})
completedLessonDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match { it.isEmpty() },
)
}
coVerify { completedLessonDb.deleteAll(match { it.isEmpty() }) }
}
@Test
fun `force refresh with more items in local`() {
fun `force refresh with more items in remote`() = runTest {
// prepare
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
flowOf(
remoteList.dropLast(1).mapToEntities(semester)
), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
coEvery { completedLessonDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = completedLessonRepository.getCompletedLessons(
student = student,
semester = semester,
start = startDate,
end = endDate,
forceRefresh = true
).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify {
completedLessonDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
}
)
}
}
@Test
fun `force refresh with more items in local`() = runTest {
// prepare
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList.dropLast(1)
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
@ -115,22 +139,29 @@ class CompletedLessonsRepositoryTest {
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { completedLessonDb.deleteAll(any()) } just Runs
coEvery { completedLessonDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
val res = completedLessonRepository.getCompletedLessons(
student = student,
semester = semester,
start = startDate,
end = endDate,
forceRefresh = true,
).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify { completedLessonDb.insertAll(match { it.isEmpty() }) }
coVerify {
completedLessonDb.deleteAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
})
completedLessonDb.removeOldAndSaveNew(
oldItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
},
newItems = match { it.isEmpty() },
)
}
}

View File

@ -9,11 +9,17 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.mockk.*
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@ -64,35 +70,42 @@ class ExamRemoteTest {
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
)
coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { examDb.deleteAll(any()) } just Runs
coEvery { examDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
val res = runBlocking {
examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult()
}
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify { examDb.insertAll(match { it.isEmpty() }) }
coVerify { examDb.deleteAll(match { it.isEmpty() }) }
coVerify { examDb.removeOldAndSaveNew(emptyList(), emptyList()) }
}
@Test
fun `force refresh with more items in remote`() {
fun `force refresh with more items in remote`() = runTest {
// prepare
coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
flowOf(
remoteList.dropLast(1).mapToEntities(semester)
), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { examDb.deleteAll(any()) } just Runs
coEvery { examDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
val res = examRepository.getExams(
student = student,
semester = semester,
start = startDate,
end = endDate,
forceRefresh = true,
).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
@ -100,15 +113,17 @@ class ExamRemoteTest {
coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify {
examDb.insertAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
})
examDb.removeOldAndSaveNew(
oldItems = emptyList(),
newItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
},
)
}
coVerify { examDb.deleteAll(match { it.isEmpty() }) }
}
@Test
fun `force refresh with more items in local`() {
fun `force refresh with more items in local`() = runTest {
// prepare
coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList.dropLast(1)
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
@ -116,22 +131,27 @@ class ExamRemoteTest {
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { examDb.deleteAll(any()) } just Runs
coEvery { examDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
val res = examRepository.getExams(
student = student,
semester = semester,
start = startDate,
end = endDate,
forceRefresh = true,
).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify { examDb.insertAll(match { it.isEmpty() }) }
coVerify {
examDb.deleteAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
})
examDb.removeOldAndSaveNew(
oldItems = match { it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1] },
newItems = emptyList()
)
}
}

View File

@ -22,6 +22,7 @@ import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@ -60,26 +61,27 @@ class GradeRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
gradeRepository =
GradeRepository(gradeDb, gradeSummaryDb, gradeDescriptiveDb, sdk, refreshHelper)
gradeRepository = GradeRepository(
gradeDb = gradeDb,
gradeSummaryDb = gradeSummaryDb,
gradeDescriptiveDb = gradeDescriptiveDb,
sdk = sdk,
refreshHelper = refreshHelper,
)
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()
coEvery { gradeDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { gradeSummaryDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()),
flowOf(listOf()),
flowOf(listOf())
)
coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
coEvery { gradeDescriptiveDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { gradeDescriptiveDb.loadAll(any(), any()) } returnsMany listOf(
flowOf(listOf()),
)
coEvery { gradeDescriptiveDb.deleteAll(any()) } just Runs
coEvery { gradeDescriptiveDb.insertAll(any()) } returns listOf()
}
@Test
@ -113,13 +115,16 @@ class GradeRepositoryTest {
assertEquals(null, res.errorOrNull)
assertEquals(4, res.dataOrNull?.first?.size)
coVerify {
gradeDb.insertAll(withArg {
assertEquals(4, it.size)
assertTrue(it[0].isRead)
assertTrue(it[1].isRead)
assertFalse(it[2].isRead)
assertFalse(it[3].isRead)
})
gradeDb.removeOldAndSaveNew(
oldItems = emptyList(),
newItems = withArg {
assertEquals(4, it.size)
assertTrue(it[0].isRead)
assertTrue(it[1].isRead)
assertFalse(it[2].isRead)
assertFalse(it[3].isRead)
},
)
}
}
@ -167,23 +172,23 @@ class GradeRepositoryTest {
assertEquals(null, res.errorOrNull)
assertEquals(4, res.dataOrNull?.first?.size)
coVerify {
gradeDb.insertAll(withArg {
assertEquals(3, it.size)
assertTrue(it[0].isRead)
assertTrue(it[1].isRead)
assertFalse(it[2].isRead)
assertEquals(remoteList.mapToEntities(semester).last(), it[2])
})
}
coVerify {
gradeDb.deleteAll(withArg {
assertEquals(2, it.size)
})
gradeDb.removeOldAndSaveNew(
oldItems = withArg {
assertEquals(2, it.size)
},
newItems = withArg {
assertEquals(3, it.size)
assertTrue(it[0].isRead)
assertTrue(it[1].isRead)
assertFalse(it[2].isRead)
assertEquals(remoteList.mapToEntities(semester).last(), it[2])
}
)
}
}
@Test
fun `force refresh when local contains duplicated grades`() {
fun `force refresh when local contains duplicated grades`() = runTest {
// prepare
val remoteList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
@ -203,13 +208,17 @@ class GradeRepositoryTest {
)
// execute
val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
val res = gradeRepository.getGrades(student, semester, true).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.first?.size)
coVerify { gradeDb.insertAll(match { it.isEmpty() }) }
coVerify { gradeDb.deleteAll(match { it.size == 1 }) } // ... here
coVerify {
gradeDb.removeOldAndSaveNew(
oldItems = match { it.size == 1 }, // ... here
newItems = emptyList()
)
}
}
@Test
@ -238,8 +247,12 @@ class GradeRepositoryTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(3, res.dataOrNull?.first?.size)
coVerify { gradeDb.insertAll(match { it.size == 1 }) } // ... here
coVerify { gradeDb.deleteAll(match { it.isEmpty() }) }
coVerify {
gradeDb.removeOldAndSaveNew(
oldItems = emptyList(),
newItems = match { it.size == 1 }, // ... here
)
}
}
@Test

View File

@ -71,8 +71,7 @@ class GradeStatisticsRepositoryTest {
flowOf(remotePartialList.mapToEntities(semester)),
flowOf(remotePartialList.mapToEntities(semester))
)
coEvery { gradePartialStatisticsDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
coEvery { gradePartialStatisticsDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@ -93,8 +92,7 @@ class GradeStatisticsRepositoryTest {
assertEquals("", items[2].partial?.studentAverage)
coVerify { sdk.getGradesPartialStatistics(1) }
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
coVerify { gradePartialStatisticsDb.removeOldAndSaveNew(emptyList(), emptyList()) }
}
@Test
@ -109,8 +107,7 @@ class GradeStatisticsRepositoryTest {
flowOf(remotePartialList.mapToEntities(semester)),
flowOf(remotePartialList.mapToEntities(semester))
)
coEvery { gradePartialStatisticsDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
coEvery { gradePartialStatisticsDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@ -131,8 +128,7 @@ class GradeStatisticsRepositoryTest {
assertEquals("5.0", items[2].partial?.studentAverage)
coVerify { sdk.getGradesPartialStatistics(1) }
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
coVerify { gradePartialStatisticsDb.removeOldAndSaveNew(emptyList(), emptyList()) }
}
private fun getGradeStatisticsPartialSubject(

View File

@ -7,11 +7,16 @@ import io.github.wulkanowy.data.mappers.mapToEntity
import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.mockk.*
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@ -53,7 +58,8 @@ class LuckyNumberRemoteTest {
coEvery { luckyNumberDb.deleteAll(any()) } just Runs
// execute
val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
val res =
runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
assertEquals(null, res.errorOrNull)
@ -65,19 +71,19 @@ class LuckyNumberRemoteTest {
}
@Test
fun `force refresh with different item on remote`() {
fun `force refresh with different item on remote`() = runTest {
// prepare
coEvery { sdk.getLuckyNumber(student.schoolShortName) } returns luckyNumber
coEvery { luckyNumberDb.load(1, date) } returnsMany listOf(
flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)),
flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)), // after fetch end before save result
// after fetch end before save result
flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)),
flowOf(luckyNumber.mapToEntity(student))
)
coEvery { luckyNumberDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { luckyNumberDb.deleteAll(any()) } just Runs
coEvery { luckyNumberDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
val res = luckyNumberRepository.getLuckyNumber(student, true).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
@ -85,13 +91,16 @@ class LuckyNumberRemoteTest {
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify {
luckyNumberDb.insertAll(match {
it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
})
luckyNumberDb.removeOldAndSaveNew(
oldItems = match {
it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
.copy(luckyNumber = 6666)
},
newItems = match {
it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
}
)
}
coVerify { luckyNumberDb.deleteAll(match {
it.size == 1 && it[0] == luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)
}) }
}
@Test
@ -103,11 +112,11 @@ class LuckyNumberRemoteTest {
flowOf(null), // after fetch end before save result
flowOf(luckyNumber.mapToEntity(student))
)
coEvery { luckyNumberDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { luckyNumberDb.deleteAll(any()) } just Runs
coEvery { luckyNumberDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
val res =
runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
assertEquals(null, res.errorOrNull)
@ -115,10 +124,12 @@ class LuckyNumberRemoteTest {
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify {
luckyNumberDb.insertAll(match {
it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
})
luckyNumberDb.removeOldAndSaveNew(
oldItems = emptyList(),
newItems = match {
it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
}
)
}
coVerify(exactly = 0) { luckyNumberDb.deleteAll(any()) }
}
}

View File

@ -113,7 +113,7 @@ class MessageRepositoryTest {
}
@Test
fun `get messages when fetched completely new message without notify`() = runBlocking {
fun `get messages when fetched completely new message without notify`() = runTest {
coEvery { mailboxDao.loadAll(any()) } returns listOf(mailbox)
every { messageDb.loadAll(mailbox.globalKey, any()) } returns flowOf(emptyList())
coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf(
@ -122,8 +122,7 @@ class MessageRepositoryTest {
readBy = 10,
)
)
coEvery { messageDb.deleteAll(any()) } just Runs
coEvery { messageDb.insertAll(any()) } returns listOf()
coEvery { messageDb.removeOldAndSaveNew(any(), any()) } just Runs
val res = repository.getMessages(
student = student,
@ -134,12 +133,14 @@ class MessageRepositoryTest {
).toFirstResult()
assertEquals(null, res.errorOrNull)
coVerify(exactly = 1) { messageDb.deleteAll(withArg { checkEquals(emptyList<Message>()) }) }
coVerify {
messageDb.insertAll(withArg {
assertEquals(4, it.single().messageId)
assertTrue(it.single().isNotified)
})
messageDb.removeOldAndSaveNew(
oldItems = withArg { checkEquals(emptyList<Message>()) },
newItems = withArg {
assertEquals(4, it.single().messageId)
assertTrue(it.single().isNotified)
},
)
}
}

View File

@ -19,7 +19,7 @@ import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@ -57,42 +57,21 @@ class MobileDeviceRepositoryTest {
}
@Test
fun `force refresh without difference`() {
fun `force refresh without difference`() = runTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(student.studentId) } returnsMany listOf(
flowOf(remoteList.mapToEntities(student)),
flowOf(remoteList.mapToEntities(student))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
coEvery { mobileDeviceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
// verify
Assert.assertEquals(null, res.errorOrNull)
Assert.assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
coVerify { mobileDeviceDb.deleteAll(match { it.isEmpty() }) }
}
@Test
fun `force refresh with more items in remote`() {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(student)),
flowOf(remoteList.dropLast(1).mapToEntities(student)), // after fetch end before save result
flowOf(remoteList.mapToEntities(student))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
// execute
val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
val res = mobileDeviceRepository.getDevices(
student = student,
semester = semester,
forceRefresh = true,
).toFirstResult()
// verify
Assert.assertEquals(null, res.errorOrNull)
@ -100,15 +79,50 @@ class MobileDeviceRepositoryTest {
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify {
mobileDeviceDb.insertAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
})
mobileDeviceDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match { it.isEmpty() },
)
}
coVerify { mobileDeviceDb.deleteAll(match { it.isEmpty() }) }
}
@Test
fun `force refresh with more items in local`() {
fun `force refresh with more items in remote`() = runTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(student)),
flowOf(
remoteList.dropLast(1).mapToEntities(student)
), // after fetch end before save result
flowOf(remoteList.mapToEntities(student))
)
coEvery { mobileDeviceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = mobileDeviceRepository.getDevices(
student = student,
semester = semester,
forceRefresh = true,
).toFirstResult()
// verify
Assert.assertEquals(null, res.errorOrNull)
Assert.assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify {
mobileDeviceDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
},
)
}
}
@Test
fun `force refresh with more items in local`() = runTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList.dropLast(1)
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
@ -116,22 +130,27 @@ class MobileDeviceRepositoryTest {
flowOf(remoteList.mapToEntities(student)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(student))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
coEvery { mobileDeviceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
val res = mobileDeviceRepository.getDevices(
student = student,
semester = semester,
forceRefresh = true,
).toFirstResult()
// verify
Assert.assertEquals(null, res.errorOrNull)
Assert.assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
coVerify {
mobileDeviceDb.deleteAll(match {
it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
})
mobileDeviceDb.removeOldAndSaveNew(
oldItems = match {
it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
},
newItems = match { it.isEmpty() },
)
}
}

View File

@ -69,7 +69,12 @@ class RecipientLocalTest {
@Test
fun `load recipients when items already in database`() {
// prepare
coEvery { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") } returnsMany listOf(
coEvery {
recipientDb.loadAll(
io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
"v4"
)
} returnsMany listOf(
remoteList.mapToEntities("v4"),
remoteList.mapToEntities("v4")
)
@ -108,8 +113,7 @@ class RecipientLocalTest {
emptyList(),
remoteList.mapToEntities("v4")
)
coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { recipientDb.deleteAll(any()) } just Runs
coEvery { recipientDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@ -123,8 +127,12 @@ class RecipientLocalTest {
// verify
assertEquals(3, res.size)
coVerify { sdk.getRecipients("v4") }
coVerify { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") }
coVerify { recipientDb.insertAll(match { it.isEmpty() }) }
coVerify { recipientDb.deleteAll(match { it.isEmpty() }) }
coVerify {
recipientDb.loadAll(
io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
"v4"
)
}
coVerify { recipientDb.removeOldAndSaveNew(match { it.isEmpty() }, match { it.isEmpty() }) }
}
}

View File

@ -50,13 +50,16 @@ class SemesterRepositoryTest {
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns semesters
coEvery { semesterDb.deleteAll(any()) } just Runs
coEvery { semesterDb.insertSemesters(any()) } returns emptyList()
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
runBlocking { semesterRepository.getSemesters(student) }
coVerify { semesterDb.insertSemesters(semesters.mapToEntities(student.studentId)) }
coVerify { semesterDb.deleteAll(emptyList()) }
coVerify {
semesterDb.removeOldAndSaveNew(
oldItems = emptyList(),
newItems = semesters.mapToEntities(student.studentId),
)
}
}
@Test
@ -71,12 +74,17 @@ class SemesterRepositoryTest {
getSemesterPojo(123, 2, now().minusMonths(3), now())
)
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns badSemesters.mapToEntities(student.studentId)
coEvery {
semesterDb.loadAll(
student.studentId,
student.classId
)
} returns badSemesters.mapToEntities(student.studentId)
coEvery { sdk.getSemesters() } returns goodSemesters
coEvery { semesterDb.deleteAll(any()) } just Runs
coEvery { semesterDb.insertSemesters(any()) } returns listOf()
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
val items = runBlocking { semesterRepository.getSemesters(student.copy(loginMode = Sdk.Mode.HEBE.name)) }
val items =
runBlocking { semesterRepository.getSemesters(student.copy(loginMode = Sdk.Mode.HEBE.name)) }
assertEquals(2, items.size)
assertEquals(0, items[0].diaryId)
}
@ -99,8 +107,7 @@ class SemesterRepositoryTest {
goodSemesters.mapToEntities(student.studentId)
)
coEvery { sdk.getSemesters() } returns goodSemesters
coEvery { semesterDb.deleteAll(any()) } just Runs
coEvery { semesterDb.insertSemesters(any()) } returns listOf()
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
val items = semesterRepository.getSemesters(
student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
@ -157,13 +164,16 @@ class SemesterRepositoryTest {
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns semesters
coEvery { semesterDb.deleteAll(any()) } just Runs
coEvery { semesterDb.insertSemesters(any()) } returns listOf()
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
coVerify { semesterDb.deleteAll(emptyList()) }
coVerify { semesterDb.insertSemesters(semesters.mapToEntities(student.studentId)) }
coVerify {
semesterDb.removeOldAndSaveNew(
oldItems = emptyList(),
newItems = semesters.mapToEntities(student.studentId),
)
}
}
@Test
@ -181,12 +191,17 @@ class SemesterRepositoryTest {
getSemesterPojo(2, 2, now().plusMonths(5), now().plusMonths(11)),
)
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semestersWithNoCurrent
coEvery {
semesterDb.loadAll(
student.studentId,
student.classId
)
} returns semestersWithNoCurrent
coEvery { sdk.getSemesters() } returns newSemesters
coEvery { semesterDb.deleteAll(any()) } just Runs
coEvery { semesterDb.insertSemesters(any()) } returns listOf()
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
val items = runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
val items =
runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
assertEquals(2, items.size)
}

View File

@ -108,8 +108,7 @@ class TimetableRepositoryTest {
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
)
coEvery { timetableDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { timetableDb.deleteAll(any()) } just Runs
coEvery { timetableDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery {
timetableAdditionalDao.loadAll(
@ -119,12 +118,10 @@ class TimetableRepositoryTest {
end = endDate
)
} returns flowOf(listOf())
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableAdditionalDao.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
coEvery { timetableHeaderDao.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@ -142,8 +139,12 @@ class TimetableRepositoryTest {
assertEquals(2, res.dataOrNull!!.lessons.size)
coVerify { sdk.getTimetable(startDate, endDate) }
coVerify { timetableDb.loadAll(1, 1, startDate, endDate) }
coVerify { timetableDb.insertAll(match { it.isEmpty() }) }
coVerify { timetableDb.deleteAll(match { it.isEmpty() }) }
coVerify {
timetableDb.removeOldAndSaveNew(
oldItems = match { it.isEmpty() },
newItems = match { it.isEmpty() },
)
}
}
private fun createTimetableRemote(