Merge pull request 'Repository Unit Tests' (#50) from davidoskky/ReaderForSelfoss-multiplatform:repository_tests into master
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/50
This commit is contained in:
commit
6ec3e96909
@ -15,6 +15,7 @@ steps:
|
|||||||
- echo "---------------------------------------------------------"
|
- echo "---------------------------------------------------------"
|
||||||
- echo "Testing..."
|
- echo "Testing..."
|
||||||
- echo "---------------------------------------------------------"
|
- echo "---------------------------------------------------------"
|
||||||
|
- ./gradlew test -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||||
environment:
|
environment:
|
||||||
SONAR_HOST_URL:
|
SONAR_HOST_URL:
|
||||||
from_secret: sonarScannerHostUrl
|
from_secret: sonarScannerHostUrl
|
||||||
|
@ -355,6 +355,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
val drawerData = DrawerData(repository.getDBTags().map { it.toView() },
|
val drawerData = DrawerData(repository.getDBTags().map { it.toView() },
|
||||||
repository.getDBSources().map { it.toView() })
|
repository.getDBSources().map { it.toView() })
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
|
// TODO: All this logic should be handled by the repository, simplify and remove direct DB access
|
||||||
// Only refresh if there is no data in the DB, or if the `UpdateSources` setting is enabled
|
// Only refresh if there is no data in the DB, or if the `UpdateSources` setting is enabled
|
||||||
if (drawerData.sources?.isEmpty() == true || appSettingsService.isUpdateSourcesEnabled()) {
|
if (drawerData.sources?.isEmpty() == true || appSettingsService.isUpdateSourcesEnabled()) {
|
||||||
drawerApiCalls(drawerData)
|
drawerApiCalls(drawerData)
|
||||||
|
@ -163,7 +163,6 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val result = repository.login()
|
val result = repository.login()
|
||||||
if (result) {
|
if (result) {
|
||||||
repository.updateApiVersion()
|
|
||||||
goToMain()
|
goToMain()
|
||||||
} else {
|
} else {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
@ -52,11 +52,13 @@ override fun doWork(): Result {
|
|||||||
|
|
||||||
repository.handleDBActions()
|
repository.handleDBActions()
|
||||||
|
|
||||||
|
val apiItems = repository.tryToCacheItemsAndGetNewOnes()
|
||||||
if (appSettingsService.isNotifyNewItemsEnabled()) {
|
if (appSettingsService.isNotifyNewItemsEnabled()) {
|
||||||
launch {
|
launch {
|
||||||
handleNewItemsNotification(repository.tryToCacheItemsAndGetNewOnes(), notificationManager)
|
handleNewItemsNotification(apiItems, notificationManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
apiItems.map { it.preloadImages(context) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success()
|
return Result.success()
|
||||||
@ -66,6 +68,7 @@ override fun doWork(): Result {
|
|||||||
newItems: List<SelfossModel.Item>?,
|
newItems: List<SelfossModel.Item>?,
|
||||||
notificationManager: NotificationManager
|
notificationManager: NotificationManager
|
||||||
) {
|
) {
|
||||||
|
// TODO: Check if this coroutine is actually required
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
val apiItems = newItems.orEmpty()
|
val apiItems = newItems.orEmpty()
|
||||||
|
|
||||||
@ -102,7 +105,6 @@ override fun doWork(): Result {
|
|||||||
notificationManager.notify(2, newItemsNotification.build())
|
notificationManager.notify(2, newItemsNotification.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apiItems.map { it.preloadImages(context) }
|
|
||||||
Timer("", false).schedule(4000) {
|
Timer("", false).schedule(4000) {
|
||||||
notificationManager.cancel(1)
|
notificationManager.cancel(1)
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,8 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("test-common"))
|
implementation(kotlin("test-common"))
|
||||||
implementation(kotlin("test-annotations-common"))
|
implementation(kotlin("test-annotations-common"))
|
||||||
|
implementation("io.mockk:mockk:1.12.0")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val androidMain by getting {
|
val androidMain by getting {
|
||||||
|
@ -11,6 +11,7 @@ import io.github.aakira.napier.Napier
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
|
class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
|
||||||
|
|
||||||
@ -36,9 +37,12 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
var badgeStarred = 0
|
var badgeStarred = 0
|
||||||
set(value) {field = if (value < 0) { 0 } else { value } }
|
set(value) {field = if (value < 0) { 0 } else { value } }
|
||||||
|
|
||||||
|
private var fetchedSources = false
|
||||||
|
private var fetchedTags = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
runBlocking {
|
||||||
updateApiVersion()
|
updateApiVersion()
|
||||||
dateUtils = DateUtils(appSettingsService)
|
dateUtils = DateUtils(appSettingsService)
|
||||||
reloadBadges()
|
reloadBadges()
|
||||||
@ -105,9 +109,9 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
val items = api.getItems(
|
val items = api.getItems(
|
||||||
itemType.type,
|
itemType.type,
|
||||||
0,
|
0,
|
||||||
tagFilter?.tag,
|
null,
|
||||||
sourceFilter?.id?.toLong(),
|
null,
|
||||||
searchFilter,
|
null,
|
||||||
null,
|
null,
|
||||||
200
|
200
|
||||||
)
|
)
|
||||||
@ -131,32 +135,40 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
badgeStarred = response.data.starred
|
badgeStarred = response.data.starred
|
||||||
success = true
|
success = true
|
||||||
}
|
}
|
||||||
} else {
|
} else if (appSettingsService.isItemCachingEnabled()) {
|
||||||
// TODO: do this differently, because it's not efficient
|
// TODO: do this differently, because it's not efficient
|
||||||
val dbItems = getDBItems()
|
val dbItems = getDBItems()
|
||||||
badgeUnread = dbItems.filter { item -> item.unread }.size
|
badgeUnread = dbItems.filter { item -> item.unread }.size
|
||||||
badgeStarred = dbItems.filter { item -> item.starred }.size
|
badgeStarred = dbItems.filter { item -> item.starred }.size
|
||||||
badgeAll = items.size
|
badgeAll = dbItems.size
|
||||||
|
success = true
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getTags(): List<SelfossModel.Tag>? {
|
suspend fun getTags(): List<SelfossModel.Tag> {
|
||||||
return if (isNetworkAvailable()) {
|
val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
|
||||||
|
return if (isNetworkAvailable() && !fetchedTags) {
|
||||||
val apiTags = api.tags()
|
val apiTags = api.tags()
|
||||||
if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
if (apiTags.success && apiTags.data != null && isDatabaseEnabled) {
|
||||||
resetDBTagsWithData(apiTags.data)
|
resetDBTagsWithData(apiTags.data)
|
||||||
|
if (!appSettingsService.isUpdateSourcesEnabled()) {
|
||||||
|
fetchedTags = true
|
||||||
}
|
}
|
||||||
apiTags.data
|
}
|
||||||
} else {
|
apiTags.data ?: emptyList()
|
||||||
|
} else if (isDatabaseEnabled) {
|
||||||
getDBTags().map { it.toView() }
|
getDBTags().map { it.toView() }
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
|
// TODO: Add tests
|
||||||
|
suspend fun getSpouts(): Map<String, SelfossModel.Spout> {
|
||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
val spouts = api.spouts()
|
val spouts = api.spouts()
|
||||||
return if (spouts.success && spouts.data != null) {
|
if (spouts.success && spouts.data != null) {
|
||||||
spouts.data
|
spouts.data
|
||||||
} else {
|
} else {
|
||||||
emptyMap() // TODO: do something here
|
emptyMap() // TODO: do something here
|
||||||
@ -166,18 +178,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
|
suspend fun getSources(): ArrayList<SelfossModel.Source> {
|
||||||
return if (isNetworkAvailable()) {
|
val isDatabaseEnabled = appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled()
|
||||||
|
return if (isNetworkAvailable() && !fetchedSources) {
|
||||||
val apiSources = api.sources()
|
val apiSources = api.sources()
|
||||||
if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
if (apiSources.success && apiSources.data != null && isDatabaseEnabled) {
|
||||||
resetDBSourcesWithData(apiSources.data)
|
resetDBSourcesWithData(apiSources.data)
|
||||||
|
if (!appSettingsService.isUpdateSourcesEnabled()) {
|
||||||
|
fetchedSources = true
|
||||||
}
|
}
|
||||||
apiSources.data
|
}
|
||||||
} else {
|
apiSources.data ?: ArrayList()
|
||||||
|
} else if (isDatabaseEnabled) {
|
||||||
ArrayList(getDBSources().map { it.toView() })
|
ArrayList(getDBSources().map { it.toView() })
|
||||||
|
} else {
|
||||||
|
ArrayList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests
|
||||||
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
||||||
val success = markAsReadById(item.id)
|
val success = markAsReadById(item.id)
|
||||||
|
|
||||||
@ -189,14 +208,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
|
|
||||||
private suspend fun markAsReadById(id: Int): Boolean {
|
private suspend fun markAsReadById(id: Int): Boolean {
|
||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
api.markAsRead(id.toString())?.isSuccess
|
api.markAsRead(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), read = true)
|
insertDBAction(id.toString(), read = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests
|
||||||
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
|
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
|
||||||
val success = unmarkAsReadById(item.id)
|
val success = unmarkAsReadById(item.id)
|
||||||
|
|
||||||
@ -208,13 +227,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
|
|
||||||
private suspend fun unmarkAsReadById(id: Int): Boolean {
|
private suspend fun unmarkAsReadById(id: Int): Boolean {
|
||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
api.unmarkAsRead(id.toString())?.isSuccess
|
api.unmarkAsRead(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), unread = true)
|
insertDBAction(id.toString(), unread = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests
|
||||||
suspend fun starr(item: SelfossModel.Item): Boolean {
|
suspend fun starr(item: SelfossModel.Item): Boolean {
|
||||||
val success = starrById(item.id)
|
val success = starrById(item.id)
|
||||||
|
|
||||||
@ -226,13 +246,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
|
|
||||||
private suspend fun starrById(id: Int): Boolean {
|
private suspend fun starrById(id: Int): Boolean {
|
||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
api.starr(id.toString())?.isSuccess
|
api.starr(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), starred = true)
|
insertDBAction(id.toString(), starred = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests
|
||||||
suspend fun unstarr(item: SelfossModel.Item): Boolean {
|
suspend fun unstarr(item: SelfossModel.Item): Boolean {
|
||||||
val success = unstarrById(item.id)
|
val success = unstarrById(item.id)
|
||||||
|
|
||||||
@ -244,17 +265,18 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
|
|
||||||
private suspend fun unstarrById(id: Int): Boolean {
|
private suspend fun unstarrById(id: Int): Boolean {
|
||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
api.unstarr(id.toString())?.isSuccess
|
api.unstarr(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), starred = true)
|
insertDBAction(id.toString(), starred = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests
|
||||||
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
||||||
var success = false
|
var success = false
|
||||||
|
|
||||||
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) {
|
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() }).isSuccess) {
|
||||||
success = true
|
success = true
|
||||||
for (item in items) {
|
for (item in items) {
|
||||||
markAsReadLocally(item)
|
markAsReadLocally(item)
|
||||||
@ -323,7 +345,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
tags,
|
tags,
|
||||||
filter,
|
filter,
|
||||||
appSettingsService.getApiVersion()
|
appSettingsService.getApiVersion()
|
||||||
)?.isSuccess == true
|
).isSuccess == true
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
@ -333,17 +355,15 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
var success = false
|
var success = false
|
||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
val response = api.deleteSource(id)
|
val response = api.deleteSource(id)
|
||||||
if (response != null) {
|
|
||||||
success = response.isSuccess
|
success = response.isSuccess
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateRemote(): Boolean {
|
suspend fun updateRemote(): Boolean {
|
||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
api.update()?.equals("finished")
|
api.update().data.equals("finished")
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -354,7 +374,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
try {
|
try {
|
||||||
val response = api.login()
|
val response = api.login()
|
||||||
result = response?.isSuccess == true
|
result = response.isSuccess == true
|
||||||
if (result) {
|
if (result) {
|
||||||
updateApiVersion()
|
updateApiVersion()
|
||||||
}
|
}
|
||||||
@ -371,7 +391,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
api.refreshLoginInformation()
|
api.refreshLoginInformation()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateApiVersion() {
|
private suspend fun updateApiVersion() {
|
||||||
val apiMajorVersion = appSettingsService.getApiVersion()
|
val apiMajorVersion = appSettingsService.getApiVersion()
|
||||||
|
|
||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
@ -390,8 +410,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
private fun deleteDBAction(action: ACTION) =
|
private fun deleteDBAction(action: ACTION) =
|
||||||
db.actionsQueries.deleteAction(action.id)
|
db.actionsQueries.deleteAction(action.id)
|
||||||
|
|
||||||
|
// TODO: This function should be private
|
||||||
fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList()
|
fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList()
|
||||||
|
|
||||||
|
// TODO: This function should be private
|
||||||
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
|
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
|
||||||
|
|
||||||
private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
|
private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
|
||||||
@ -430,8 +452,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
private fun updateDBItem(item: SelfossModel.Item) =
|
private fun updateDBItem(item: SelfossModel.Item) =
|
||||||
db.itemsQueries.updateItem(item.datetime, item.title.getHtmlDecoded(), item.content, item.unread, item.starred, item.thumbnail, item.icon, item.link, item.sourcetitle, item.tags.joinToString(","), item.id.toString())
|
db.itemsQueries.updateItem(item.datetime, item.title.getHtmlDecoded(), item.content, item.unread, item.starred, item.thumbnail, item.icon, item.link, item.sourcetitle, item.tags.joinToString(","), item.id.toString())
|
||||||
|
|
||||||
|
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item> {
|
||||||
suspend fun tryToCacheItemsAndGetNewOnes(): List<SelfossModel.Item>? {
|
|
||||||
try {
|
try {
|
||||||
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
|
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
|
||||||
val allItems = getMaxItemsForBackground(ItemType.ALL)
|
val allItems = getMaxItemsForBackground(ItemType.ALL)
|
||||||
@ -444,6 +465,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add tests
|
||||||
suspend fun handleDBActions() {
|
suspend fun handleDBActions() {
|
||||||
|
|
||||||
val actions: List<ACTION> = getDBActions()
|
val actions: List<ACTION> = getDBActions()
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user