Compare commits
25
Commits
v122092561
...
41c951b659
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41c951b659 | ||
|
|
e2afff0b8e | ||
|
|
a382fc89ea | ||
|
|
3f0a3903ae | ||
|
|
f46f98cef0 | ||
|
|
bf6f1a917e | ||
|
|
71c0a4d340 | ||
|
|
63c550ead3 | ||
|
|
366b2e10f1 | ||
|
|
d2436bb976 | ||
|
|
da71de6806 | ||
|
|
0264da8ccc | ||
|
|
270d959ee0 | ||
|
|
6d11dfb80c | ||
|
|
4184bbb900 | ||
|
|
ef994460c1 | ||
|
|
758708e18d | ||
|
|
c0381144d1 | ||
|
|
cda3ba6cb4 | ||
|
|
a4636cc0c8 | ||
|
|
4c12c9d570 | ||
|
|
f4db02521d | ||
|
|
60c24fc75a | ||
|
|
5853a19937 | ||
|
|
99f2c04bf6 |
+101
-4
@@ -1,21 +1,118 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: android
|
||||
name: test
|
||||
|
||||
steps:
|
||||
- name: code-analysis
|
||||
- name: AnylyseBuildTest
|
||||
image: mingc/android-build-box:latest
|
||||
failure: ignore
|
||||
commands:
|
||||
- ls -la
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Analysing..."
|
||||
- ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\""
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Building..."
|
||||
- ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Testing..."
|
||||
- echo "---------------------------------------------------------"
|
||||
- ./gradlew test -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
environment:
|
||||
SONAR_HOST_URL:
|
||||
from_secret: sonarScannerHostUrl
|
||||
SONAR_LOGIN:
|
||||
from_secret: sonarScannerLogin
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Publish
|
||||
|
||||
steps:
|
||||
- name: createTag
|
||||
image: ubuntu:latest
|
||||
commands:
|
||||
- apt-get update && apt-get install -y git
|
||||
- ./build.sh --publish --from-ci
|
||||
- git remote add pushing https://$GITEA_USR:$GITEA_PASS@gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform.git
|
||||
- git push pushing --tags
|
||||
environment:
|
||||
GITEA_USR:
|
||||
from_secret: giteaUsr
|
||||
GITEA_PASS:
|
||||
from_secret: giteaPass
|
||||
|
||||
- name: scpFiles
|
||||
image: appleboy/drone-scp
|
||||
settings:
|
||||
host: amine-louveau.fr
|
||||
username: ubuntu
|
||||
key:
|
||||
from_secret: privateKey
|
||||
port: 22
|
||||
target: /home/ubuntu/
|
||||
source: version.txt
|
||||
|
||||
- name: deploy
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: amine-louveau.fr
|
||||
user: ubuntu
|
||||
key:
|
||||
from_secret: privateKey
|
||||
command_timeout: 2m
|
||||
script:
|
||||
- cd /home/ubuntu
|
||||
- sudo rm -rf /var/www/amine/version.txt
|
||||
- sudo chown www-data:www-data ./version.txt
|
||||
- sudo mv version.txt /var/www/amine/
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- promote
|
||||
target:
|
||||
- production
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: Release
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: mingc/android-build-box:latest
|
||||
commands:
|
||||
- ./gradlew :androidApp:build -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
- echo "Generate APK"
|
||||
- ./gradlew :androidApp:assembleGithubConfigRelease -PignoreGitVersion=true -P appLoginUrl="\"URL\"" -P appLoginUsername="\"LOGIN\"" -P appLoginPassword="\"PASS\"" -P pushCache=false
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Get Key"
|
||||
- wget https://amine-louveau.fr/key
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Zipalign"
|
||||
- $ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Sign"
|
||||
- $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias $YOUR_KEY_ALIAS --ks-pass pass:$YOUR_KEYSTORE_PASSWORD --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
|
||||
- echo "---------------------------------------------------------"
|
||||
- echo "Verify"
|
||||
- $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk
|
||||
environment:
|
||||
YOUR_KEYSTORE_PASSWORD:
|
||||
from_secret: keyPass
|
||||
YOUR_KEY_ALIAS:
|
||||
from_secret: keyAlias
|
||||
|
||||
- name: gitea_release
|
||||
image: plugins/gitea-release
|
||||
settings:
|
||||
api_key:
|
||||
from_secret: giteaAPI
|
||||
base_url: https://gitea.amine-louveau.fr
|
||||
files: signed.apk
|
||||
trigger:
|
||||
event:
|
||||
- tag
|
||||
@@ -1,4 +1,4 @@
|
||||
# ReaderForSelfoss-multiplatform
|
||||
# ReaderForSelfoss-multiplatform [](https://build.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform)
|
||||
|
||||
[](https://crowdin.com/project/readerforselfoss)
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
namespace = "bou.amine.apps.readerforselfossv2.android"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="bou.amine.apps.readerforselfossv2.android">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
||||
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
@@ -84,7 +84,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
|
||||
super.onResume()
|
||||
|
||||
val baseUrl = appSettingsService.getBaseUrl()
|
||||
if (baseUrl.isEmpty() || !baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
||||
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid(this@AddSourceActivity)) {
|
||||
mustLoginToAddSource()
|
||||
} else {
|
||||
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
|
||||
|
||||
@@ -14,7 +14,7 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
|
||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
|
||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
@@ -115,14 +115,14 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
binding.passwordView.error = null
|
||||
|
||||
// Store values at the time of the login attempt.
|
||||
val url = binding.urlView.text.toString()
|
||||
val login = binding.loginView.text.toString()
|
||||
val password = binding.passwordView.text.toString()
|
||||
val url = binding.urlView.text.toString().trim()
|
||||
val login = binding.loginView.text.toString().trim()
|
||||
val password = binding.passwordView.text.toString().trim()
|
||||
|
||||
var cancel = false
|
||||
var focusView: View? = null
|
||||
|
||||
if (!url.isBaseUrlValid(this@LoginActivity)) {
|
||||
if (url.isBaseUrlInvalid(this@LoginActivity)) {
|
||||
binding.urlView.error = getString(R.string.login_url_problem)
|
||||
focusView = binding.urlView
|
||||
cancel = true
|
||||
|
||||
+3
-2
@@ -268,8 +268,9 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
|
||||
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
|
||||
if (repository.isNetworkAvailable()) {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi()
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
// TODO: The api should be accessed through the repository
|
||||
val parser = MercuryApi()
|
||||
|
||||
parser.parseUrl(url).enqueue(
|
||||
object : Callback<ParsedContent> {
|
||||
|
||||
+2
-2
@@ -163,7 +163,7 @@ private fun openInBrowser(linkDecoded: String, app: Activity) {
|
||||
fun String.isUrlValid(): Boolean =
|
||||
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
||||
|
||||
fun String.isBaseUrlValid(ctx: Context): Boolean {
|
||||
fun String.isBaseUrlInvalid(ctx: Context): Boolean {
|
||||
val baseUrl = this.toHttpUrlOrNull()
|
||||
var existsAndEndsWithSlash = false
|
||||
if (baseUrl != null) {
|
||||
@@ -171,7 +171,7 @@ fun String.isBaseUrlValid(ctx: Context): Boolean {
|
||||
existsAndEndsWithSlash = "" == pathSegments[pathSegments.size - 1]
|
||||
}
|
||||
|
||||
return Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash
|
||||
return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash)
|
||||
}
|
||||
|
||||
fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10")
|
||||
classpath("com.android.tools.build:gradle:7.2.2")
|
||||
classpath("com.android.tools.build:gradle:7.3.0")
|
||||
|
||||
// sonarquve
|
||||
classpath("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513")
|
||||
|
||||
@@ -21,11 +21,11 @@ VERSION="${INITIAL_VERSION}${TODAYS_VERSION}"
|
||||
|
||||
PARAMS_EXCEPT_PUBLISH=$(echo $1 | sed 's/\-\-publish//')
|
||||
|
||||
./version.sh ${VERSION} ${PARAMS_EXCEPT_PUBLISH}
|
||||
./version.sh ${VERSION} ${PARAMS_EXCEPT_PUBLISH} $@
|
||||
|
||||
if [[ "$@" == *'--publish'* ]]
|
||||
then
|
||||
./publish-version.sh ${VERSION}
|
||||
./publish-version.sh ${VERSION} $@
|
||||
else
|
||||
echo "Did not publish. If you wanted to do so, call the script with \"--publish\" or \"--publish-local\"."
|
||||
fi
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#Wed Feb 09 17:05:19 CET 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+8
-4
@@ -5,7 +5,11 @@
|
||||
rm -f version.txt
|
||||
printf "versionName=$1-github\nversionCode=$1" >> version.txt
|
||||
|
||||
# You'll need to change server as your server and define a VERSION_PATH.
|
||||
scp version.txt server:$VERSION_PATH
|
||||
|
||||
rm version.txt
|
||||
if [[ "$@" == *'--from-ci'* ]]
|
||||
then
|
||||
echo "File created. HANDLE IN CI"
|
||||
else
|
||||
# You'll need to change server as your server and define a VERSION_PATH.
|
||||
scp version.txt server:$VERSION_PATH
|
||||
rm version.txt
|
||||
fi
|
||||
|
||||
@@ -56,6 +56,8 @@ kotlin {
|
||||
dependencies {
|
||||
implementation(kotlin("test-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 {
|
||||
@@ -112,6 +114,7 @@ android {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
namespace = "bou.amine.apps.readerforselfossv2"
|
||||
}
|
||||
|
||||
sqldelight {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="bou.amine.apps.readerforselfossv2" />
|
||||
<manifest />
|
||||
@@ -105,4 +105,16 @@ class SelfossModel {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
class StatusAndData<T>(val success: Boolean, val data: T? = null) {
|
||||
companion object {
|
||||
fun <T> succes(d: T): StatusAndData<T> {
|
||||
return StatusAndData(true, d)
|
||||
}
|
||||
|
||||
fun <T> error(): StatusAndData<T> {
|
||||
return StatusAndData(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
-38
@@ -11,6 +11,7 @@ import io.github.aakira.napier.Napier
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
class Repository(private val api: SelfossApi, private val appSettingsService: AppSettingsService, connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) {
|
||||
|
||||
@@ -38,7 +39,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
init {
|
||||
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
runBlocking {
|
||||
updateApiVersion()
|
||||
dateUtils = DateUtils(appSettingsService)
|
||||
reloadBadges()
|
||||
@@ -47,7 +48,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||
// TODO: Use the updatedSince parameter
|
||||
var fetchedItems: List<SelfossModel.Item>? = null
|
||||
var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
|
||||
if (isNetworkAvailable()) {
|
||||
fetchedItems = api.getItems(
|
||||
displayedItems.type,
|
||||
@@ -59,23 +60,25 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
)
|
||||
} else {
|
||||
if (appSettingsService.isItemCachingEnabled()) {
|
||||
fetchedItems = getDBItems().filter {
|
||||
displayedItems == ItemType.ALL ||
|
||||
(it.unread && displayedItems == ItemType.UNREAD) ||
|
||||
(it.starred && displayedItems == ItemType.STARRED)
|
||||
}.map { it.toView() }
|
||||
fetchedItems = SelfossModel.StatusAndData.succes(
|
||||
getDBItems().filter {
|
||||
displayedItems == ItemType.ALL ||
|
||||
(it.unread && displayedItems == ItemType.UNREAD) ||
|
||||
(it.starred && displayedItems == ItemType.STARRED)
|
||||
}.map { it.toView() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (fetchedItems != null) {
|
||||
items = ArrayList(fetchedItems)
|
||||
if (fetchedItems.success && fetchedItems.data != null) {
|
||||
items = ArrayList(fetchedItems.data!!)
|
||||
sortItems()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
suspend fun getOlderItems(): ArrayList<SelfossModel.Item> {
|
||||
var fetchedItems: List<SelfossModel.Item>? = null
|
||||
var fetchedItems: SelfossModel.StatusAndData<List<SelfossModel.Item>> = SelfossModel.StatusAndData.error()
|
||||
if (isNetworkAvailable()) {
|
||||
val offset = items.size
|
||||
fetchedItems = api.getItems(
|
||||
@@ -88,16 +91,16 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
)
|
||||
} // When using the db cache, we load everything the first time, so there should be nothing more to load.
|
||||
|
||||
if (fetchedItems != null) {
|
||||
items.addAll(fetchedItems)
|
||||
if (fetchedItems.success && fetchedItems.data != null) {
|
||||
items.addAll(fetchedItems.data!!)
|
||||
sortItems()
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item>? {
|
||||
private suspend fun getMaxItemsForBackground(itemType: ItemType): List<SelfossModel.Item> {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.getItems(
|
||||
val items = api.getItems(
|
||||
itemType.type,
|
||||
0,
|
||||
tagFilter?.tag,
|
||||
@@ -106,6 +109,11 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
null,
|
||||
200
|
||||
)
|
||||
return if (items.success && items.data != null) {
|
||||
items.data
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
@@ -119,10 +127,10 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
var success = false
|
||||
if (isNetworkAvailable()) {
|
||||
val response = api.stats()
|
||||
if (response != null) {
|
||||
badgeUnread = response.unread
|
||||
badgeAll = response.total
|
||||
badgeStarred = response.starred
|
||||
if (response.success && response.data != null) {
|
||||
badgeUnread = response.data.unread
|
||||
badgeAll = response.data.total
|
||||
badgeStarred = response.data.starred
|
||||
success = true
|
||||
}
|
||||
} else {
|
||||
@@ -138,18 +146,24 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
suspend fun getTags(): List<SelfossModel.Tag>? {
|
||||
return if (isNetworkAvailable()) {
|
||||
val apiTags = api.tags()
|
||||
if (apiTags != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
||||
resetDBTagsWithData(apiTags)
|
||||
if (apiTags.success && apiTags.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
||||
resetDBTagsWithData(apiTags.data)
|
||||
}
|
||||
apiTags
|
||||
apiTags.data
|
||||
} else {
|
||||
getDBTags().map { it.toView() }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun getSpouts(): Map<String, SelfossModel.Spout>? {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.spouts()
|
||||
val spouts = api.spouts()
|
||||
return if (spouts.success && spouts.data != null) {
|
||||
spouts.data
|
||||
} else {
|
||||
emptyMap() // TODO: do something here
|
||||
}
|
||||
} else {
|
||||
throw NetworkUnavailableException()
|
||||
}
|
||||
@@ -158,15 +172,16 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
suspend fun getSources(): ArrayList<SelfossModel.Source>? {
|
||||
return if (isNetworkAvailable()) {
|
||||
val apiSources = api.sources()
|
||||
if (apiSources != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
||||
resetDBSourcesWithData(apiSources)
|
||||
if (apiSources.success && apiSources.data != null && (appSettingsService.isItemCachingEnabled() || !appSettingsService.isUpdateSourcesEnabled())) {
|
||||
resetDBSourcesWithData(apiSources.data)
|
||||
}
|
||||
apiSources
|
||||
apiSources.data
|
||||
} else {
|
||||
ArrayList(getDBSources().map { it.toView() })
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun markAsRead(item: SelfossModel.Item): Boolean {
|
||||
val success = markAsReadById(item.id)
|
||||
|
||||
@@ -178,14 +193,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun markAsReadById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.markAsRead(id.toString())?.isSuccess == true
|
||||
api.markAsRead(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), read = true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
|
||||
val success = unmarkAsReadById(item.id)
|
||||
|
||||
@@ -197,13 +212,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun unmarkAsReadById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.unmarkAsRead(id.toString())?.isSuccess == true
|
||||
api.unmarkAsRead(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), unread = true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun starr(item: SelfossModel.Item): Boolean {
|
||||
val success = starrById(item.id)
|
||||
|
||||
@@ -215,13 +231,14 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun starrById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.starr(id.toString())?.isSuccess == true
|
||||
api.starr(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), starred = true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun unstarr(item: SelfossModel.Item): Boolean {
|
||||
val success = unstarrById(item.id)
|
||||
|
||||
@@ -233,17 +250,18 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
private suspend fun unstarrById(id: Int): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.unstarr(id.toString())?.isSuccess == true
|
||||
api.unstarr(id.toString())?.isSuccess
|
||||
} else {
|
||||
insertDBAction(id.toString(), starred = true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
||||
var success = false
|
||||
|
||||
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess == true) {
|
||||
if (isNetworkAvailable() && api.markAllAsRead(items.map { it.id.toString() })?.isSuccess) {
|
||||
success = true
|
||||
for (item in items) {
|
||||
markAsReadLocally(item)
|
||||
@@ -332,7 +350,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
|
||||
suspend fun updateRemote(): Boolean {
|
||||
return if (isNetworkAvailable()) {
|
||||
api.update()?.equals("finished") ?: false
|
||||
api.update()?.equals("finished")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -360,17 +378,20 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
api.refreshLoginInformation()
|
||||
}
|
||||
|
||||
// TODO: This should be private
|
||||
suspend fun updateApiVersion() {
|
||||
val apiMajorVersion = appSettingsService.getApiVersion()
|
||||
|
||||
if (isNetworkAvailable()) {
|
||||
val fetchedVersion = api.version()
|
||||
if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) {
|
||||
appSettingsService.updateApiVersion(fetchedVersion.getApiMajorVersion())
|
||||
if (fetchedVersion.success && fetchedVersion.data != null && fetchedVersion.data.getApiMajorVersion() != apiMajorVersion) {
|
||||
appSettingsService.updateApiVersion(fetchedVersion.data.getApiMajorVersion())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be private (since all api calls are made through the repository
|
||||
// no other entity needs to know about the connectivity status)
|
||||
fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride
|
||||
|
||||
private fun getDBActions(): List<ACTION> =
|
||||
@@ -379,11 +400,13 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
private fun deleteDBAction(action: ACTION) =
|
||||
db.actionsQueries.deleteAction(action.id)
|
||||
|
||||
// TODO: This function should be private
|
||||
fun getDBTags(): List<TAG> = db.tagsQueries.tags().executeAsList()
|
||||
|
||||
// TODO: This function should be private
|
||||
fun getDBSources(): List<SOURCE> = db.sourcesQueries.sources().executeAsList()
|
||||
|
||||
fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
|
||||
private fun resetDBTagsWithData(tagEntities: List<SelfossModel.Tag>) {
|
||||
db.tagsQueries.deleteAllTags()
|
||||
|
||||
db.tagsQueries.transaction {
|
||||
@@ -393,7 +416,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
}
|
||||
}
|
||||
|
||||
fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) {
|
||||
private fun resetDBSourcesWithData(sources: List<SelfossModel.Source>) {
|
||||
db.sourcesQueries.deleteAllSources()
|
||||
|
||||
db.sourcesQueries.transaction {
|
||||
@@ -420,12 +443,12 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
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 {
|
||||
val newItems = getMaxItemsForBackground(ItemType.UNREAD)
|
||||
val allItems = getMaxItemsForBackground(ItemType.ALL)
|
||||
val starredItems = getMaxItemsForBackground(ItemType.STARRED)
|
||||
insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty())
|
||||
insertDBItems(newItems + allItems + starredItems)
|
||||
return newItems
|
||||
} catch (e: Throwable) {
|
||||
// We do nothing
|
||||
@@ -433,6 +456,7 @@ class Repository(private val api: SelfossApi, private val appSettingsService: Ap
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// TODO: Add tests
|
||||
suspend fun handleDBActions() {
|
||||
|
||||
val actions: List<ACTION> = getDBActions()
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ktor.client.plugins.contentnegotiation.*
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import io.ktor.client.statement.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -34,7 +35,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
appSettingsService.logApiCalls(message)
|
||||
}
|
||||
}
|
||||
level = LogLevel.ALL
|
||||
level = LogLevel.INFO
|
||||
}
|
||||
install(HttpTimeout) {
|
||||
requestTimeoutMillis = appSettingsService.getApiTimeout()
|
||||
@@ -65,11 +66,11 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
client = createHttpClient()
|
||||
}
|
||||
|
||||
suspend fun login(): SelfossModel.SuccessResponse? =
|
||||
client.get(url("/login")) {
|
||||
suspend fun login(): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.get(url("/login")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun getItems(
|
||||
type: String,
|
||||
@@ -79,8 +80,8 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
search: String?,
|
||||
updatedSince: String?,
|
||||
items: Int? = null
|
||||
): List<SelfossModel.Item>? =
|
||||
client.get(url("/items")) {
|
||||
): SelfossModel.StatusAndData<List<SelfossModel.Item>> =
|
||||
bodyOrFailure(client.get(url("/items")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
parameter("type", type)
|
||||
@@ -90,74 +91,74 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
parameter("updatedsince", updatedSince)
|
||||
parameter("items", items ?: appSettingsService.getItemsNumber())
|
||||
parameter("offset", offset)
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun stats(): SelfossModel.Stats? =
|
||||
client.get(url("/stats")) {
|
||||
suspend fun stats(): SelfossModel.StatusAndData<SelfossModel.Stats> =
|
||||
bodyOrFailure(client.get(url("/stats")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun tags(): List<SelfossModel.Tag>? =
|
||||
client.get(url("/tags")) {
|
||||
suspend fun tags(): SelfossModel.StatusAndData<List<SelfossModel.Tag>> =
|
||||
bodyOrFailure(client.get(url("/tags")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun update(): String? =
|
||||
client.get(url("/update")) {
|
||||
suspend fun update(): SelfossModel.StatusAndData<String> =
|
||||
bodyOrFailure(client.get(url("/update")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun spouts(): Map<String, SelfossModel.Spout>? =
|
||||
client.get(url("/sources/spouts")) {
|
||||
suspend fun spouts(): SelfossModel.StatusAndData<Map<String, SelfossModel.Spout>> =
|
||||
bodyOrFailure(client.get(url("/sources/spouts")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun sources(): ArrayList<SelfossModel.Source>? =
|
||||
client.get(url("/sources/list")) {
|
||||
suspend fun sources(): SelfossModel.StatusAndData<ArrayList<SelfossModel.Source>> =
|
||||
bodyOrFailure(client.get(url("/sources/list")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun version(): SelfossModel.ApiVersion? =
|
||||
client.get(url("/api/about")).body()
|
||||
suspend fun version(): SelfossModel.StatusAndData<SelfossModel.ApiVersion> =
|
||||
bodyOrFailure(client.get(url("/api/about")))
|
||||
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/mark/$id")) {
|
||||
suspend fun markAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/mark/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/unmark/$id")) {
|
||||
suspend fun unmarkAsRead(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/unmark/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun starr(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/starr/$id")) {
|
||||
suspend fun starr(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/starr/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun unstarr(id: String): SelfossModel.SuccessResponse? =
|
||||
client.post(url("/unstarr/$id")) {
|
||||
suspend fun unstarr(id: String): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.post(url("/unstarr/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse? =
|
||||
client.submitForm(
|
||||
suspend fun markAllAsRead(ids: List<String>): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.submitForm(
|
||||
url = url("/mark"),
|
||||
formParameters = Parameters.build {
|
||||
append("username", appSettingsService.getUserName())
|
||||
append("password", appSettingsService.getPassword())
|
||||
ids.map { append("ids[]", it) }
|
||||
}
|
||||
).body()
|
||||
))
|
||||
|
||||
suspend fun createSourceForVersion(
|
||||
title: String,
|
||||
@@ -166,12 +167,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
tags: String,
|
||||
filter: String,
|
||||
version: Int
|
||||
): SelfossModel.SuccessResponse? =
|
||||
if (version > 1) {
|
||||
createSource2(title, url, spout, tags, filter)
|
||||
} else {
|
||||
createSource(title, url, spout, tags, filter)
|
||||
}
|
||||
): SelfossModel.SuccessResponse =
|
||||
maybeResponse(
|
||||
if (version > 1) {
|
||||
createSource2(title, url, spout, tags, filter)
|
||||
} else {
|
||||
createSource(title, url, spout, tags, filter)
|
||||
}
|
||||
)
|
||||
|
||||
suspend fun createSource(
|
||||
title: String,
|
||||
@@ -179,7 +182,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String
|
||||
): SelfossModel.SuccessResponse? =
|
||||
): HttpResponse =
|
||||
client.submitForm(
|
||||
url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
|
||||
formParameters = Parameters.build {
|
||||
@@ -189,7 +192,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
append("tags", tags)
|
||||
append("filter", filter)
|
||||
}
|
||||
).body()
|
||||
)
|
||||
|
||||
suspend fun createSource2(
|
||||
title: String,
|
||||
@@ -197,7 +200,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
spout: String,
|
||||
tags: String,
|
||||
filter: String
|
||||
): SelfossModel.SuccessResponse? =
|
||||
): HttpResponse =
|
||||
client.submitForm(
|
||||
url = url("/source?username=${appSettingsService.getUserName()}&password=${appSettingsService.getPassword()}"),
|
||||
formParameters = Parameters.build {
|
||||
@@ -207,11 +210,27 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
||||
append("tags[]", tags)
|
||||
append("filter", filter)
|
||||
}
|
||||
).body()
|
||||
)
|
||||
|
||||
suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse? =
|
||||
client.delete(url("/source/$id")) {
|
||||
suspend fun deleteSource(id: Int): SelfossModel.SuccessResponse =
|
||||
maybeResponse(client.delete(url("/source/$id")) {
|
||||
parameter("username", appSettingsService.getUserName())
|
||||
parameter("password", appSettingsService.getPassword())
|
||||
}.body()
|
||||
})
|
||||
|
||||
suspend fun maybeResponse(r: HttpResponse): SelfossModel.SuccessResponse {
|
||||
return if (r.status.isSuccess()) {
|
||||
r.body()
|
||||
} else {
|
||||
SelfossModel.SuccessResponse(false)
|
||||
}
|
||||
}
|
||||
|
||||
suspend inline fun <reified T> bodyOrFailure(r: HttpResponse): SelfossModel.StatusAndData<T> {
|
||||
return if (r.status.isSuccess()) {
|
||||
SelfossModel.StatusAndData.succes(r.body())
|
||||
} else {
|
||||
SelfossModel.StatusAndData.error()
|
||||
}
|
||||
}
|
||||
}
|
||||
+839
@@ -0,0 +1,839 @@
|
||||
package bou.amine.apps.readerforselfossv2.repository
|
||||
|
||||
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||
import bou.amine.apps.readerforselfossv2.dao.SOURCE
|
||||
import bou.amine.apps.readerforselfossv2.dao.TAG
|
||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||
import com.github.ln_12.library.ConnectivityStatus
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.test.*
|
||||
|
||||
class RepositoryTest() {
|
||||
private val connectivityStatus = mockk<ConnectivityStatus>()
|
||||
private val db = mockk<ReaderForSelfossDB>()
|
||||
private val appSettingsService = mockk<AppSettingsService>()
|
||||
private val api = mockk<SelfossApi>()
|
||||
|
||||
private val NUMBER_ARTICLES = 100
|
||||
private val NUMBER_UNREAD = 50
|
||||
private val NUMBER_STARRED = 20
|
||||
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
clearAllMocks()
|
||||
every { appSettingsService.getApiVersion() } returns 4
|
||||
every { appSettingsService.getBaseUrl() } returns "https://test.com/selfoss/"
|
||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||
every { appSettingsService.isUpdateSourcesEnabled() } returns true
|
||||
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(true)
|
||||
|
||||
coEvery { api.version() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.ApiVersion("2.19-ba1e8e3", "4.0.0"))
|
||||
coEvery { api.stats() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED))
|
||||
|
||||
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
|
||||
every { db.tagsQueries.deleteAllTags() } returns Unit
|
||||
every { db.tagsQueries.transaction(any(), any()) } returns Unit
|
||||
every { db.tagsQueries.insertTag(any()) } returns Unit
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Instantiate repository`() {
|
||||
val success = try {
|
||||
Repository(api, appSettingsService, connectivityStatus, db)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
assertEquals(true, success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Instantiate repository without api version`() {
|
||||
every { appSettingsService.getApiVersion() } returns -1
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val success = try {
|
||||
Repository(api, appSettingsService, connectivityStatus, db)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
assertEquals(true, success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Instantiate repository with negative stats`() {
|
||||
coEvery { api.stats() } returns SelfossModel.StatusAndData(success = true, data = SelfossModel.Stats(-100, -50, -20))
|
||||
|
||||
val success = try {
|
||||
Repository(api, appSettingsService, connectivityStatus, db)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
assertEquals(true, success)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get api 4 date with api 1 version stored`() {
|
||||
every { appSettingsService.getApiVersion() } returns 1
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
every { appSettingsService.updateApiVersion(any()) } returns Unit
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
runBlocking {
|
||||
repository.getNewerItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 1)
|
||||
verify(exactly = 1) { appSettingsService.updateApiVersion(4) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get api 1 date with api 4 version stored`() {
|
||||
every { appSettingsService.getApiVersion() } returns 4
|
||||
coEvery { api.version() } returns SelfossModel.StatusAndData(success = false, null)
|
||||
val itemParameters = FakeItemParameters()
|
||||
itemParameters.datetime = "2021-04-23 11:45:32"
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameters))
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
runBlocking {
|
||||
repository.getNewerItems()
|
||||
}
|
||||
|
||||
assertSame(1, repository.items.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get newer items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
runBlocking {
|
||||
repository.getNewerItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 1)
|
||||
coVerify(exactly = 1) { api.getItems("unread", 0, null, null, null, null, any()) }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get all newer items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.displayedItems = ItemType.ALL
|
||||
runBlocking {
|
||||
repository.getNewerItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 1)
|
||||
coVerify(exactly = 1) { api.getItems("all", 0, null, null, null, null, any()) }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get newer starred items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.displayedItems = ItemType.STARRED
|
||||
runBlocking {
|
||||
repository.getNewerItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 1)
|
||||
coVerify(exactly = 1) { api.getItems("starred", 0, null, null, null, null, any()) }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get newer items without connectivity`() {
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
runBlocking {
|
||||
repository.getNewerItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 1)
|
||||
coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) }
|
||||
verify(atLeast = 1) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get older items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.items = ArrayList(generateTestApiItem())
|
||||
runBlocking {
|
||||
repository.getOlderItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 2)
|
||||
coVerify(exactly = 1) { api.getItems("unread", 1, null, null, null, null, any()) }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get all older items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.items = ArrayList(generateTestApiItem())
|
||||
repository.displayedItems = ItemType.ALL
|
||||
runBlocking {
|
||||
repository.getOlderItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 2)
|
||||
coVerify(exactly = 1) { api.getItems("all", 1, null, null, null, null, any()) }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get older starred items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.displayedItems = ItemType.STARRED
|
||||
repository.items = ArrayList(generateTestApiItem())
|
||||
runBlocking {
|
||||
repository.getOlderItems()
|
||||
}
|
||||
|
||||
assertSame(repository.items.size, 2)
|
||||
coVerify(exactly = 1) { api.getItems("starred", 1, null, null, null, null, any()) }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Reload badges`() {
|
||||
var success = false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
runBlocking {
|
||||
success = repository.reloadBadges()
|
||||
}
|
||||
|
||||
assertSame(true, success)
|
||||
assertSame(NUMBER_ARTICLES, repository.badgeAll)
|
||||
assertSame(NUMBER_UNREAD, repository.badgeUnread)
|
||||
assertSame(NUMBER_STARRED, repository.badgeStarred)
|
||||
coVerify(atLeast = 1) { api.stats() }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Reload badges without response`() {
|
||||
coEvery { api.stats() } returns SelfossModel.StatusAndData(success = false, data = null)
|
||||
|
||||
var success = false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
runBlocking {
|
||||
success = repository.reloadBadges()
|
||||
}
|
||||
|
||||
assertSame(false, success)
|
||||
assertSame(0, repository.badgeAll)
|
||||
assertSame(0, repository.badgeUnread)
|
||||
assertSame(0, repository.badgeStarred)
|
||||
coVerify(atLeast = 1) { api.stats() }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Reload badges with items caching`() {
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||
|
||||
val itemParameter = FakeItemParameters()
|
||||
itemParameter.starred = true
|
||||
itemParameter.unread = false
|
||||
|
||||
var success = false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.items = ArrayList(generateTestApiItem(itemParameter))
|
||||
runBlocking {
|
||||
success = repository.reloadBadges()
|
||||
}
|
||||
|
||||
assertSame(true, success)
|
||||
assertSame(1, repository.badgeAll)
|
||||
assertSame(0, repository.badgeUnread)
|
||||
assertSame(1, repository.badgeStarred)
|
||||
coVerify(exactly = 0) { api.stats() }
|
||||
verify(atLeast = 1) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Reload badges without items caching`() {
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||
|
||||
val itemParameter = FakeItemParameters()
|
||||
itemParameter.starred = true
|
||||
itemParameter.unread = false
|
||||
|
||||
var success = false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.items = ArrayList(generateTestApiItem(itemParameter))
|
||||
runBlocking {
|
||||
success = repository.reloadBadges()
|
||||
}
|
||||
|
||||
assertSame(false, success)
|
||||
assertSame(0, repository.badgeAll)
|
||||
assertSame(0, repository.badgeUnread)
|
||||
assertSame(0, repository.badgeStarred)
|
||||
coVerify(exactly = 0) { api.stats() }
|
||||
verify(exactly = 0) { db.itemsQueries.items().executeAsList()}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get tags`() {
|
||||
val tags = listOf(SelfossModel.Tag("test", "red", 6),
|
||||
SelfossModel.Tag("second", "yellow", 0))
|
||||
|
||||
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testTags: List<SelfossModel.Tag>? = null
|
||||
runBlocking {
|
||||
testTags = repository.getTags()
|
||||
}
|
||||
|
||||
assertSame(tags, testTags)
|
||||
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get tags with sources update disabled`() {
|
||||
val tags = listOf(SelfossModel.Tag("test", "red", 6),
|
||||
SelfossModel.Tag("second", "yellow", 0))
|
||||
val tagsDB = listOf(TAG("test_DB", "red", 6),
|
||||
TAG("second_DB", "yellow", 0))
|
||||
|
||||
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
|
||||
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
|
||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testTags: List<SelfossModel.Tag>? = null
|
||||
runBlocking {
|
||||
testTags = repository.getTags()
|
||||
}
|
||||
|
||||
assertNotSame(tags, testTags)
|
||||
assertSame(tagsDB.first().name, testTags?.first()?.tag)
|
||||
coVerify(exactly = 0) { api.tags() }
|
||||
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get tags with sources update and db disabled`() {
|
||||
val tags = listOf(SelfossModel.Tag("test", "red", 6),
|
||||
SelfossModel.Tag("second", "yellow", 0))
|
||||
val tagsDB = listOf(TAG("test_DB", "red", 6),
|
||||
TAG("second_DB", "yellow", 0))
|
||||
|
||||
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
|
||||
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
|
||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testTags: List<SelfossModel.Tag>? = null
|
||||
runBlocking {
|
||||
testTags = repository.getTags()
|
||||
}
|
||||
|
||||
assertSame(null, testTags)
|
||||
coVerify(exactly = 0) { api.tags() }
|
||||
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get tags without connection`() {
|
||||
val tags = listOf(SelfossModel.Tag("test", "red", 6),
|
||||
SelfossModel.Tag("second", "yellow", 0))
|
||||
val tagsDB = listOf(TAG("test_DB", "red", 6),
|
||||
TAG("second_DB", "yellow", 0))
|
||||
|
||||
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
|
||||
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testTags: List<SelfossModel.Tag>? = null
|
||||
runBlocking {
|
||||
testTags = repository.getTags()
|
||||
}
|
||||
|
||||
assertNotSame(tags, testTags)
|
||||
assertSame(tagsDB.first().name, testTags?.first()?.tag)
|
||||
coVerify(exactly = 0) { api.tags() }
|
||||
verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Get tags without connection and items caching disabled`() {
|
||||
val tags = listOf(SelfossModel.Tag("test", "red", 6),
|
||||
SelfossModel.Tag("second", "yellow", 0))
|
||||
val tagsDB = listOf(TAG("test_DB", "red", 6),
|
||||
TAG("second_DB", "yellow", 0))
|
||||
|
||||
coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
|
||||
coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testTags: List<SelfossModel.Tag>? = null
|
||||
runBlocking {
|
||||
testTags = repository.getTags()
|
||||
}
|
||||
|
||||
assertSame(null, testTags)
|
||||
coVerify(exactly = 0) { api.tags() }
|
||||
verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get sources`() {
|
||||
val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
|
||||
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testSources: List<SelfossModel.Source>? = null
|
||||
runBlocking {
|
||||
testSources = repository.getSources()
|
||||
}
|
||||
|
||||
assertSame(sources, testSources)
|
||||
coVerify(exactly = 1) { api.sources() }
|
||||
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get sources without connection`() {
|
||||
val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
val sourcesDB = listOf<SOURCE>(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
|
||||
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testSources: List<SelfossModel.Source>? = null
|
||||
runBlocking {
|
||||
testSources = repository.getSources()
|
||||
}
|
||||
|
||||
assertContentEquals(sources, testSources)
|
||||
coVerify(exactly = 0) { api.sources() }
|
||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get sources without connection and items caching disabled`() {
|
||||
val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
val sourcesDB = listOf<SOURCE>(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
|
||||
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testSources: List<SelfossModel.Source>? = null
|
||||
runBlocking {
|
||||
testSources = repository.getSources()
|
||||
}
|
||||
|
||||
assertSame(null, testSources)
|
||||
coVerify(exactly = 0) { api.sources() }
|
||||
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get sources with sources update disabled`() {
|
||||
val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
val sourcesDB = listOf<SOURCE>(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
|
||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
|
||||
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testSources: List<SelfossModel.Source>? = null
|
||||
runBlocking {
|
||||
testSources = repository.getSources()
|
||||
}
|
||||
|
||||
assertContentEquals(sources, testSources)
|
||||
coVerify(exactly = 0) { api.sources() }
|
||||
verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get sources with sources update and items caching disabled`() {
|
||||
val sources = arrayListOf(SelfossModel.Source(1, "First source", listOf("Test", "second"),"spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SelfossModel.Source(2, "Second source", listOf("second"),"spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
val sourcesDB = listOf<SOURCE>(SOURCE("1", "First source", "Test,second","spouts\\rss\\fulltextrss", "", "d8c92cdb1ef119ea85c4b9205c879ca7.png"),
|
||||
SOURCE("2", "Second source", "second","spouts\\rss\\fulltextrss", "", "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"))
|
||||
|
||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||
every { appSettingsService.isItemCachingEnabled() } returns false
|
||||
coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
|
||||
every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var testSources: List<SelfossModel.Source>? = null
|
||||
runBlocking {
|
||||
testSources = repository.getSources()
|
||||
}
|
||||
|
||||
assertSame(null, testSources)
|
||||
coVerify(exactly = 0) { api.sources() }
|
||||
verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create source`() {
|
||||
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.SuccessResponse(true)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.createSource("test", "https://test.com/feed", "spouts\\rss\\fulltextrss", "Test, New", "")
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) }
|
||||
assertSame(true, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create source but response fails`() {
|
||||
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.SuccessResponse(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.createSource("test", "https://test.com/feed", "spouts\\rss\\fulltextrss", "Test, New", "")
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create source without connection`() {
|
||||
coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.SuccessResponse(true)
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.createSource("test", "https://test.com/feed", "spouts\\rss\\fulltextrss", "Test, New", "")
|
||||
}
|
||||
|
||||
coVerify(exactly = 0) { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete source`() {
|
||||
coEvery { api.deleteSource(any())} returns SelfossModel.SuccessResponse(true)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.deleteSource(5)
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.deleteSource(5) }
|
||||
assertSame(true, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete source but response fails`() {
|
||||
coEvery { api.deleteSource(any())} returns SelfossModel.SuccessResponse(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.deleteSource(5)
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.deleteSource(5) }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `delete source without connection`() {
|
||||
coEvery { api.deleteSource(any())} returns SelfossModel.SuccessResponse(false)
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.deleteSource(5)
|
||||
}
|
||||
|
||||
coVerify(exactly = 0) { api.deleteSource(5) }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update remote`() {
|
||||
coEvery { api.update()} returns SelfossModel.StatusAndData(success = true, data = "finished")
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.updateRemote()
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.update() }
|
||||
assertSame(true, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update remote but response fails`() {
|
||||
coEvery { api.update()} returns SelfossModel.StatusAndData(success = false, data = "undocumented...")
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.updateRemote()
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.update() }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `update remote without connection`() {
|
||||
coEvery { api.update()} returns SelfossModel.StatusAndData(success = true, data = "undocumented...")
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.updateRemote()
|
||||
}
|
||||
|
||||
coVerify(exactly = 0) { api.update() }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun login() {
|
||||
coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.login()
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.login() }
|
||||
assertSame(true, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login but response fails`() {
|
||||
coEvery { api.login() } returns SelfossModel.SuccessResponse(success = false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.login()
|
||||
}
|
||||
|
||||
coVerify(exactly = 1) { api.login() }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `login but without connection`() {
|
||||
coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true)
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
var response = false
|
||||
runBlocking {
|
||||
response = repository.login()
|
||||
}
|
||||
|
||||
coVerify(exactly = 0) { api.login() }
|
||||
assertSame(false, response)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `refresh login information`() {
|
||||
coEvery { api.refreshLoginInformation() } returns Unit
|
||||
coEvery { appSettingsService.refreshLoginInformation(any(), any(), any()) } returns Unit
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.refreshLoginInformation("https://test.com/selfoss/", "login", "password")
|
||||
|
||||
coVerify(exactly = 1) { api.refreshLoginInformation() }
|
||||
coVerify(exactly = 1) {appSettingsService.refreshLoginInformation("https://test.com/selfoss/", "login", "password")}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cache items`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
|
||||
repository.sourceFilter = SelfossModel.Source(
|
||||
1,
|
||||
"First source",
|
||||
listOf("Test", "second"),
|
||||
"spouts\\rss\\fulltextrss",
|
||||
"",
|
||||
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
|
||||
)
|
||||
repository.searchFilter = "search"
|
||||
var items = emptyList<SelfossModel.Item>()
|
||||
runBlocking {
|
||||
items = repository.tryToCacheItemsAndGetNewOnes()
|
||||
}
|
||||
|
||||
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
|
||||
assertSame(3, items.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cache items but response fails`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = false, data = generateTestApiItem())
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
|
||||
repository.sourceFilter = SelfossModel.Source(
|
||||
1,
|
||||
"First source",
|
||||
listOf("Test", "second"),
|
||||
"spouts\\rss\\fulltextrss",
|
||||
"",
|
||||
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
|
||||
)
|
||||
repository.searchFilter = "search"
|
||||
var items = emptyList<SelfossModel.Item>()
|
||||
runBlocking {
|
||||
items = repository.tryToCacheItemsAndGetNewOnes()
|
||||
}
|
||||
|
||||
coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
|
||||
assertSame(0, items.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cache items without connection`() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
SelfossModel.StatusAndData(success = false, data = generateTestApiItem())
|
||||
every { connectivityStatus.isNetworkConnected } returns MutableStateFlow(false)
|
||||
|
||||
val repository = Repository(api, appSettingsService, connectivityStatus, db)
|
||||
repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
|
||||
repository.sourceFilter = SelfossModel.Source(
|
||||
1,
|
||||
"First source",
|
||||
listOf("Test", "second"),
|
||||
"spouts\\rss\\fulltextrss",
|
||||
"",
|
||||
"d8c92cdb1ef119ea85c4b9205c879ca7.png"
|
||||
)
|
||||
repository.searchFilter = "search"
|
||||
var items = emptyList<SelfossModel.Item>()
|
||||
runBlocking {
|
||||
items = repository.tryToCacheItemsAndGetNewOnes()
|
||||
}
|
||||
|
||||
coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) }
|
||||
assertSame(emptyList<SelfossModel.Item>(), items)
|
||||
}
|
||||
}
|
||||
|
||||
fun generateTestDBItems(item : FakeItemParameters = FakeItemParameters()) : List<ITEM> {
|
||||
return listOf(ITEM(
|
||||
id = item.id,
|
||||
datetime = item.datetime,
|
||||
title = item.title,
|
||||
content = item.content,
|
||||
unread = item.unread,
|
||||
starred = item.starred,
|
||||
thumbnail = item.thumbnail,
|
||||
icon = item.icon,
|
||||
link = item.link,
|
||||
sourcetitle = item.sourcetitle,
|
||||
tags = item.tags
|
||||
))}
|
||||
|
||||
fun generateTestApiItem(item : FakeItemParameters = FakeItemParameters()) : List<SelfossModel.Item> {
|
||||
return listOf(
|
||||
SelfossModel.Item(
|
||||
id = item.id.toInt(),
|
||||
datetime = item.datetime,
|
||||
title = item.title,
|
||||
content = item.content,
|
||||
unread = item.unread,
|
||||
starred = item.starred,
|
||||
thumbnail = item.thumbnail,
|
||||
icon = item.icon,
|
||||
link = item.link,
|
||||
sourcetitle = item.sourcetitle,
|
||||
tags = item.tags.split(',')
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class FakeItemParameters() {
|
||||
val id = "20"
|
||||
var datetime = "2022-09-09T03:32:01-04:00"
|
||||
val title = "Etica della ricerca sotto i riflettori."
|
||||
val content = "<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>L’etica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.</p>\n<p>L’ultimo è quello delle intelligenze artificiali capaci di creare opere complesse basate su immagini e parole memorizzate con il rischio di fake news e di contenuti disturbanti.</p>\n<p>Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui l’intelligenza artificiale parte.</p>\n<p>Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne l’origine.</p>\n<p>Come si comprende, si conferma che gli aspetti etici dell’innovazione e della ricerca si diversificato sempre di più.</p>\n<p>La biologia molecolare e la genetica già in passato hanno posto all’attenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.</p>\n<p>L’argomento, sempre attuale, torna sulle prime pagine a seguito della pubblicazione di una ricerca della Università di Cambridge che ha sviluppato una struttura cellulare di un topo con un cuore che batte regolarmente.</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image002-1.png?w=481\" alt=\"\" width=\"697\" height=\"430\" /><img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image003-1.png?w=906\" alt=\"\" /><p>Magdalena Zernicka-Goetz</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image004.jpg?w=474\" alt=\"\" width=\"622\" height=\"465\" /><p>Gianluca Amadei</p>\n<p>Del gruppo fa parte anche uno scienziato italiano Gianluca Amadei,che dinnanzi alle obiezioni di natura etica sulla realizzazione della vita artificiale si è affrettato a sostenere che non è creare nuove vite il fine primario della ricerca, ma quello di salvare quelle esistenti, di dare contributi essenziali alla medicina citando il caso del fallimento tuttora non interpretato di alcune gravidanze e di superare la sperimentazione animale, così contribuendo positivamente alla soluzione di un altro dilemma etico.</p>\n<p>L’embrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nell’offerta rispetto alla domanda, con attese fino a 4 anni per i trapianti di cuore ed a 2 anni per quelli di fegato. Il lavoro dovrebbe adesso continuare presso l’Ateneo di Padova per creare nuovi organi e nuovi farmaci.</p>"
|
||||
var unread = true
|
||||
var starred = true
|
||||
val thumbnail = null
|
||||
val icon = "ba79e238383ce83c23a169929c8906ef.png"
|
||||
val link = "https://ilblogdellasci.wordpress.com/2022/09/09/etica-della-ricerca-sotto-i-riflettori/"
|
||||
val sourcetitle = "La Chimica e la Società"
|
||||
val tags = "Chimica, Testing"
|
||||
}
|
||||
+10
-7
@@ -1,11 +1,14 @@
|
||||
#!/bin/bash
|
||||
# You can pass --force as first parameter to force push and tag creation.
|
||||
|
||||
echo "Creating tag $@"
|
||||
echo "Creating tag $1"
|
||||
|
||||
TAG="v$@"
|
||||
git tag ${TAG}
|
||||
TAG="v$1"
|
||||
git tag -a ${TAG} -m ${TAG}
|
||||
|
||||
echo "Pushing tag"
|
||||
|
||||
git push origin ${TAG}
|
||||
if [[ "$@" == *'--from-ci'* ]]
|
||||
then
|
||||
echo "Tag created. HANDLE IN CI"
|
||||
else
|
||||
echo "Pushing tag"
|
||||
git push origin ${TAG}
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user