Merge pull request #6947 from thundernest/refactor_AutoconfigFetcher
Refactor `AutoconfigFetcher`
This commit is contained in:
commit
dbe09a9ac2
13 changed files with 193 additions and 199 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package app.k9mail.autodiscovery.autoconfig
|
||||
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
internal interface HttpFetcher {
|
||||
suspend fun fetch(url: HttpUrl): HttpFetchResult
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in a new issue