Merge pull request #6947 from thundernest/refactor_AutoconfigFetcher

Refactor `AutoconfigFetcher`
This commit is contained in:
cketti 2023-06-06 17:13:36 +02:00 committed by GitHub
commit dbe09a9ac2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 193 additions and 199 deletions

View file

@ -1,20 +1,14 @@
package app.k9mail.autodiscovery.autoconfig
import app.k9mail.autodiscovery.api.AutoDiscovery
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.autodiscovery.api.AutoDiscoveryRunnable
import app.k9mail.core.common.mail.EmailAddress
import app.k9mail.core.common.mail.toDomain
import app.k9mail.core.common.net.toDomain
import com.fsck.k9.logging.Timber
import java.io.IOException
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
class AutoconfigDiscovery internal constructor(
private val urlProvider: AutoconfigUrlProvider,
private val fetcher: AutoconfigFetcher,
private val parser: SuspendableAutoconfigParser,
private val autoconfigFetcher: AutoconfigFetcher,
) : AutoDiscovery {
override fun initDiscovery(email: EmailAddress): List<AutoDiscoveryRunnable> {
@ -24,24 +18,10 @@ class AutoconfigDiscovery internal constructor(
return autoconfigUrls.map { autoconfigUrl ->
AutoDiscoveryRunnable {
getAutoconfig(email, autoconfigUrl)
autoconfigFetcher.fetchAutoconfig(autoconfigUrl, email)
}
}
}
private suspend fun getAutoconfig(email: EmailAddress, autoconfigUrl: HttpUrl): AutoDiscoveryResult? {
return try {
fetcher.fetchAutoconfigFile(autoconfigUrl)?.use { inputStream ->
parser.parseSettings(inputStream, email)
}
} catch (e: AutoconfigParserException) {
Timber.d(e, "Failed to parse config from URL: %s", autoconfigUrl)
null
} catch (e: IOException) {
Timber.d(e, "Error fetching Autoconfig from URL: %s", autoconfigUrl)
null
}
}
}
fun createProviderAutoconfigDiscovery(
@ -61,7 +41,9 @@ private fun createAutoconfigDiscovery(
okHttpClient: OkHttpClient,
urlProvider: AutoconfigUrlProvider,
): AutoconfigDiscovery {
val fetcher = OkHttpAutoconfigFetcher(okHttpClient)
val parser = SuspendableAutoconfigParser(RealAutoconfigParser())
return AutoconfigDiscovery(urlProvider, fetcher, parser)
val autoconfigFetcher = RealAutoconfigFetcher(
fetcher = OkHttpFetcher(okHttpClient),
parser = SuspendableAutoconfigParser(RealAutoconfigParser()),
)
return AutoconfigDiscovery(urlProvider, autoconfigFetcher)
}

View file

@ -1,8 +1,12 @@
package app.k9mail.autodiscovery.autoconfig
import java.io.InputStream
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.core.common.mail.EmailAddress
import okhttp3.HttpUrl
/**
* Fetches and parses Autoconfig settings.
*/
internal interface AutoconfigFetcher {
suspend fun fetchAutoconfigFile(url: HttpUrl): InputStream?
suspend fun fetchAutoconfig(autoconfigUrl: HttpUrl, email: EmailAddress): AutoDiscoveryResult?
}

View file

@ -0,0 +1,24 @@
package app.k9mail.autodiscovery.autoconfig
import java.io.InputStream
/**
* Result type for [HttpFetcher].
*/
internal sealed interface HttpFetchResult {
/**
* The HTTP request returned a success response.
*
* @param inputStream Contains the response body.
*/
data class SuccessResponse(
val inputStream: InputStream,
) : HttpFetchResult
/**
* The server returned an error response.
*
* @param code The HTTP status code.
*/
data class ErrorResponse(val code: Int) : HttpFetchResult
}

View file

@ -0,0 +1,7 @@
package app.k9mail.autodiscovery.autoconfig
import okhttp3.HttpUrl
internal interface HttpFetcher {
suspend fun fetch(url: HttpUrl): HttpFetchResult
}

View file

@ -6,10 +6,8 @@ import app.k9mail.autodiscovery.api.AutoDiscoveryRunnable
import app.k9mail.core.common.mail.EmailAddress
import app.k9mail.core.common.mail.toDomain
import app.k9mail.core.common.net.Domain
import app.k9mail.core.common.net.toDomain
import com.fsck.k9.logging.Timber
import java.io.IOException
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
class MxLookupAutoconfigDiscovery internal constructor(
@ -17,8 +15,7 @@ class MxLookupAutoconfigDiscovery internal constructor(
private val baseDomainExtractor: BaseDomainExtractor,
private val subDomainExtractor: SubDomainExtractor,
private val urlProvider: AutoconfigUrlProvider,
private val fetcher: AutoconfigFetcher,
private val parser: SuspendableAutoconfigParser,
private val autoconfigFetcher: AutoconfigFetcher,
) : AutoDiscovery {
override fun initDiscovery(email: EmailAddress): List<AutoDiscoveryRunnable> {
@ -47,7 +44,7 @@ class MxLookupAutoconfigDiscovery internal constructor(
for (domainToCheck in listOfNotNull(mxSubDomain, mxBaseDomain)) {
for (autoconfigUrl in urlProvider.getAutoconfigUrls(domainToCheck)) {
val discoveryResult = getAutoconfig(email, autoconfigUrl)
val discoveryResult = autoconfigFetcher.fetchAutoconfig(autoconfigUrl, email)
if (discoveryResult != null) {
return discoveryResult
}
@ -74,30 +71,19 @@ class MxLookupAutoconfigDiscovery internal constructor(
private fun getNextSubDomain(domain: Domain): Domain? {
return subDomainExtractor.extractSubDomain(domain)
}
private suspend fun getAutoconfig(email: EmailAddress, autoconfigUrl: HttpUrl): AutoDiscoveryResult? {
return try {
fetcher.fetchAutoconfigFile(autoconfigUrl)?.use { inputStream ->
parser.parseSettings(inputStream, email)
}
} catch (e: AutoconfigParserException) {
Timber.d(e, "Failed to parse config from URL: %s", autoconfigUrl)
null
} catch (e: IOException) {
Timber.d(e, "Error fetching Autoconfig from URL: %s", autoconfigUrl)
null
}
}
}
fun createMxLookupAutoconfigDiscovery(okHttpClient: OkHttpClient): MxLookupAutoconfigDiscovery {
val baseDomainExtractor = OkHttpBaseDomainExtractor()
val autoconfigFetcher = RealAutoconfigFetcher(
fetcher = OkHttpFetcher(okHttpClient),
parser = SuspendableAutoconfigParser(RealAutoconfigParser()),
)
return MxLookupAutoconfigDiscovery(
mxResolver = SuspendableMxResolver(MiniDnsMxResolver()),
baseDomainExtractor = baseDomainExtractor,
subDomainExtractor = RealSubDomainExtractor(baseDomainExtractor),
urlProvider = IspDbAutoconfigUrlProvider(),
fetcher = OkHttpAutoconfigFetcher(okHttpClient),
parser = SuspendableAutoconfigParser(RealAutoconfigParser()),
autoconfigFetcher = autoconfigFetcher,
)
}

View file

@ -1,8 +1,6 @@
package app.k9mail.autodiscovery.autoconfig
import com.fsck.k9.logging.Timber
import java.io.IOException
import java.io.InputStream
import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
@ -12,20 +10,11 @@ import okhttp3.OkHttpClient
import okhttp3.Request.Builder
import okhttp3.Response
internal class OkHttpAutoconfigFetcher(
internal class OkHttpFetcher(
private val okHttpClient: OkHttpClient,
) : AutoconfigFetcher {
) : HttpFetcher {
override suspend fun fetchAutoconfigFile(url: HttpUrl): InputStream? {
return try {
performHttpRequest(url)
} catch (e: IOException) {
Timber.d(e, "Error fetching URL: %s", url)
null
}
}
private suspend fun performHttpRequest(url: HttpUrl): InputStream? {
override suspend fun fetch(url: HttpUrl): HttpFetchResult {
return suspendCancellableCoroutine { cancellableContinuation ->
val request = Builder()
.url(url)
@ -44,12 +33,16 @@ internal class OkHttpAutoconfigFetcher(
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val inputStream = response.body?.byteStream()
cancellableContinuation.resume(inputStream)
val result = HttpFetchResult.SuccessResponse(
inputStream = response.body!!.byteStream(),
)
cancellableContinuation.resume(result)
} else {
// We don't care about the body of error responses.
response.close()
cancellableContinuation.resume(null)
val result = HttpFetchResult.ErrorResponse(response.code)
cancellableContinuation.resume(result)
}
}
}

View file

@ -0,0 +1,33 @@
package app.k9mail.autodiscovery.autoconfig
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.autodiscovery.autoconfig.HttpFetchResult.ErrorResponse
import app.k9mail.autodiscovery.autoconfig.HttpFetchResult.SuccessResponse
import app.k9mail.core.common.mail.EmailAddress
import com.fsck.k9.logging.Timber
import java.io.IOException
import okhttp3.HttpUrl
internal class RealAutoconfigFetcher(
private val fetcher: HttpFetcher,
private val parser: SuspendableAutoconfigParser,
) : AutoconfigFetcher {
override suspend fun fetchAutoconfig(autoconfigUrl: HttpUrl, email: EmailAddress): AutoDiscoveryResult? {
return try {
when (val fetchResult = fetcher.fetch(autoconfigUrl)) {
is SuccessResponse -> {
fetchResult.inputStream.use { inputStream ->
parser.parseSettings(inputStream, email)
}
}
is ErrorResponse -> null
}
} catch (e: AutoconfigParserException) {
Timber.d(e, "Failed to parse config from URL: %s", autoconfigUrl)
null
} catch (e: IOException) {
Timber.d(e, "Error fetching Autoconfig from URL: %s", autoconfigUrl)
null
}
}
}

View file

@ -1,5 +1,7 @@
package app.k9mail.autodiscovery.autoconfig
import app.k9mail.autodiscovery.autoconfig.MockAutoconfigFetcher.Companion.RESULT_ONE
import app.k9mail.autodiscovery.autoconfig.MockAutoconfigFetcher.Companion.RESULT_TWO
import app.k9mail.core.common.mail.toEmailAddress
import app.k9mail.core.common.net.toDomain
import assertk.assertThat
@ -15,47 +17,37 @@ private val IRRELEVANT_EMAIL_ADDRESS = "irrelevant@domain.example".toEmailAddres
class AutoconfigDiscoveryTest {
private val urlProvider = MockAutoconfigUrlProvider()
private val fetcher = MockAutoconfigFetcher()
private val parser = MockAutoconfigParser()
private val discovery = AutoconfigDiscovery(urlProvider, fetcher, SuspendableAutoconfigParser(parser))
private val autoconfigFetcher = MockAutoconfigFetcher()
private val discovery = AutoconfigDiscovery(urlProvider, autoconfigFetcher)
@Test
fun `AutoconfigFetcher and AutoconfigParser should only be called when AutoDiscoveryRunnable is run`() = runTest {
val emailAddress = "user@domain.example".toEmailAddress()
val autoconfigUrl = "https://autoconfig.domain.invalid/mail/config-v1.1.xml".toHttpUrl()
urlProvider.addResult(listOf(autoconfigUrl))
fetcher.addResult("data")
parser.addResult(MockAutoconfigParser.RESULT_ONE)
autoconfigFetcher.addResult(RESULT_ONE)
val autoDiscoveryRunnables = discovery.initDiscovery(emailAddress)
assertThat(autoDiscoveryRunnables).hasSize(1)
assertThat(urlProvider.callArguments).containsExactly("domain.example".toDomain() to emailAddress)
assertThat(fetcher.callCount).isEqualTo(0)
assertThat(parser.callCount).isEqualTo(0)
assertThat(autoconfigFetcher.callCount).isEqualTo(0)
val discoveryResult = autoDiscoveryRunnables.first().run()
assertThat(fetcher.callArguments).containsExactly(autoconfigUrl)
assertThat(parser.callArguments).containsExactly("data" to emailAddress)
assertThat(discoveryResult).isEqualTo(MockAutoconfigParser.RESULT_ONE)
assertThat(autoconfigFetcher.callArguments).containsExactly(autoconfigUrl to emailAddress)
assertThat(discoveryResult).isEqualTo(RESULT_ONE)
}
@Test
fun `Two Autoconfig URLs should return two AutoDiscoveryRunnables`() = runTest {
urlProvider.addResult(
listOf(
"https://autoconfig.domain1.invalid/mail/config-v1.1.xml".toHttpUrl(),
"https://autoconfig.domain2.invalid/mail/config-v1.1.xml".toHttpUrl(),
),
)
fetcher.apply {
addResult("data1")
addResult("data2")
}
parser.apply {
addResult(MockAutoconfigParser.RESULT_ONE)
addResult(MockAutoconfigParser.RESULT_TWO)
val urlOne = "https://autoconfig.domain1.invalid/mail/config-v1.1.xml".toHttpUrl()
val urlTwo = "https://autoconfig.domain2.invalid/mail/config-v1.1.xml".toHttpUrl()
urlProvider.addResult(listOf(urlOne, urlTwo))
autoconfigFetcher.apply {
addResult(RESULT_ONE)
addResult(RESULT_TWO)
}
val autoDiscoveryRunnables = discovery.initDiscovery(IRRELEVANT_EMAIL_ADDRESS)
@ -64,14 +56,14 @@ class AutoconfigDiscoveryTest {
val discoveryResultOne = autoDiscoveryRunnables[0].run()
assertThat(parser.callArguments).extracting { it.first }.containsExactly("data1")
assertThat(discoveryResultOne).isEqualTo(MockAutoconfigParser.RESULT_ONE)
assertThat(autoconfigFetcher.callArguments).extracting { it.first }.containsExactly(urlOne)
assertThat(discoveryResultOne).isEqualTo(RESULT_ONE)
parser.callArguments.clear()
autoconfigFetcher.callArguments.clear()
val discoveryResultTwo = autoDiscoveryRunnables[1].run()
assertThat(parser.callArguments).extracting { it.first }.containsExactly("data2")
assertThat(discoveryResultTwo).isEqualTo(MockAutoconfigParser.RESULT_TWO)
assertThat(autoconfigFetcher.callArguments).extracting { it.first }.containsExactly(urlTwo)
assertThat(discoveryResultTwo).isEqualTo(RESULT_TWO)
}
}

View file

@ -1,24 +1,70 @@
package app.k9mail.autodiscovery.autoconfig
import java.io.InputStream
import app.k9mail.autodiscovery.api.AuthenticationType.PasswordCleartext
import app.k9mail.autodiscovery.api.AuthenticationType.PasswordEncrypted
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.autodiscovery.api.ConnectionSecurity.StartTLS
import app.k9mail.autodiscovery.api.ConnectionSecurity.TLS
import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.autodiscovery.api.SmtpServerSettings
import app.k9mail.core.common.mail.EmailAddress
import app.k9mail.core.common.net.toHostname
import app.k9mail.core.common.net.toPort
import okhttp3.HttpUrl
internal class MockAutoconfigFetcher : AutoconfigFetcher {
val callArguments = mutableListOf<HttpUrl>()
val callArguments = mutableListOf<Pair<HttpUrl, EmailAddress>>()
val callCount: Int
get() = callArguments.size
private val results = mutableListOf<InputStream?>()
private val results = mutableListOf<AutoDiscoveryResult?>()
fun addResult(data: String?) {
results.add(data?.byteInputStream())
fun addResult(discoveryResult: AutoDiscoveryResult?) {
results.add(discoveryResult)
}
override suspend fun fetchAutoconfigFile(url: HttpUrl): InputStream? {
callArguments.add(url)
override suspend fun fetchAutoconfig(autoconfigUrl: HttpUrl, email: EmailAddress): AutoDiscoveryResult? {
callArguments.add(autoconfigUrl to email)
check(results.isNotEmpty()) { "fetchAutoconfigFile($url) called but no result provided" }
check(results.isNotEmpty()) {
"MockAutoconfigFetcher.fetchAutoconfig($autoconfigUrl) called but no result provided"
}
return results.removeAt(0)
}
companion object {
val RESULT_ONE = AutoDiscoveryResult(
incomingServerSettings = ImapServerSettings(
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
username = "irrelevant@domain.example",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.domain.example".toHostname(),
port = 465.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
username = "irrelevant@domain.example",
),
)
val RESULT_TWO = AutoDiscoveryResult(
incomingServerSettings = ImapServerSettings(
hostname = "imap.company.example".toHostname(),
port = 143.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordEncrypted,
username = "irrelevant@company.example",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.company.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordEncrypted,
username = "irrelevant@company.example",
),
)
}
}

View file

@ -1,69 +0,0 @@
package app.k9mail.autodiscovery.autoconfig
import app.k9mail.autodiscovery.api.AuthenticationType.PasswordCleartext
import app.k9mail.autodiscovery.api.AuthenticationType.PasswordEncrypted
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
import app.k9mail.autodiscovery.api.ConnectionSecurity.StartTLS
import app.k9mail.autodiscovery.api.ConnectionSecurity.TLS
import app.k9mail.autodiscovery.api.ImapServerSettings
import app.k9mail.autodiscovery.api.SmtpServerSettings
import app.k9mail.core.common.mail.EmailAddress
import app.k9mail.core.common.net.toHostname
import app.k9mail.core.common.net.toPort
import java.io.InputStream
internal class MockAutoconfigParser : AutoconfigParser {
val callArguments = mutableListOf<Pair<String, EmailAddress>>()
val callCount: Int
get() = callArguments.size
private val results = mutableListOf<AutoDiscoveryResult?>()
fun addResult(discoveryResult: AutoDiscoveryResult?) {
results.add(discoveryResult)
}
override fun parseSettings(inputStream: InputStream, email: EmailAddress): AutoDiscoveryResult? {
val data = String(inputStream.readBytes())
callArguments.add(data to email)
check(results.isNotEmpty()) { "parseSettings($data, $email) called but no result provided" }
return results.removeAt(0)
}
companion object {
val RESULT_ONE = AutoDiscoveryResult(
incomingServerSettings = ImapServerSettings(
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
username = "irrelevant@domain.example",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.domain.example".toHostname(),
port = 465.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
username = "irrelevant@domain.example",
),
)
val RESULT_TWO = AutoDiscoveryResult(
incomingServerSettings = ImapServerSettings(
hostname = "imap.company.example".toHostname(),
port = 143.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordEncrypted,
username = "irrelevant@company.example",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.company.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordEncrypted,
username = "irrelevant@company.example",
),
)
}
}

View file

@ -1,5 +1,6 @@
package app.k9mail.autodiscovery.autoconfig
import app.k9mail.autodiscovery.autoconfig.MockAutoconfigFetcher.Companion.RESULT_ONE
import app.k9mail.core.common.mail.toEmailAddress
import app.k9mail.core.common.net.toDomain
import assertk.assertThat
@ -16,15 +17,13 @@ class MxLookupAutoconfigDiscoveryTest {
private val mxResolver = MockMxResolver()
private val baseDomainExtractor = OkHttpBaseDomainExtractor()
private val urlProvider = MockAutoconfigUrlProvider()
private val fetcher = MockAutoconfigFetcher()
private val parser = MockAutoconfigParser()
private val autoconfigFetcher = MockAutoconfigFetcher()
private val discovery = MxLookupAutoconfigDiscovery(
mxResolver = SuspendableMxResolver(mxResolver),
baseDomainExtractor = baseDomainExtractor,
subDomainExtractor = RealSubDomainExtractor(baseDomainExtractor),
urlProvider = urlProvider,
fetcher = fetcher,
parser = SuspendableAutoconfigParser(parser),
autoconfigFetcher = autoconfigFetcher,
)
@Test
@ -32,24 +31,21 @@ class MxLookupAutoconfigDiscoveryTest {
val emailAddress = "user@company.example".toEmailAddress()
mxResolver.addResult("mx.emailprovider.example".toDomain())
urlProvider.addResult(listOf("https://ispdb.invalid/emailprovider.example".toHttpUrl()))
fetcher.addResult("data")
parser.addResult(MockAutoconfigParser.RESULT_ONE)
autoconfigFetcher.addResult(RESULT_ONE)
val autoDiscoveryRunnables = discovery.initDiscovery(emailAddress)
assertThat(autoDiscoveryRunnables).hasSize(1)
assertThat(mxResolver.callCount).isEqualTo(0)
assertThat(fetcher.callCount).isEqualTo(0)
assertThat(parser.callCount).isEqualTo(0)
assertThat(autoconfigFetcher.callCount).isEqualTo(0)
val discoveryResult = autoDiscoveryRunnables.first().run()
assertThat(mxResolver.callArguments).containsExactly("company.example".toDomain())
assertThat(urlProvider.callArguments).extracting { it.first }
.containsExactly("emailprovider.example".toDomain())
assertThat(fetcher.callCount).isEqualTo(1)
assertThat(parser.callCount).isEqualTo(1)
assertThat(discoveryResult).isEqualTo(MockAutoconfigParser.RESULT_ONE)
assertThat(autoconfigFetcher.callCount).isEqualTo(1)
assertThat(discoveryResult).isEqualTo(RESULT_ONE)
}
@Test
@ -60,7 +56,7 @@ class MxLookupAutoconfigDiscoveryTest {
addResult(listOf("https://ispdb.invalid/something.emailprovider.example".toHttpUrl()))
addResult(listOf("https://ispdb.invalid/emailprovider.example".toHttpUrl()))
}
fetcher.apply {
autoconfigFetcher.apply {
addResult(null)
addResult(null)
}
@ -72,8 +68,7 @@ class MxLookupAutoconfigDiscoveryTest {
"something.emailprovider.example".toDomain(),
"emailprovider.example".toDomain(),
)
assertThat(fetcher.callCount).isEqualTo(2)
assertThat(parser.callCount).isEqualTo(0)
assertThat(autoconfigFetcher.callCount).isEqualTo(2)
assertThat(discoveryResult).isNull()
}
@ -87,8 +82,7 @@ class MxLookupAutoconfigDiscoveryTest {
assertThat(mxResolver.callCount).isEqualTo(1)
assertThat(urlProvider.callCount).isEqualTo(0)
assertThat(fetcher.callCount).isEqualTo(0)
assertThat(parser.callCount).isEqualTo(0)
assertThat(autoconfigFetcher.callCount).isEqualTo(0)
assertThat(discoveryResult).isNull()
}
@ -102,8 +96,7 @@ class MxLookupAutoconfigDiscoveryTest {
assertThat(mxResolver.callCount).isEqualTo(1)
assertThat(urlProvider.callCount).isEqualTo(0)
assertThat(fetcher.callCount).isEqualTo(0)
assertThat(parser.callCount).isEqualTo(0)
assertThat(autoconfigFetcher.callCount).isEqualTo(0)
assertThat(discoveryResult).isNull()
}
}

View file

@ -1,9 +1,12 @@
package app.k9mail.autodiscovery.autoconfig
import assertk.all
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.isEqualTo
import assertk.assertions.isNull
import kotlin.test.assertNotNull
import assertk.assertions.isInstanceOf
import assertk.assertions.prop
import java.net.UnknownHostException
import kotlinx.coroutines.test.runTest
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
@ -11,17 +14,17 @@ import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.Test
class OkHttpAutoconfigFetcherTest {
private val fetcher = OkHttpAutoconfigFetcher(OkHttpClient.Builder().build())
class OkHttpFetcherTest {
private val fetcher = OkHttpFetcher(OkHttpClient.Builder().build())
@Test
fun shouldHandleNonexistentUrl() = runTest {
val nonExistentUrl =
"https://autoconfig.domain.invalid/mail/config-v1.1.xml?emailaddress=test%40domain.example".toHttpUrl()
val inputStream = fetcher.fetchAutoconfigFile(nonExistentUrl)
assertThat(inputStream).isNull()
assertFailure {
fetcher.fetch(nonExistentUrl)
}.isInstanceOf<UnknownHostException>()
}
@Test
@ -36,10 +39,10 @@ class OkHttpAutoconfigFetcherTest {
}
val url = server.url("/empty/")
val inputStream = fetcher.fetchAutoconfigFile(url)
val result = fetcher.fetch(url)
assertNotNull(inputStream) { inputStream ->
assertThat(inputStream.available()).isEqualTo(0)
assertThat(result).isInstanceOf<HttpFetchResult.SuccessResponse>().all {
prop(HttpFetchResult.SuccessResponse::inputStream).transform { it.available() }.isEqualTo(0)
}
}
}

View file

@ -145,7 +145,7 @@ mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:4.1.0"
turbine = "app.cash.turbine:turbine:0.12.3"
jdom2 = "org.jdom:jdom2:2.0.6.1"
icu4j-charset = "com.ibm.icu:icu4j-charset:72.1"
assertk = "com.willowtreeapps.assertk:assertk-jvm:0.26"
assertk = "com.willowtreeapps.assertk:assertk-jvm:0.26.1"
leakcanary-android = "com.squareup.leakcanary:leakcanary-android:2.9.1"