mirror of
https://github.com/afollestad/nock-nock.git
synced 2025-09-08 10:36:03 +00:00
Update ValidationExecutorTest
This commit is contained in:
parent
8535a6fe8b
commit
6bb131fb23
6 changed files with 378 additions and 109 deletions
|
@ -19,12 +19,13 @@ import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import com.afollestad.nocknock.data.AppDatabase
|
import com.afollestad.nocknock.data.AppDatabase
|
||||||
import com.afollestad.nocknock.data.model.RetryPolicy
|
import com.afollestad.nocknock.data.HeaderDao
|
||||||
import com.afollestad.nocknock.data.RetryPolicyDao
|
import com.afollestad.nocknock.data.RetryPolicyDao
|
||||||
import com.afollestad.nocknock.data.SiteDao
|
import com.afollestad.nocknock.data.SiteDao
|
||||||
import com.afollestad.nocknock.data.SiteSettingsDao
|
import com.afollestad.nocknock.data.SiteSettingsDao
|
||||||
import com.afollestad.nocknock.data.ValidationResultsDao
|
import com.afollestad.nocknock.data.ValidationResultsDao
|
||||||
import com.afollestad.nocknock.data.model.Header
|
import com.afollestad.nocknock.data.model.Header
|
||||||
|
import com.afollestad.nocknock.data.model.RetryPolicy
|
||||||
import com.afollestad.nocknock.data.model.Site
|
import com.afollestad.nocknock.data.model.Site
|
||||||
import com.afollestad.nocknock.data.model.SiteSettings
|
import com.afollestad.nocknock.data.model.SiteSettings
|
||||||
import com.afollestad.nocknock.data.model.Status
|
import com.afollestad.nocknock.data.model.Status
|
||||||
|
@ -56,7 +57,8 @@ fun fakeSettingsModel(
|
||||||
validationMode = validationMode,
|
validationMode = validationMode,
|
||||||
validationArgs = null,
|
validationArgs = null,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
networkTimeout = 10000
|
networkTimeout = 10000,
|
||||||
|
certificate = null
|
||||||
)
|
)
|
||||||
|
|
||||||
fun fakeResultModel(
|
fun fakeResultModel(
|
||||||
|
@ -165,12 +167,29 @@ fun mockDatabase(): AppDatabase {
|
||||||
on { update(isA()) } doReturn 1
|
on { update(isA()) } doReturn 1
|
||||||
on { delete(isA()) } doReturn 1
|
on { delete(isA()) } doReturn 1
|
||||||
}
|
}
|
||||||
|
val headerDao = mock<HeaderDao> {
|
||||||
|
on { all() } doReturn com.afollestad.nocknock.engine.MOCK_MODEL_1.headers + com.afollestad.nocknock.engine.MOCK_MODEL_2.headers + com.afollestad.nocknock.engine.MOCK_MODEL_3.headers
|
||||||
|
on { forSite(isA()) } doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<Long>(0)
|
||||||
|
return@doAnswer when (id) {
|
||||||
|
1L -> com.afollestad.nocknock.engine.MOCK_MODEL_1.headers
|
||||||
|
2L -> com.afollestad.nocknock.engine.MOCK_MODEL_2.headers
|
||||||
|
3L -> com.afollestad.nocknock.engine.MOCK_MODEL_3.headers
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on { insert(isA<Header>()) } doReturn 1L
|
||||||
|
on { insert(isA<List<Header>>()) } doReturn listOf(1L, 2L)
|
||||||
|
on { update(isA()) } doReturn 1
|
||||||
|
on { delete(isA()) } doReturn 1
|
||||||
|
}
|
||||||
|
|
||||||
return mock {
|
return mock {
|
||||||
on { siteDao() } doReturn siteDao
|
on { siteDao() } doReturn siteDao
|
||||||
on { siteSettingsDao() } doReturn settingsDao
|
on { siteSettingsDao() } doReturn settingsDao
|
||||||
on { validationResultsDao() } doReturn resultsDao
|
on { validationResultsDao() } doReturn resultsDao
|
||||||
on { retryPolicyDao() } doReturn retryDao
|
on { retryPolicyDao() } doReturn retryDao
|
||||||
|
on { headerDao() } doReturn headerDao
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.annotation.CheckResult
|
import androidx.annotation.CheckResult
|
||||||
|
import com.afollestad.nocknock.utilities.ext.toUri
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
@ -33,8 +34,8 @@ import timber.log.Timber.d as log
|
||||||
interface SslManager {
|
interface SslManager {
|
||||||
|
|
||||||
@CheckResult fun clientForCertificate(
|
@CheckResult fun clientForCertificate(
|
||||||
certUri: Uri,
|
certUri: String,
|
||||||
host: String,
|
siteUri: String,
|
||||||
client: OkHttpClient
|
client: OkHttpClient
|
||||||
): OkHttpClient
|
): OkHttpClient
|
||||||
}
|
}
|
||||||
|
@ -45,21 +46,25 @@ class RealSslManager(
|
||||||
) : SslManager {
|
) : SslManager {
|
||||||
|
|
||||||
override fun clientForCertificate(
|
override fun clientForCertificate(
|
||||||
certUri: Uri,
|
certUri: String,
|
||||||
host: String,
|
siteUri: String,
|
||||||
client: OkHttpClient
|
client: OkHttpClient
|
||||||
): OkHttpClient {
|
): OkHttpClient {
|
||||||
log("Loading certificate $certUri for host $host")
|
val parsedCertUri = certUri.toUri()
|
||||||
|
val parsedSiteUri = siteUri.toUri()
|
||||||
|
val siteHost = parsedSiteUri.host ?: ""
|
||||||
|
|
||||||
|
log("Loading certificate $certUri for host $siteHost")
|
||||||
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
|
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
|
||||||
keyStore.load(null, null)
|
keyStore.load(null, null)
|
||||||
|
|
||||||
val certInputStream = app.openUri(certUri)
|
val certInputStream = app.openUri(parsedCertUri)
|
||||||
val bis = BufferedInputStream(certInputStream)
|
val bis = BufferedInputStream(certInputStream)
|
||||||
val certificateFactory = CertificateFactory.getInstance("X.509")
|
val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||||
|
|
||||||
while (bis.available() > 0) {
|
while (bis.available() > 0) {
|
||||||
val cert = certificateFactory.generateCertificate(bis)
|
val cert = certificateFactory.generateCertificate(bis)
|
||||||
keyStore.setCertificateEntry(host, cert)
|
keyStore.setCertificateEntry(siteHost, cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
val trustManagerFactory =
|
val trustManagerFactory =
|
||||||
|
@ -76,7 +81,7 @@ class RealSslManager(
|
||||||
.sslSocketFactory(sslContext.socketFactory, trustManager)
|
.sslSocketFactory(sslContext.socketFactory, trustManager)
|
||||||
.hostnameVerifier { hostname, _ ->
|
.hostnameVerifier { hostname, _ ->
|
||||||
log("Verifying hostname $hostname")
|
log("Verifying hostname $hostname")
|
||||||
hostname == host
|
hostname == siteHost
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package com.afollestad.nocknock.engine.validation
|
||||||
|
|
||||||
import android.app.job.JobScheduler
|
import android.app.job.JobScheduler
|
||||||
import android.app.job.JobScheduler.RESULT_SUCCESS
|
import android.app.job.JobScheduler.RESULT_SUCCESS
|
||||||
|
import android.net.Uri
|
||||||
import com.afollestad.nocknock.data.AppDatabase
|
import com.afollestad.nocknock.data.AppDatabase
|
||||||
import com.afollestad.nocknock.data.allSites
|
import com.afollestad.nocknock.data.allSites
|
||||||
import com.afollestad.nocknock.data.model.Site
|
import com.afollestad.nocknock.data.model.Site
|
||||||
|
@ -25,7 +26,6 @@ import com.afollestad.nocknock.data.model.Status.OK
|
||||||
import com.afollestad.nocknock.engine.R
|
import com.afollestad.nocknock.engine.R
|
||||||
import com.afollestad.nocknock.engine.ssl.SslManager
|
import com.afollestad.nocknock.engine.ssl.SslManager
|
||||||
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.KEY_SITE_ID
|
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.KEY_SITE_ID
|
||||||
import com.afollestad.nocknock.utilities.ext.toUri
|
|
||||||
import com.afollestad.nocknock.utilities.providers.BundleProvider
|
import com.afollestad.nocknock.utilities.providers.BundleProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.JobInfoProvider
|
import com.afollestad.nocknock.utilities.providers.JobInfoProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.StringProvider
|
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
|
@ -45,6 +45,8 @@ data class CheckResult(
|
||||||
|
|
||||||
typealias ClientTimeoutChanger = (client: OkHttpClient, timeout: Int) -> OkHttpClient
|
typealias ClientTimeoutChanger = (client: OkHttpClient, timeout: Int) -> OkHttpClient
|
||||||
|
|
||||||
|
typealias UriConverter = (String) -> Uri
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
interface ValidationExecutor {
|
interface ValidationExecutor {
|
||||||
|
|
||||||
|
@ -168,8 +170,8 @@ class RealValidationExecutor(
|
||||||
val clientWithTimeout = clientTimeoutChanger(okHttpClient, siteSettings.networkTimeout)
|
val clientWithTimeout = clientTimeoutChanger(okHttpClient, siteSettings.networkTimeout)
|
||||||
val client = if (!siteSettings.certificate.isNullOrEmpty()) {
|
val client = if (!siteSettings.certificate.isNullOrEmpty()) {
|
||||||
sslManager.clientForCertificate(
|
sslManager.clientForCertificate(
|
||||||
certUri = siteSettings.certificate!!.toUri(),
|
certUri = siteSettings.certificate!!,
|
||||||
host = site.url.toUri().host ?: "",
|
siteUri = site.url,
|
||||||
client = clientWithTimeout
|
client = clientWithTimeout
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -212,9 +214,7 @@ class RealValidationExecutor(
|
||||||
jobScheduler.allPendingJobs
|
jobScheduler.allPendingJobs
|
||||||
.firstOrNull { job -> job.id == site.id.toInt() }
|
.firstOrNull { job -> job.id == site.id.toInt() }
|
||||||
|
|
||||||
@Suppress("unused")
|
@TestOnly fun setClientTimeoutChanger(changer: ClientTimeoutChanger) {
|
||||||
@TestOnly
|
|
||||||
fun setClientTimeoutChanger(changer: ClientTimeoutChanger) {
|
|
||||||
this.clientTimeoutChanger = changer
|
this.clientTimeoutChanger = changer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
189
engine/src/test/java/com/afollestad/nocknock/engine/TestData.kt
Normal file
189
engine/src/test/java/com/afollestad/nocknock/engine/TestData.kt
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
/**
|
||||||
|
* Designed and developed by Aidan Follestad (@afollestad)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.afollestad.nocknock.engine
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import com.afollestad.nocknock.data.AppDatabase
|
||||||
|
import com.afollestad.nocknock.data.HeaderDao
|
||||||
|
import com.afollestad.nocknock.data.RetryPolicyDao
|
||||||
|
import com.afollestad.nocknock.data.SiteDao
|
||||||
|
import com.afollestad.nocknock.data.SiteSettingsDao
|
||||||
|
import com.afollestad.nocknock.data.ValidationResultsDao
|
||||||
|
import com.afollestad.nocknock.data.model.Header
|
||||||
|
import com.afollestad.nocknock.data.model.RetryPolicy
|
||||||
|
import com.afollestad.nocknock.data.model.Site
|
||||||
|
import com.afollestad.nocknock.data.model.SiteSettings
|
||||||
|
import com.afollestad.nocknock.data.model.Status
|
||||||
|
import com.afollestad.nocknock.data.model.Status.OK
|
||||||
|
import com.afollestad.nocknock.data.model.ValidationMode
|
||||||
|
import com.afollestad.nocknock.data.model.ValidationMode.STATUS_CODE
|
||||||
|
import com.afollestad.nocknock.data.model.ValidationResult
|
||||||
|
import com.nhaarman.mockitokotlin2.doAnswer
|
||||||
|
import com.nhaarman.mockitokotlin2.doReturn
|
||||||
|
import com.nhaarman.mockitokotlin2.isA
|
||||||
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
|
import java.lang.System.currentTimeMillis
|
||||||
|
|
||||||
|
fun fakeIntent(action: String): Intent {
|
||||||
|
return mock {
|
||||||
|
on { getAction() } doReturn action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fakeSettingsModel(
|
||||||
|
id: Long,
|
||||||
|
validationMode: ValidationMode = STATUS_CODE
|
||||||
|
) = SiteSettings(
|
||||||
|
siteId = id,
|
||||||
|
validationIntervalMs = 600000,
|
||||||
|
validationMode = validationMode,
|
||||||
|
validationArgs = null,
|
||||||
|
disabled = false,
|
||||||
|
networkTimeout = 10000,
|
||||||
|
certificate = null
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fakeResultModel(
|
||||||
|
id: Long,
|
||||||
|
status: Status = OK,
|
||||||
|
reason: String? = null
|
||||||
|
) = ValidationResult(
|
||||||
|
siteId = id,
|
||||||
|
status = status,
|
||||||
|
reason = reason,
|
||||||
|
timestampMs = currentTimeMillis()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fakeRetryPolicy(
|
||||||
|
id: Long,
|
||||||
|
count: Int = 3,
|
||||||
|
minutes: Int = 6
|
||||||
|
) = RetryPolicy(
|
||||||
|
siteId = id,
|
||||||
|
count = count,
|
||||||
|
minutes = minutes
|
||||||
|
)
|
||||||
|
|
||||||
|
fun fakeHeaders(siteId: Long): List<Header> {
|
||||||
|
return listOf(
|
||||||
|
Header(id = siteId + 1, siteId = siteId, key = "Content-Type", value = "text/html"),
|
||||||
|
Header(id = siteId + 2, siteId = siteId, key = "User-Agent", value = "NockNock")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fakeModel(id: Long) = Site(
|
||||||
|
id = id,
|
||||||
|
name = "Test",
|
||||||
|
url = "https://test.com",
|
||||||
|
tags = "",
|
||||||
|
settings = fakeSettingsModel(id),
|
||||||
|
lastResult = fakeResultModel(id),
|
||||||
|
retryPolicy = fakeRetryPolicy(id),
|
||||||
|
headers = fakeHeaders(id)
|
||||||
|
)
|
||||||
|
|
||||||
|
val MOCK_MODEL_1 = fakeModel(1)
|
||||||
|
val MOCK_MODEL_2 = fakeModel(2)
|
||||||
|
val MOCK_MODEL_3 = fakeModel(3)
|
||||||
|
val ALL_MOCK_MODELS = listOf(MOCK_MODEL_1, MOCK_MODEL_2, MOCK_MODEL_3)
|
||||||
|
|
||||||
|
fun mockDatabase(): AppDatabase {
|
||||||
|
val siteDao = mock<SiteDao> {
|
||||||
|
on { insert(isA()) } doReturn 1
|
||||||
|
on { one(isA()) } doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<Long>(0)
|
||||||
|
return@doAnswer when (id) {
|
||||||
|
1L -> listOf(MOCK_MODEL_1)
|
||||||
|
2L -> listOf(MOCK_MODEL_2)
|
||||||
|
3L -> listOf(MOCK_MODEL_3)
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on { all() } doReturn ALL_MOCK_MODELS
|
||||||
|
on { update(isA()) } doAnswer { inv ->
|
||||||
|
return@doAnswer inv.arguments.size
|
||||||
|
}
|
||||||
|
on { delete(isA()) } doAnswer { inv ->
|
||||||
|
return@doAnswer inv.arguments.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val settingsDao = mock<SiteSettingsDao> {
|
||||||
|
on { insert(isA()) } doReturn 1L
|
||||||
|
on { forSite(isA()) } doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<Long>(0)
|
||||||
|
return@doAnswer when (id) {
|
||||||
|
1L -> listOf(MOCK_MODEL_1.settings!!)
|
||||||
|
2L -> listOf(MOCK_MODEL_2.settings!!)
|
||||||
|
3L -> listOf(MOCK_MODEL_3.settings!!)
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on { update(isA()) } doReturn 1
|
||||||
|
on { delete(isA()) } doReturn 1
|
||||||
|
}
|
||||||
|
val resultsDao = mock<ValidationResultsDao> {
|
||||||
|
on { insert(isA()) } doReturn 1L
|
||||||
|
on { forSite(isA()) } doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<Long>(0)
|
||||||
|
return@doAnswer when (id) {
|
||||||
|
1L -> listOf(MOCK_MODEL_1.lastResult!!)
|
||||||
|
2L -> listOf(MOCK_MODEL_2.lastResult!!)
|
||||||
|
3L -> listOf(MOCK_MODEL_3.lastResult!!)
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on { update(isA()) } doReturn 1
|
||||||
|
on { delete(isA()) } doReturn 1
|
||||||
|
}
|
||||||
|
val retryDao = mock<RetryPolicyDao> {
|
||||||
|
on { insert(isA()) } doReturn 1L
|
||||||
|
on { forSite(isA()) } doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<Long>(0)
|
||||||
|
return@doAnswer when (id) {
|
||||||
|
1L -> listOf(MOCK_MODEL_1.retryPolicy!!)
|
||||||
|
2L -> listOf(MOCK_MODEL_2.retryPolicy!!)
|
||||||
|
3L -> listOf(MOCK_MODEL_3.retryPolicy!!)
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on { update(isA()) } doReturn 1
|
||||||
|
on { delete(isA()) } doReturn 1
|
||||||
|
}
|
||||||
|
val headerDao = mock<HeaderDao> {
|
||||||
|
on { all() } doReturn MOCK_MODEL_1.headers + MOCK_MODEL_2.headers + MOCK_MODEL_3.headers
|
||||||
|
on { forSite(isA()) } doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<Long>(0)
|
||||||
|
return@doAnswer when (id) {
|
||||||
|
1L -> MOCK_MODEL_1.headers
|
||||||
|
2L -> MOCK_MODEL_2.headers
|
||||||
|
3L -> MOCK_MODEL_3.headers
|
||||||
|
else -> listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
on { insert(isA<Header>()) } doReturn 1L
|
||||||
|
on { insert(isA<List<Header>>()) } doReturn listOf(1L, 2L)
|
||||||
|
on { update(isA()) } doReturn 1
|
||||||
|
on { delete(isA()) } doReturn 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return mock {
|
||||||
|
on { siteDao() } doReturn siteDao
|
||||||
|
on { siteSettingsDao() } doReturn settingsDao
|
||||||
|
on { validationResultsDao() } doReturn resultsDao
|
||||||
|
on { retryPolicyDao() } doReturn retryDao
|
||||||
|
on { headerDao() } doReturn headerDao
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,8 @@ package com.afollestad.nocknock.engine
|
||||||
import android.app.job.JobInfo
|
import android.app.job.JobInfo
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.os.PersistableBundle
|
import android.os.PersistableBundle
|
||||||
|
import com.afollestad.nocknock.data.AppDatabase
|
||||||
|
import com.afollestad.nocknock.data.model.Site
|
||||||
import com.afollestad.nocknock.utilities.providers.BundleProvider
|
import com.afollestad.nocknock.utilities.providers.BundleProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.IBundle
|
import com.afollestad.nocknock.utilities.providers.IBundle
|
||||||
import com.afollestad.nocknock.utilities.providers.IBundler
|
import com.afollestad.nocknock.utilities.providers.IBundler
|
||||||
|
@ -34,11 +36,11 @@ fun testBundleProvider(): BundleProvider {
|
||||||
val realBundle = mock<PersistableBundle>()
|
val realBundle = mock<PersistableBundle>()
|
||||||
val creator = it.getArgument<IBundler>(0)
|
val creator = it.getArgument<IBundler>(0)
|
||||||
creator(object : IBundle {
|
creator(object : IBundle {
|
||||||
override fun putInt(
|
override fun putLong(
|
||||||
key: String,
|
key: String,
|
||||||
value: Int
|
value: Long
|
||||||
) {
|
) {
|
||||||
whenever(realBundle.getInt(key)).doReturn(value)
|
whenever(realBundle.getLong(key)).doReturn(value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return@doAnswer realBundle
|
return@doAnswer realBundle
|
||||||
|
@ -66,3 +68,21 @@ fun testJobInfoProvider(): JobInfoProvider {
|
||||||
}
|
}
|
||||||
return provider
|
return provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun AppDatabase.setAllSites(vararg sites: Site) {
|
||||||
|
whenever(siteDao().all()).doReturn(listOf(*sites))
|
||||||
|
for (site in sites) {
|
||||||
|
whenever(siteSettingsDao().forSite(site.id))
|
||||||
|
.doReturn(listOf(site.settings!!))
|
||||||
|
if (site.lastResult != null) {
|
||||||
|
whenever(validationResultsDao().forSite(site.id))
|
||||||
|
.doReturn(listOf(site.lastResult!!))
|
||||||
|
}
|
||||||
|
if (site.retryPolicy != null) {
|
||||||
|
whenever(retryPolicyDao().forSite(site.id))
|
||||||
|
.doReturn(listOf(site.retryPolicy!!))
|
||||||
|
}
|
||||||
|
whenever(headerDao().forSite(site.id))
|
||||||
|
.doReturn(site.headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,13 +17,12 @@ package com.afollestad.nocknock.engine
|
||||||
|
|
||||||
import android.app.job.JobInfo
|
import android.app.job.JobInfo
|
||||||
import android.app.job.JobScheduler
|
import android.app.job.JobScheduler
|
||||||
import com.afollestad.nocknock.data.legacy.ServerModel
|
import com.afollestad.nocknock.data.model.Header
|
||||||
import com.afollestad.nocknock.data.model.Status.ERROR
|
import com.afollestad.nocknock.data.model.Status.ERROR
|
||||||
import com.afollestad.nocknock.data.model.Status.OK
|
import com.afollestad.nocknock.data.model.Status.OK
|
||||||
import com.afollestad.nocknock.data.model.ValidationMode.STATUS_CODE
|
import com.afollestad.nocknock.engine.ssl.SslManager
|
||||||
import com.afollestad.nocknock.data.legacy.ServerModelStore
|
import com.afollestad.nocknock.engine.validation.RealValidationExecutor
|
||||||
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.KEY_SITE_ID
|
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.KEY_SITE_ID
|
||||||
import com.afollestad.nocknock.engine.validation.RealValidationManager
|
|
||||||
import com.afollestad.nocknock.utilities.providers.StringProvider
|
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import com.nhaarman.mockitokotlin2.any
|
import com.nhaarman.mockitokotlin2.any
|
||||||
|
@ -56,15 +55,21 @@ class ValidationExecutorTest {
|
||||||
}
|
}
|
||||||
private val bundleProvider = testBundleProvider()
|
private val bundleProvider = testBundleProvider()
|
||||||
private val jobInfoProvider = testJobInfoProvider()
|
private val jobInfoProvider = testJobInfoProvider()
|
||||||
private val store = mock<ServerModelStore>()
|
private val database = mockDatabase()
|
||||||
|
private val sslManager = mock<SslManager> {
|
||||||
|
on { clientForCertificate(any(), any(), any()) } doAnswer { inv ->
|
||||||
|
inv.getArgument<OkHttpClient>(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val manager = RealValidationManager(
|
private val manager = RealValidationExecutor(
|
||||||
jobScheduler,
|
jobScheduler,
|
||||||
okHttpClient,
|
okHttpClient,
|
||||||
stringProvider,
|
stringProvider,
|
||||||
bundleProvider,
|
bundleProvider,
|
||||||
jobInfoProvider,
|
jobInfoProvider,
|
||||||
store
|
database,
|
||||||
|
sslManager
|
||||||
).apply {
|
).apply {
|
||||||
setClientTimeoutChanger { _, timeout ->
|
setClientTimeoutChanger { _, timeout ->
|
||||||
whenever(okHttpClient.callTimeoutMillis()).doReturn(timeout)
|
whenever(okHttpClient.callTimeoutMillis()).doReturn(timeout)
|
||||||
|
@ -72,202 +77,241 @@ class ValidationExecutorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun ensureScheduledChecks_noEnabledSites() = runBlocking {
|
@Test fun ensureScheduledValidations_noEnabledSites() = runBlocking {
|
||||||
val model1 = fakeModel().copy(disabled = true)
|
val model1 = fakeModel(id = 1)
|
||||||
whenever(store.get()).doReturn(listOf(model1))
|
model1.settings = model1.settings!!.copy(disabled = true)
|
||||||
|
database.setAllSites(model1)
|
||||||
|
|
||||||
manager.ensureScheduledChecks()
|
manager.ensureScheduledValidations()
|
||||||
|
|
||||||
verifyNoMoreInteractions(jobScheduler)
|
verifyNoMoreInteractions(jobScheduler)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun ensureScheduledChecks_sitesAlreadyHaveJobs() = runBlocking<Unit> {
|
@Test fun ensureScheduledValidations_sitesAlreadyHaveJobs() = runBlocking<Unit> {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
val job1 = fakeJob(model1.id)
|
val job1 = fakeJob(1)
|
||||||
whenever(store.get()).doReturn(listOf(model1))
|
database.setAllSites(model1)
|
||||||
whenever(jobScheduler.allPendingJobs).doReturn(listOf(job1))
|
whenever(jobScheduler.allPendingJobs).doReturn(listOf(job1))
|
||||||
|
|
||||||
manager.ensureScheduledChecks()
|
manager.ensureScheduledValidations()
|
||||||
|
|
||||||
verify(jobScheduler, never()).schedule(any())
|
verify(jobScheduler, never()).schedule(any())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun ensureScheduledChecks() = runBlocking {
|
@Test fun ensureScheduledValidations() = runBlocking {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
whenever(store.get()).doReturn(listOf(model1))
|
database.setAllSites(model1)
|
||||||
|
|
||||||
whenever(jobScheduler.allPendingJobs).doReturn(listOf<JobInfo>())
|
whenever(jobScheduler.allPendingJobs).doReturn(listOf<JobInfo>())
|
||||||
|
|
||||||
manager.ensureScheduledChecks()
|
manager.ensureScheduledValidations()
|
||||||
|
|
||||||
val jobCaptor = argumentCaptor<JobInfo>()
|
val jobCaptor = argumentCaptor<JobInfo>()
|
||||||
verify(jobScheduler).schedule(jobCaptor.capture())
|
verify(jobScheduler).schedule(jobCaptor.capture())
|
||||||
val jobInfo = jobCaptor.allValues.single()
|
val jobInfo = jobCaptor.allValues.single()
|
||||||
assertThat(jobInfo.id).isEqualTo(model1.id)
|
assertThat(jobInfo.id).isEqualTo(model1.id)
|
||||||
assertThat(jobInfo.extras.getInt(KEY_SITE_ID)).isEqualTo(model1.id)
|
assertThat(jobInfo.extras.getLong(KEY_SITE_ID)).isEqualTo(model1.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun scheduleCheck_rightNow() {
|
@Test fun scheduleValidation_rightNow() {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
whenever(jobScheduler.allPendingJobs).doReturn(listOf<JobInfo>())
|
whenever(jobScheduler.allPendingJobs).doReturn(listOf<JobInfo>())
|
||||||
|
|
||||||
manager.scheduleCheck(
|
manager.scheduleValidation(
|
||||||
site = model1,
|
site = model1,
|
||||||
rightNow = true
|
rightNow = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val jobCaptor = argumentCaptor<JobInfo>()
|
val jobCaptor = argumentCaptor<JobInfo>()
|
||||||
verify(jobScheduler).schedule(jobCaptor.capture())
|
verify(jobScheduler).schedule(jobCaptor.capture())
|
||||||
verify(jobScheduler).cancel(model1.id)
|
verify(jobScheduler).cancel(1)
|
||||||
|
|
||||||
val jobInfo = jobCaptor.allValues.single()
|
val jobInfo = jobCaptor.allValues.single()
|
||||||
assertThat(jobInfo.id).isEqualTo(model1.id)
|
assertThat(jobInfo.id).isEqualTo(model1.id)
|
||||||
assertThat(jobInfo.extras.getInt(KEY_SITE_ID)).isEqualTo(model1.id)
|
assertThat(jobInfo.extras.getLong(KEY_SITE_ID)).isEqualTo(model1.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException::class)
|
@Test(expected = IllegalStateException::class)
|
||||||
fun scheduleCheck_notFromFinishingJob_haveExistingJob() {
|
fun scheduleValidation_notFromFinishingJob_haveExistingJob() {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
val job1 = fakeJob(model1.id)
|
val job1 = fakeJob(1)
|
||||||
whenever(jobScheduler.allPendingJobs).doReturn(listOf(job1))
|
whenever(jobScheduler.allPendingJobs).doReturn(listOf(job1))
|
||||||
|
|
||||||
manager.scheduleCheck(
|
manager.scheduleValidation(
|
||||||
site = model1,
|
site = model1,
|
||||||
fromFinishingJob = false
|
fromFinishingJob = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun scheduleCheck_fromFinishingJob_haveExistingJob() {
|
@Test fun scheduleValidation_fromFinishingJob_haveExistingJob() {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
val job1 = fakeJob(model1.id)
|
val job1 = fakeJob(1)
|
||||||
whenever(jobScheduler.allPendingJobs).doReturn(listOf(job1))
|
whenever(jobScheduler.allPendingJobs).doReturn(listOf(job1))
|
||||||
|
|
||||||
manager.scheduleCheck(
|
manager.scheduleValidation(
|
||||||
site = model1,
|
site = model1,
|
||||||
fromFinishingJob = true
|
fromFinishingJob = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val jobCaptor = argumentCaptor<JobInfo>()
|
val jobCaptor = argumentCaptor<JobInfo>()
|
||||||
verify(jobScheduler).schedule(jobCaptor.capture())
|
verify(jobScheduler).schedule(jobCaptor.capture())
|
||||||
verify(jobScheduler, never()).cancel(model1.id)
|
verify(jobScheduler, never()).cancel(any())
|
||||||
|
|
||||||
val jobInfo = jobCaptor.allValues.single()
|
val jobInfo = jobCaptor.allValues.single()
|
||||||
assertThat(jobInfo.id).isEqualTo(model1.id)
|
assertThat(jobInfo.id).isEqualTo(model1.id)
|
||||||
assertThat(jobInfo.extras.getInt(KEY_SITE_ID)).isEqualTo(model1.id)
|
assertThat(jobInfo.extras.getLong(KEY_SITE_ID)).isEqualTo(model1.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun scheduleCheck() {
|
@Test fun scheduleValidation() {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
whenever(jobScheduler.allPendingJobs).doReturn(listOf<JobInfo>())
|
whenever(jobScheduler.allPendingJobs).doReturn(listOf<JobInfo>())
|
||||||
|
|
||||||
manager.scheduleCheck(
|
manager.scheduleValidation(
|
||||||
site = model1,
|
site = model1,
|
||||||
fromFinishingJob = true
|
fromFinishingJob = true
|
||||||
)
|
)
|
||||||
|
|
||||||
val jobCaptor = argumentCaptor<JobInfo>()
|
val jobCaptor = argumentCaptor<JobInfo>()
|
||||||
verify(jobScheduler).schedule(jobCaptor.capture())
|
verify(jobScheduler).schedule(jobCaptor.capture())
|
||||||
verify(jobScheduler, never()).cancel(model1.id)
|
verify(jobScheduler, never()).cancel(any())
|
||||||
|
|
||||||
val jobInfo = jobCaptor.allValues.single()
|
val jobInfo = jobCaptor.allValues.single()
|
||||||
assertThat(jobInfo.id).isEqualTo(model1.id)
|
assertThat(jobInfo.id).isEqualTo(model1.id)
|
||||||
assertThat(jobInfo.extras.getInt(KEY_SITE_ID)).isEqualTo(model1.id)
|
assertThat(jobInfo.extras.getLong(KEY_SITE_ID)).isEqualTo(model1.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun cancelCheck() {
|
@Test fun cancelScheduledValidation() {
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
manager.cancelCheck(model1)
|
manager.cancelScheduledValidation(model1)
|
||||||
verify(jobScheduler).cancel(model1.id)
|
verify(jobScheduler).cancel(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun performCheck_httpNotSuccess() = runBlocking {
|
@Test fun performValidation_httpNotSuccess() = runBlocking {
|
||||||
val response = fakeResponse(500, "Internal Server Error", "Hello World")
|
val response = fakeResponse(500, "Internal Server Error", "Hello World")
|
||||||
val call = mock<Call> {
|
val call = mock<Call> {
|
||||||
on { execute() } doReturn response
|
on { execute() } doReturn response
|
||||||
}
|
}
|
||||||
whenever(okHttpClient.newCall(any())).doReturn(call)
|
whenever(okHttpClient.newCall(any())).doReturn(call)
|
||||||
|
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
val result = manager.performCheck(model1)
|
val result = manager.performValidation(model1)
|
||||||
|
|
||||||
assertThat(result.model).isEqualTo(
|
assertThat(result.model).isEqualTo(
|
||||||
model1.copy(
|
model1.copy(
|
||||||
status = ERROR,
|
lastResult = model1.lastResult?.copy(
|
||||||
reason = "Response 500 - Hello World"
|
status = ERROR,
|
||||||
|
reason = "Response 500 - Hello World"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun performCheck_socketTimeout() = runBlocking {
|
@Test fun performValidation_socketTimeout() = runBlocking {
|
||||||
val error = SocketTimeoutException("Oh no!")
|
val error = SocketTimeoutException("Oh no!")
|
||||||
val call = mock<Call> {
|
val call = mock<Call> {
|
||||||
on { execute() } doAnswer { throw error }
|
on { execute() } doAnswer { throw error }
|
||||||
}
|
}
|
||||||
whenever(okHttpClient.newCall(any())).doReturn(call)
|
whenever(okHttpClient.newCall(any())).doReturn(call)
|
||||||
|
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
val result = manager.performCheck(model1)
|
val result = manager.performValidation(model1)
|
||||||
|
|
||||||
assertThat(result.model).isEqualTo(
|
assertThat(result.model).isEqualTo(
|
||||||
model1.copy(
|
model1.copy(
|
||||||
status = ERROR,
|
lastResult = model1.lastResult?.copy(
|
||||||
reason = timeoutError
|
status = ERROR,
|
||||||
|
reason = timeoutError
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun performCheck_exception() = runBlocking {
|
@Test fun performValidation_exception() = runBlocking {
|
||||||
val error = Exception("Oh no!")
|
val error = Exception("Oh no!")
|
||||||
val call = mock<Call> {
|
val call = mock<Call> {
|
||||||
on { execute() } doAnswer { throw error }
|
on { execute() } doAnswer { throw error }
|
||||||
}
|
}
|
||||||
whenever(okHttpClient.newCall(any())).doReturn(call)
|
whenever(okHttpClient.newCall(any())).doReturn(call)
|
||||||
|
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1)
|
||||||
val result = manager.performCheck(model1)
|
val result = manager.performValidation(model1)
|
||||||
|
|
||||||
assertThat(result.model).isEqualTo(
|
assertThat(result.model).isEqualTo(
|
||||||
model1.copy(
|
model1.copy(
|
||||||
status = ERROR,
|
lastResult = model1.lastResult?.copy(
|
||||||
reason = "Oh no!"
|
status = ERROR,
|
||||||
|
reason = "Oh no!"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun performCheck_success() = runBlocking {
|
@Test fun performValidation_success_withHeaders() = runBlocking {
|
||||||
|
val requestCaptor = argumentCaptor<Request>()
|
||||||
|
val response = fakeResponse(200, "OK", "Hello World")
|
||||||
|
|
||||||
|
val call = mock<Call> {
|
||||||
|
on { execute() } doReturn response
|
||||||
|
}
|
||||||
|
whenever(okHttpClient.newCall(requestCaptor.capture()))
|
||||||
|
.doReturn(call)
|
||||||
|
|
||||||
|
val model1 = fakeModel(id = 1).copy(
|
||||||
|
headers = listOf(
|
||||||
|
Header(
|
||||||
|
key = "X-Test-Header",
|
||||||
|
value = "Hello, World!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val result = manager.performValidation(model1)
|
||||||
|
val httpRequest = requestCaptor.firstValue
|
||||||
|
|
||||||
|
assertThat(result.model).isEqualTo(
|
||||||
|
model1.copy(
|
||||||
|
lastResult = model1.lastResult?.copy(
|
||||||
|
status = OK,
|
||||||
|
reason = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(okHttpClient.callTimeoutMillis())
|
||||||
|
.isEqualTo(model1.settings!!.networkTimeout)
|
||||||
|
assertThat(httpRequest.header("X-Test-Header"))
|
||||||
|
.isEqualTo("Hello, World!")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun performValidation_success_withCustomSslCert() = runBlocking<Unit> {
|
||||||
val response = fakeResponse(200, "OK", "Hello World")
|
val response = fakeResponse(200, "OK", "Hello World")
|
||||||
val call = mock<Call> {
|
val call = mock<Call> {
|
||||||
on { execute() } doReturn response
|
on { execute() } doReturn response
|
||||||
}
|
}
|
||||||
whenever(okHttpClient.newCall(any())).doReturn(call)
|
whenever(okHttpClient.newCall(any())).doReturn(call)
|
||||||
|
|
||||||
val model1 = fakeModel()
|
val model1 = fakeModel(id = 1).copy(
|
||||||
val result = manager.performCheck(model1)
|
url = "http://wwww.mysite.com/test.html",
|
||||||
|
headers = emptyList()
|
||||||
|
)
|
||||||
|
model1.settings = model1.settings!!.copy(
|
||||||
|
certificate = "file:///sdcard/cert.pem"
|
||||||
|
)
|
||||||
|
val result = manager.performValidation(model1)
|
||||||
|
|
||||||
assertThat(result.model).isEqualTo(
|
assertThat(result.model).isEqualTo(
|
||||||
model1.copy(
|
model1.copy(
|
||||||
status = OK,
|
lastResult = model1.lastResult?.copy(
|
||||||
reason = null
|
status = OK,
|
||||||
|
reason = null
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assertThat(okHttpClient.callTimeoutMillis())
|
assertThat(okHttpClient.callTimeoutMillis())
|
||||||
.isEqualTo(model1.networkTimeout)
|
.isEqualTo(model1.settings!!.networkTimeout)
|
||||||
}
|
|
||||||
|
|
||||||
@Test fun performCheck_401_butStillSuccess() = runBlocking {
|
verify(sslManager).clientForCertificate(
|
||||||
val response = fakeResponse(401, "Unauthorized", "Hello World")
|
"file:///sdcard/cert.pem",
|
||||||
val call = mock<Call> {
|
"http://wwww.mysite.com/test.html",
|
||||||
on { execute() } doReturn response
|
okHttpClient
|
||||||
}
|
|
||||||
whenever(okHttpClient.newCall(any())).doReturn(call)
|
|
||||||
|
|
||||||
val model1 = fakeModel()
|
|
||||||
val result = manager.performCheck(model1)
|
|
||||||
|
|
||||||
assertThat(result.model).isEqualTo(
|
|
||||||
model1.copy(
|
|
||||||
status = OK,
|
|
||||||
reason = null
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,14 +337,6 @@ class ValidationExecutorTest {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fakeModel() = ServerModel(
|
|
||||||
id = 1,
|
|
||||||
name = "Wakanda Forever",
|
|
||||||
url = "https://www.wakanda.gov",
|
|
||||||
validationMode = STATUS_CODE,
|
|
||||||
networkTimeout = 60000
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun fakeJob(id: Int): JobInfo {
|
private fun fakeJob(id: Int): JobInfo {
|
||||||
return mock {
|
return mock {
|
||||||
on { this.id } doReturn id
|
on { this.id } doReturn id
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue