Repository: thunderbird/thunderbird-android (language: kotlin)

Issue:
Fix support for server settings with authentication type "None"
Currently we don't have an `AuthType.NONE` value that signifies that no authentication should be used (we support this for outgoing servers). The "no authentication" option is currently encoded by using an empty username and password. However, the new server settings screen doesn't properly support this (see https://github.com/thunderbird/thunderbird-android/issues/7524#issuecomment-1968795638).

Instead of making the server settings screen support the existing encoding, I suggest we add an `AuthType.NONE` value and make `ServerSettings.username` optional by using the `String?` type.


Discussion hints:
This requires changes in the way we import settings files. We currently don't support rewriting old server setting values to a new format.

Pull request: Fix editing server settings with authentication type "None"
Fixes #7682

Test command: ./gradlew :feature:account:server:settings:testDebugUnitTest --tests "app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsStateMapperKtTest" :mail:protocols:smtp:test --tests "com.fsck.k9.mail.transport.smtp.SmtpTransportTest" :feature:account:common:testDebugUnitTest --tests "app.k9mail.feature.account.common.domain.entity.AuthenticationTypeTest" :app:storage:testDebugUnitTest --tests "com.fsck.k9.preferences.migration.StorageMigrationTo25Test"

Fix patch:
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/ServerSettingsDescriptions.kt b/app/core/src/main/java/com/fsck/k9/preferences/ServerSettingsDescriptions.kt
index 3c0fdbc14d5..c65fad1d342 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/ServerSettingsDescriptions.kt
+++ b/app/core/src/main/java/com/fsck/k9/preferences/ServerSettingsDescriptions.kt
@@ -6,6 +6,7 @@ import com.fsck.k9.preferences.Settings.SettingsUpgrader
 import com.fsck.k9.preferences.Settings.StringSetting
 import com.fsck.k9.preferences.upgrader.ServerSettingsUpgraderTo92
 import com.fsck.k9.preferences.upgrader.ServerSettingsUpgraderTo94
+import com.fsck.k9.preferences.upgrader.ServerSettingsUpgraderTo95
 import java.util.TreeMap
 
 /**
@@ -60,6 +61,15 @@ internal class ServerSettingsDescriptions {
                         "XOAUTH2",
                     ),
                 ),
+                95 to NoDefaultStringEnumSetting(
+                    values = setOf(
+                        "PLAIN",
+                        "CRAM_MD5",
+                        "EXTERNAL",
+                        "XOAUTH2",
+                        "NONE",
+                    ),
+                ),
             ),
             USERNAME to versions(
                 1 to StringSetting(""),
@@ -77,6 +87,7 @@ internal class ServerSettingsDescriptions {
         mapOf(
             92 to ServerSettingsUpgraderTo92(),
             94 to ServerSettingsUpgraderTo94(),
+            95 to ServerSettingsUpgraderTo95(),
         )
     }
 
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
index cde3eb6482a..394fcec9139 100644
--- a/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
+++ b/app/core/src/main/java/com/fsck/k9/preferences/Settings.java
@@ -36,7 +36,7 @@ public class Settings {
      *
      * @see SettingsExporter
      */
-    public static final int VERSION = 93;
+    public static final int VERSION = 95;
 
     static Map<String, Object> validate(int version, Map<String, TreeMap<Integer, SettingsDescription>> settings,
             Map<String, String> importedSettings, boolean useDefaultValues) {
diff --git a/app/core/src/main/java/com/fsck/k9/preferences/upgrader/ServerSettingsUpgraderTo95.kt b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/ServerSettingsUpgraderTo95.kt
new file mode 100644
index 00000000000..eef4eb45cae
--- /dev/null
+++ b/app/core/src/main/java/com/fsck/k9/preferences/upgrader/ServerSettingsUpgraderTo95.kt
@@ -0,0 +1,23 @@
+package com.fsck.k9.preferences.upgrader
+
+import com.fsck.k9.preferences.ServerSettingsDescriptions.Companion.AUTHENTICATION_TYPE
+import com.fsck.k9.preferences.ServerSettingsDescriptions.Companion.PASSWORD
+import com.fsck.k9.preferences.ServerSettingsDescriptions.Companion.USERNAME
+import com.fsck.k9.preferences.Settings.SettingsUpgrader
+
+/**
+ * Updates server settings to use an authentication type value of "NONE" when appropriate.
+ */
+class ServerSettingsUpgraderTo95 : SettingsUpgrader {
+    override fun upgrade(settings: MutableMap<String, Any?>): Set<String> {
+        val username = settings[USERNAME] as? String
+
+        if (username.isNullOrEmpty()) {
+            settings[AUTHENTICATION_TYPE] = "NONE"
+            settings[USERNAME] = ""
+            settings[PASSWORD] = null
+        }
+
+        return emptySet()
+    }
+}
diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java b/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java
index 5cda80dba7b..04a55d5062e 100644
--- a/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java
+++ b/app/storage/src/main/java/com/fsck/k9/preferences/K9StoragePersister.java
@@ -19,7 +19,7 @@
 
 
 public class K9StoragePersister implements StoragePersister {
-    private static final int DB_VERSION = 24;
+    private static final int DB_VERSION = 25;
     private static final String DB_NAME = "preferences_storage";
 
     private final Context context;
diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrationTo25.kt b/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrationTo25.kt
new file mode 100644
index 00000000000..55962ae6387
--- /dev/null
+++ b/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrationTo25.kt
@@ -0,0 +1,67 @@
+package com.fsck.k9.preferences.migration
+
+import android.database.sqlite.SQLiteDatabase
+import com.squareup.moshi.JsonAdapter
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.Types
+
+/**
+ * Updates outgoing server settings to use an authentication type value of "NONE" when appropriate.
+ */
+class StorageMigrationTo25(
+    private val db: SQLiteDatabase,
+    private val migrationsHelper: StorageMigrationHelper,
+) {
+    fun convertToAuthTypeNone() {
+        val accountUuidsListValue = migrationsHelper.readValue(db, "accountUuids")
+        if (accountUuidsListValue.isNullOrEmpty()) {
+            return
+        }
+
+        val accountUuids = accountUuidsListValue.split(",")
+        for (accountUuid in accountUuids) {
+            convertToAuthTypeNoneForAccount(accountUuid)
+        }
+    }
+
+    private fun convertToAuthTypeNoneForAccount(accountUuid: String) {
+        val outgoingServerSettingsJson = migrationsHelper.readValue(db, "$accountUuid.outgoingServerSettings") ?: return
+
+        val adapter = createJsonAdapter()
+
+        adapter.fromJson(outgoingServerSettingsJson)?.let { settings ->
+            createUpdatedServerSettings(settings)?.let { newSettings ->
+                val json = adapter.toJson(newSettings)
+                migrationsHelper.writeValue(db, "$accountUuid.outgoingServerSettings", json)
+            }
+        }
+    }
+
+    private fun createUpdatedServerSettings(serverSettings: Map<String, Any?>): Map<String, Any?>? {
+        val username = serverSettings["username"] as? String?
+
+        return if (username.isNullOrEmpty()) {
+            serverSettings.toMutableMap().apply {
+                fixPortType()
+                this["authenticationType"] = "NONE"
+                this["username"] = ""
+                this["password"] = null
+            }
+        } else {
+            null
+        }
+    }
+
+    private fun MutableMap<String, Any?>.fixPortType() {
+        // This is so we don't end up with a port value of e.g. "993.0". It would still work, but it looks odd.
+        this["port"] = (this["port"] as? Double)?.toInt()
+    }
+
+    private fun createJsonAdapter(): JsonAdapter<Map<String, Any?>> {
+        val moshi = Moshi.Builder().build()
+
+        return moshi.adapter<Map<String, Any?>>(
+            Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java),
+        ).serializeNulls()
+    }
+}
diff --git a/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrations.kt b/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrations.kt
index e304efae2c8..4f824dff843 100644
--- a/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrations.kt
+++ b/app/storage/src/main/java/com/fsck/k9/preferences/migration/StorageMigrations.kt
@@ -31,5 +31,6 @@ internal object StorageMigrations {
         if (oldVersion < 22) StorageMigrationTo22(db, migrationsHelper).fixServerSettings()
         if (oldVersion < 23) StorageMigrationTo23(db, migrationsHelper).renameSendClientId()
         if (oldVersion < 24) StorageMigrationTo24(db, migrationsHelper).removeLegacyAuthenticationModes()
+        if (oldVersion < 25) StorageMigrationTo25(db, migrationsHelper).convertToAuthTypeNone()
     }
 }
diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt
index e06847867e7..5fd2a8dfa6d 100644
--- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt
+++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationType.kt
@@ -39,7 +39,7 @@ enum class AuthenticationType(
 
 fun AuthenticationType.toAuthType(): AuthType {
     return when (this) {
-        AuthenticationType.None -> AuthType.PLAIN
+        AuthenticationType.None -> AuthType.NONE
         AuthenticationType.PasswordCleartext -> AuthType.PLAIN
         AuthenticationType.PasswordEncrypted -> AuthType.CRAM_MD5
         AuthenticationType.ClientCertificate -> AuthType.EXTERNAL
@@ -53,6 +53,6 @@ fun AuthType.toAuthenticationType(): AuthenticationType {
         AuthType.CRAM_MD5 -> AuthenticationType.PasswordEncrypted
         AuthType.EXTERNAL -> AuthenticationType.ClientCertificate
         AuthType.XOAUTH2 -> AuthenticationType.OAuth2
-        else -> AuthenticationType.None
+        AuthType.NONE -> AuthenticationType.None
     }
 }
diff --git a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt
index 290215e253b..e444ad22196 100644
--- a/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt
+++ b/feature/account/server/settings/src/main/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapper.kt
@@ -23,7 +23,11 @@ fun AccountState.toOutgoingServerSettingsState(): State {
 }
 
 private fun AccountState.getOutgoingServerPassword(): String {
-    return outgoingServerSettings?.password ?: incomingServerSettings?.password ?: ""
+    return if (outgoingServerSettings?.authenticationType?.toAuthenticationType()?.isPasswordRequired == false) {
+        ""
+    } else {
+        outgoingServerSettings?.password ?: incomingServerSettings?.password ?: ""
+    }
 }
 
 private fun ServerSettings.toOutgoingServerSettingsState(password: String): State {
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/AuthType.java b/mail/common/src/main/java/com/fsck/k9/mail/AuthType.java
index ec634512f7b..d8a842b7260 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/AuthType.java
+++ b/mail/common/src/main/java/com/fsck/k9/mail/AuthType.java
@@ -22,4 +22,5 @@ public enum AuthType {
      * https://developers.google.com/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism
      */
     XOAUTH2,
+    NONE,
 }
diff --git a/mail/common/src/main/java/com/fsck/k9/mail/ServerSettings.kt b/mail/common/src/main/java/com/fsck/k9/mail/ServerSettings.kt
index ed2e783b8c0..f055b219cb7 100644
--- a/mail/common/src/main/java/com/fsck/k9/mail/ServerSettings.kt
+++ b/mail/common/src/main/java/com/fsck/k9/mail/ServerSettings.kt
@@ -15,9 +15,10 @@ data class ServerSettings @JvmOverloads constructor(
     val extra: Map<String, String?> = emptyMap(),
 ) {
     val isMissingCredentials: Boolean = when (authenticationType) {
+        AuthType.NONE -> false
         AuthType.EXTERNAL -> clientCertificateAlias == null
         AuthType.XOAUTH2 -> username.isBlank()
-        else -> username.isNotBlank() && password == null
+        else -> username.isBlank() || password.isNullOrBlank()
     }
 
     init {
diff --git a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
index fe46eff6b83..e6a98f62817 100644
--- a/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
+++ b/mail/protocols/smtp/src/main/java/com/fsck/k9/mail/transport/smtp/SmtpTransport.kt
@@ -147,50 +147,48 @@ class SmtpTransport(
             }
             parseOptionalSizeValue(extensions["SIZE"])
 
-            if (
-                username.isNotEmpty() &&
-                (!password.isNullOrEmpty() || AuthType.EXTERNAL == authType || AuthType.XOAUTH2 == authType)
-            ) {
-                when (authType) {
-                    AuthType.PLAIN -> {
-                        // try saslAuthPlain first, because it supports UTF-8 explicitly
-                        if (authPlainSupported) {
-                            saslAuthPlain()
-                        } else if (authLoginSupported) {
-                            saslAuthLogin()
-                        } else {
-                            throw MissingCapabilityException("AUTH PLAIN")
-                        }
-                    }
-                    AuthType.CRAM_MD5 -> {
-                        if (authCramMD5Supported) {
-                            saslAuthCramMD5()
-                        } else {
-                            throw MissingCapabilityException("AUTH CRAM-MD5")
-                        }
+            when (authType) {
+                AuthType.NONE -> {
+                    // The outgoing server is configured to not use any authentication. So do nothing.
+                }
+                AuthType.PLAIN -> {
+                    // try saslAuthPlain first, because it supports UTF-8 explicitly
+                    if (authPlainSupported) {
+                        saslAuthPlain()
+                    } else if (authLoginSupported) {
+                        saslAuthLogin()
+                    } else {
+                        throw MissingCapabilityException("AUTH PLAIN")
                     }
-                    AuthType.XOAUTH2 -> {
-                        if (oauthTokenProvider == null) {
-                            throw MessagingException("No OAuth2TokenProvider available.")
-                        } else if (authOAuthBearerSupported) {
-                            saslOAuth(OAuthMethod.OAUTHBEARER)
-                        } else if (authXoauth2Supported) {
-                            saslOAuth(OAuthMethod.XOAUTH2)
-                        } else {
-                            throw MissingCapabilityException("AUTH OAUTHBEARER")
-                        }
+                }
+                AuthType.CRAM_MD5 -> {
+                    if (authCramMD5Supported) {
+                        saslAuthCramMD5()
+                    } else {
+                        throw MissingCapabilityException("AUTH CRAM-MD5")
                     }
-                    AuthType.EXTERNAL -> {
-                        if (authExternalSupported) {
-                            saslAuthExternal()
-                        } else {
-                            throw MissingCapabilityException("AUTH EXTERNAL")
-                        }
+                }
+                AuthType.XOAUTH2 -> {
+                    if (oauthTokenProvider == null) {
+                        throw MessagingException("No OAuth2TokenProvider available.")
+                    } else if (authOAuthBearerSupported) {
+                        saslOAuth(OAuthMethod.OAUTHBEARER)
+                    } else if (authXoauth2Supported) {
+                        saslOAuth(OAuthMethod.XOAUTH2)
+                    } else {
+                        throw MissingCapabilityException("AUTH OAUTHBEARER")
                     }
-                    else -> {
-                        throw MessagingException("Unhandled authentication method found in server settings (bug).")
+                }
+                AuthType.EXTERNAL -> {
+                    if (authExternalSupported) {
+                        saslAuthExternal()
+                    } else {
+                        throw MissingCapabilityException("AUTH EXTERNAL")
                     }
                 }
+                else -> {
+                    throw MessagingException("Unhandled authentication method found in server settings (bug).")
+                }
             }
         } catch (e: MessagingException) {
             close()


Test patch:
diff --git a/app/core/src/test/java/com/fsck/k9/preferences/upgrader/ServerSettingsUpgraderTo95Test.kt b/app/core/src/test/java/com/fsck/k9/preferences/upgrader/ServerSettingsUpgraderTo95Test.kt
new file mode 100644
index 00000000000..49a6b3594be
--- /dev/null
+++ b/app/core/src/test/java/com/fsck/k9/preferences/upgrader/ServerSettingsUpgraderTo95Test.kt
@@ -0,0 +1,45 @@
+package com.fsck.k9.preferences.upgrader
+
+import assertk.assertThat
+import assertk.assertions.isEmpty
+import assertk.assertions.isEqualTo
+import kotlin.test.Test
+
+class ServerSettingsUpgraderTo95Test {
+    private val upgrader = ServerSettingsUpgraderTo95()
+
+    @Test
+    fun `server settings with empty username should be migrated to use authenticationType = NONE`() {
+        val mutableSettings = mutableMapOf<String, Any?>(
+            "authenticationType" to "PLAIN",
+            "username" to "",
+            "password" to "",
+        )
+
+        val result = upgrader.upgrade(mutableSettings)
+
+        assertThat(result).isEmpty()
+        assertThat(mutableSettings).isEqualTo(
+            mapOf<String, Any?>(
+                "authenticationType" to "NONE",
+                "username" to "",
+                "password" to null,
+            ),
+        )
+    }
+
+    @Test
+    fun `server settings with username and no password should not be modified`() {
+        val settings = mapOf<String, Any?>(
+            "authenticationType" to "PLAIN",
+            "username" to "user",
+            "password" to null,
+        )
+        val mutableSettings = settings.toMutableMap()
+
+        val result = upgrader.upgrade(mutableSettings)
+
+        assertThat(result).isEmpty()
+        assertThat(mutableSettings).isEqualTo(settings)
+    }
+}
diff --git a/app/storage/src/test/java/com/fsck/k9/preferences/migration/StorageMigrationTo25Test.kt b/app/storage/src/test/java/com/fsck/k9/preferences/migration/StorageMigrationTo25Test.kt
new file mode 100644
index 00000000000..ef3ddf94a07
--- /dev/null
+++ b/app/storage/src/test/java/com/fsck/k9/preferences/migration/StorageMigrationTo25Test.kt
@@ -0,0 +1,124 @@
+package com.fsck.k9.preferences.migration
+
+import assertk.assertThat
+import assertk.assertions.isEqualTo
+import assertk.assertions.key
+import com.fsck.k9.preferences.createPreferencesDatabase
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.Types
+import java.util.UUID
+import kotlin.test.Test
+import org.junit.After
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class StorageMigrationTo25Test {
+    private val database = createPreferencesDatabase()
+    private val migrationHelper = DefaultStorageMigrationHelper()
+    private val migration = StorageMigrationTo25(database, migrationHelper)
+
+    @After
+    fun tearDown() {
+        database.close()
+    }
+
+    @Test
+    fun `outgoing server settings with empty username should be migrated to use authenticationType = NONE`() {
+        val account = createAccount(
+            "outgoingServerSettings" to toJson(
+                "type" to "smtp",
+                "host" to "irrelevant.invalid",
+                "port" to 465,
+                "connectionSecurity" to "SSL_TLS_REQUIRED",
+                "authenticationType" to "PLAIN",
+                "username" to "",
+                "password" to null,
+                "clientCertificateAlias" to null,
+            ),
+        )
+        writeAccountUuids(account)
+
+        migration.convertToAuthTypeNone()
+
+        assertThat(migrationHelper.readAllValues(database))
+            .key("$account.outgoingServerSettings")
+            .isEqualTo(
+                """
+                {
+                    "type": "smtp",
+                    "host": "irrelevant.invalid",
+                    "port": 465,
+                    "connectionSecurity": "SSL_TLS_REQUIRED",
+                    "authenticationType": "NONE",
+                    "username": "",
+                    "password": null,
+                    "clientCertificateAlias": null
+                }
+                """.toCompactJson(),
+            )
+    }
+
+    @Test
+    fun `outgoing server settings with username and no password should not be modified`() {
+        val account = createAccount(
+            "outgoingServerSettings" to toJson(
+                "type" to "smtp",
+                "host" to "irrelevant.invalid",
+                "port" to 465,
+                "connectionSecurity" to "SSL_TLS_REQUIRED",
+                "authenticationType" to "PLAIN",
+                "username" to "user",
+                "password" to null,
+                "clientCertificateAlias" to null,
+            ),
+        )
+        writeAccountUuids(account)
+
+        migration.convertToAuthTypeNone()
+
+        assertThat(migrationHelper.readAllValues(database))
+            .key("$account.outgoingServerSettings")
+            .isEqualTo(
+                """
+                {
+                    "type": "smtp",
+                    "host": "irrelevant.invalid",
+                    "port": 465,
+                    "connectionSecurity": "SSL_TLS_REQUIRED",
+                    "authenticationType": "PLAIN",
+                    "username": "user",
+                    "password": null,
+                    "clientCertificateAlias": null
+                }
+                """.toCompactJson(),
+            )
+    }
+
+    private fun writeAccountUuids(vararg accounts: String) {
+        val accountUuids = accounts.joinToString(separator = ",")
+        migrationHelper.insertValue(database, "accountUuids", accountUuids)
+    }
+
+    private fun createAccount(vararg pairs: Pair<String, String>): String {
+        val accountUuid = UUID.randomUUID().toString()
+
+        for ((key, value) in pairs) {
+            migrationHelper.insertValue(database, "$accountUuid.$key", value)
+        }
+
+        return accountUuid
+    }
+
+    private fun toJson(vararg pairs: Pair<String, Any?>): String {
+        val moshi = Moshi.Builder().build()
+        val adapter = moshi.adapter<Map<String, Any?>>(
+            Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java),
+        ).serializeNulls()
+
+        return adapter.toJson(pairs.toMap()) ?: error("Failed to create JSON")
+    }
+
+    // Note: This only works for JSON strings where keys and values don't contain any spaces
+    private fun String.toCompactJson(): String = replace(" ", "").replace("\n", "")
+}
diff --git a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt
index 2fdca47716a..7b28f9c81ab 100644
--- a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt
+++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthenticationTypeTest.kt
@@ -20,7 +20,7 @@ class AuthenticationTypeTest {
                     AuthenticationType.PasswordEncrypted -> AuthType.CRAM_MD5
                     AuthenticationType.OAuth2 -> AuthType.XOAUTH2
                     AuthenticationType.ClientCertificate -> AuthType.EXTERNAL
-                    else -> AuthType.PLAIN
+                    AuthenticationType.None -> AuthType.NONE
                 },
             )
         }
@@ -39,7 +39,7 @@ class AuthenticationTypeTest {
                     AuthType.CRAM_MD5 -> AuthenticationType.PasswordEncrypted
                     AuthType.EXTERNAL -> AuthenticationType.ClientCertificate
                     AuthType.XOAUTH2 -> AuthenticationType.OAuth2
-                    else -> AuthenticationType.None
+                    AuthType.NONE -> AuthenticationType.None
                 },
             )
         }
diff --git a/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt
index 81828c85428..06b649cb15e 100644
--- a/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt
+++ b/feature/account/server/settings/src/test/kotlin/app/k9mail/feature/account/server/settings/ui/outgoing/OutgoingServerSettingsStateMapperKtTest.kt
@@ -63,7 +63,7 @@ class OutgoingServerSettingsStateMapperKtTest {
     }
 
     @Test
-    fun `should use password from incomingServerSettings when outgoingServerSettings contains no password`() {
+    fun `should use password from incomingServerSettings when outgoingServerSettings is missing a password`() {
         val accountState = AccountState(
             incomingServerSettings = IMAP_SERVER_SETTINGS,
             outgoingServerSettings = SMTP_SERVER_SETTINGS.copy(password = null),
diff --git a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt
index b8634cd31b4..da8741a6c1d 100644
--- a/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt
+++ b/mail/protocols/smtp/src/test/java/com/fsck/k9/mail/transport/smtp/SmtpTransportTest.kt
@@ -39,14 +39,14 @@ class SmtpTransportTest {
     private val oAuth2TokenProvider = createMockOAuth2TokenProvider()
 
     @Test
-    fun `open() should provide hostname`() {
+    fun `open() should issue EHLO command`() {
         val server = MockSmtpServer().apply {
             output("220 localhost Simple Mail Transfer Service Ready")
             expect("EHLO [127.0.0.1]")
             output("250-localhost Hello client.localhost")
             output("250 OK")
         }
-        val transport = startServerAndCreateSmtpTransport(server, password = null)
+        val transport = startServerAndCreateSmtpTransportWithoutAuthentication(server)
 
         transport.open()
 
@@ -55,14 +55,14 @@ class SmtpTransportTest {
     }
 
     @Test
-    fun `open() without AUTH LOGIN extension should connect without authentication`() {
+    fun `open() without AUTH LOGIN extension should connect when not using authentication`() {
         val server = MockSmtpServer().apply {
             output("220 localhost Simple Mail Transfer Service Ready")
             expect("EHLO [127.0.0.1]")
             output("250-localhost Hello client.localhost")
             output("250 OK")
         }
-        val transport = startServerAndCreateSmtpTransportWithoutPassword(server)
+        val transport = startServerAndCreateSmtpTransportWithoutAuthentication(server)
 
         transport.open()
 
@@ -473,7 +473,7 @@ class SmtpTransportTest {
             expect("HELO [127.0.0.1]")
             output("250 localhost")
         }
-        val transport = startServerAndCreateSmtpTransportWithoutPassword(server)
+        val transport = startServerAndCreateSmtpTransportWithoutAuthentication(server)
 
         transport.open()
 
@@ -885,8 +885,8 @@ class SmtpTransportTest {
         server.verifyInteractionCompleted()
     }
 
-    private fun startServerAndCreateSmtpTransportWithoutPassword(server: MockSmtpServer): SmtpTransport {
-        return startServerAndCreateSmtpTransport(server, AuthType.PLAIN, ConnectionSecurity.NONE, null)
+    private fun startServerAndCreateSmtpTransportWithoutAuthentication(server: MockSmtpServer): SmtpTransport {
+        return startServerAndCreateSmtpTransport(server, AuthType.NONE, ConnectionSecurity.NONE, password = null)
     }
 
     private fun startServerAndCreateSmtpTransport(