diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-03 18:36:42 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-03 18:36:42 +0100 |
| commit | cc880cf62ea8391f693f034858578cd4b693043b (patch) | |
| tree | 767a1ebb24b6c4e94bd0c3073a2cb5e98d406c0a /app/src | |
| parent | 9dba2efd0b8d9d117956d671468361acac060482 (diff) | |
| download | HardenedPass-cc880cf62ea8391f693f034858578cd4b693043b.tar.gz HardenedPass-cc880cf62ea8391f693f034858578cd4b693043b.zip | |
feat(app): drop two sort orders, fix the proxy user name
The Compose list shows folders and entries in separate groups, so the
files first and type independent sort orders did nothing visible. They
are removed; a stored one migrates to folders first, and any unknown
value reads as folders first instead of throwing. The OpenKeychain SSH
key ID, which nothing reads any more, is removed once.
GitSettings wrote the proxy user name to the proxy preferences but read
it from the main settings, so ProxyUtils never authenticated.
Adds the pure parts of the settings port: proxy port parsing, HEAD
status and the commit log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src')
20 files changed, 274 insertions, 42 deletions
diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt new file mode 100644 index 00000000..eca6770c --- /dev/null +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * SPDX-License-Identifier: GPL-3.0-only + */ + +package app.passwordstore.ui.settings + +import app.passwordstore.util.extensions.hash +import app.passwordstore.util.extensions.time +import app.passwordstore.util.git.GitCommit +import java.io.IOException +import logcat.LogPriority.ERROR +import logcat.asLog +import logcat.logcat +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.api.errors.GitAPIException +import org.eclipse.jgit.api.errors.JGitInternalException +import org.eclipse.jgit.lib.Constants +import org.eclipse.jgit.lib.Repository +import org.eclipse.jgit.lib.RepositoryState + +private const val TAG = "SettingsModel" + +/** + * The proxy port typed as [text]: -1 when empty (no port, as stored before any was set), null when + * it is not a port number from 1 to 65535. + */ +fun proxyPort(text: String): Int? { + val trimmed = text.trim() + if (trimmed.isEmpty()) return -1 + return trimmed.toIntOrNull()?.takeIf { it in 1..65535 } +} + +/** Where HEAD points, as the git config screen reports it. */ +sealed interface HeadStatus { + data class Branch(val name: String) : HeadStatus + + data class Detached(val hash: String) : HeadStatus + + data object Missing : HeadStatus +} + +/** [repo]'s HEAD: a branch (also one without commits yet), a detached commit, or missing. */ +fun headStatus(repo: Repository): HeadStatus = + try { + val head = repo.findRef(Constants.HEAD) + when { + head == null -> HeadStatus.Missing + head.isSymbolic -> HeadStatus.Branch(Repository.shortenRefName(head.target.name)) + else -> + head.objectId?.let { HeadStatus.Detached(it.abbreviate(8).name()) } ?: HeadStatus.Missing + } + } catch (e: IOException) { + logcat(TAG, ERROR) { e.asLog() } + HeadStatus.Missing + } + +/** Whether a rebase or a merge is in progress, so "Abort rebase" has something to abort. */ +fun needsAbort(repo: Repository): Boolean = + repo.repositoryState.let { it.isRebasing || it == RepositoryState.MERGING } + +/** + * The commits reachable from HEAD, newest first; empty without a repository or a commit. Blocking: + * call it off the main thread. + */ +// ponytail: the whole log at once, as the old screen did (tested there with 1000 commits); page it +// if a store's history ever makes this slow. +fun gitLog(repo: Repository?): List<GitCommit> { + if (repo == null) return emptyList() + return try { + Git(repo).log().call().map { GitCommit(it.hash, it.shortMessage, it.authorIdent.name, it.time) } + } catch (e: GitAPIException) { + logcat(TAG, ERROR) { e.asLog() } + emptyList() + } catch (e: JGitInternalException) { + logcat(TAG, ERROR) { e.asLog() } + emptyList() + } +} diff --git a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt index d315daf3..004028c4 100644 --- a/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt +++ b/app/src/main/java/app/passwordstore/util/settings/GitSettings.kt @@ -108,7 +108,7 @@ constructor( } var proxyUsername - get() = settings.getString(PreferenceKeys.PROXY_USERNAME) + get() = proxySettings.getString(PreferenceKeys.PROXY_USERNAME) set(value) { proxySettings.edit { putString(PreferenceKeys.PROXY_USERNAME, value) } } diff --git a/app/src/main/java/app/passwordstore/util/settings/Migrations.kt b/app/src/main/java/app/passwordstore/util/settings/Migrations.kt index 06075655..82847f02 100644 --- a/app/src/main/java/app/passwordstore/util/settings/Migrations.kt +++ b/app/src/main/java/app/passwordstore/util/settings/Migrations.kt @@ -32,6 +32,8 @@ fun runMigrations(filesDirPath: String, sharedPrefs: SharedPreferences, gitSetti migrateToDiceware(sharedPrefs) removeExternalStorageProperties(sharedPrefs) removeCurrentBranchValue(sharedPrefs) + migrateRemovedSortOrders(sharedPrefs) + removeOpenKeystoreKeyId(sharedPrefs) } /** @@ -194,3 +196,20 @@ private fun removeExternalStorageProperties(prefs: SharedPreferences) { } } } + +/** + * "Files first" and "type independent" were removed with the Compose list, which shows folders and + * entries in separate groups: a stored one becomes folders first, the list's default. + */ +private fun migrateRemovedSortOrders(sharedPrefs: SharedPreferences) { + val stored = sharedPrefs.getString(PreferenceKeys.SORT_ORDER) ?: return + if (stored != "FILE_FIRST" && stored != "INDEPENDENT") return + logcat(TAG, INFO) { "Replacing the removed sort order $stored" } + sharedPrefs.edit { putString(PreferenceKeys.SORT_ORDER, PasswordSortOrder.FOLDER_FIRST.name) } +} + +/** OpenKeychain SSH support is gone and nothing reads the remembered key ID any more. */ +private fun removeOpenKeystoreKeyId(sharedPrefs: SharedPreferences) { + if (!sharedPrefs.contains(PreferenceKeys.SSH_OPENKEYSTORE_KEYID)) return + sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) } +} diff --git a/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt b/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt index 224d9d66..921e9bc4 100644 --- a/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt +++ b/app/src/main/java/app/passwordstore/util/settings/PasswordSortOrder.kt @@ -18,11 +18,6 @@ enum class PasswordSortOrder(val comparator: java.util.Comparator<PasswordItem>) (p1.type + p1.name).compareTo(p2.type + p2.name, ignoreCase = true) } ), - INDEPENDENT( - Comparator { p1: PasswordItem, p2: PasswordItem -> - p1.name.compareTo(p2.name, ignoreCase = true) - } - ), RECENTLY_USED( Comparator { p1: PasswordItem, p2: PasswordItem -> val recentHistory = @@ -36,18 +31,18 @@ enum class PasswordSortOrder(val comparator: java.util.Comparator<PasswordItem>) else -> p1.name.compareTo(p2.name, ignoreCase = true) } } - ), - FILE_FIRST( - Comparator { p1: PasswordItem, p2: PasswordItem -> - (p2.type + p1.name).compareTo(p1.type + p2.name, ignoreCase = true) - } ); companion object { + /** + * The stored sort order; anything else (the removed "files first" and "type independent", a + * value from another version) reads as [FOLDER_FIRST]. + */ @JvmStatic fun getSortOrder(settings: SharedPreferences): PasswordSortOrder { - return valueOf(settings.getString(PreferenceKeys.SORT_ORDER) ?: FOLDER_FIRST.name) + val stored = settings.getString(PreferenceKeys.SORT_ORDER) + return entries.firstOrNull { it.name == stored } ?: FOLDER_FIRST } } } diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt index ca570dc4..f8cbf1e4 100644 --- a/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt +++ b/app/src/main/java/app/passwordstore/util/viewmodel/SearchableRepositoryViewModel.kt @@ -71,12 +71,7 @@ private fun PasswordItem.Companion.makeComparator( directoryStructure: DirectoryStructure, ): Comparator<PasswordItem> { return when (typeSortOrder) { - PasswordSortOrder.FOLDER_FIRST -> compareBy { it.type } - // In order to let INDEPENDENT not distinguish between items based on their type, we - // simply - // declare them all equal at this stage. - PasswordSortOrder.INDEPENDENT -> Comparator { _, _ -> 0 } - PasswordSortOrder.FILE_FIRST -> compareByDescending { it.type } + PasswordSortOrder.FOLDER_FIRST -> compareBy<PasswordItem> { it.type } PasswordSortOrder.RECENTLY_USED -> PasswordSortOrder.RECENTLY_USED.comparator } .then( diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 493391a8..618aac0d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -96,8 +96,6 @@ <string name="pref_recursive_filter_summary">Findet Passwörter auch in Unterordnern.</string> <string name="pref_sort_order_title">Passwortsortierung</string> <string name="pref_folder_first_sort_order">Ordner zuerst</string> - <string name="pref_file_first_sort_order">Dateien zuerst</string> - <string name="pref_type_independent_sort_order">typunabhängig</string> <string name="pref_recently_used_sort_order">Zuletzt verwendet</string> <string name="pref_category_autofill_title">Automatisch ausfüllen</string> <string name="pref_autofill_enable_title">Autofill aktivieren</string> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d2a469e7..4cc7d29d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -94,8 +94,6 @@ <string name="pref_recursive_filter_summary">Cherche le mot de passe dans tous les sous-répertoires du répertoire actuel.</string> <string name="pref_sort_order_title">Ordre de tri des mots de passe</string> <string name="pref_folder_first_sort_order">Dossiers en premier</string> - <string name="pref_file_first_sort_order">Fichiers en premier</string> - <string name="pref_type_independent_sort_order">Indifférent au type d\'entrée</string> <string name="pref_recently_used_sort_order">Récemment utilisé</string> <string name="pref_category_autofill_title">Saisie automatique</string> <string name="pref_autofill_enable_title">Saisie automatique</string> diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index ad4c663d..34b1ce6a 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -97,8 +97,6 @@ <string name="pref_directory_structure">Organización dos contrasinais</string> <string name="pref_sort_order_title">Orde para mostrar contrasinais</string> <string name="pref_folder_first_sort_order">Primeiro cartafoles</string> - <string name="pref_file_first_sort_order">Primeiro ficheiros</string> - <string name="pref_type_independent_sort_order">Independentemente do tipo</string> <string name="pref_recently_used_sort_order">Usadas recentemente</string> <string name="pref_category_autofill_title">Completado automático</string> <string name="pref_autofill_enable_title">Activar completado automático</string> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8edc9d3d..9b67482c 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -94,8 +94,6 @@ <string name="pref_recursive_filter_summary">Trova ricorsivamente le password della directory corrente.</string> <string name="pref_sort_order_title">Ordine password</string> <string name="pref_folder_first_sort_order">Prima le cartelle</string> - <string name="pref_file_first_sort_order">Prima i file</string> - <string name="pref_type_independent_sort_order">Indipendente dal tipo</string> <string name="pref_recently_used_sort_order">Usato di recente</string> <string name="pref_category_autofill_title">Compilazione automatica</string> <string name="pref_autofill_enable_title">Attiva la compilazione automatica</string> diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index aaa438dd..06e0a555 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -95,8 +95,6 @@ <string name="pref_directory_structure">암허 파일 정리</string> <string name="pref_sort_order_title">암호 정렬 순서</string> <string name="pref_folder_first_sort_order">폴더 우선</string> - <string name="pref_file_first_sort_order">파일 우선</string> - <string name="pref_type_independent_sort_order">타입순</string> <string name="pref_recently_used_sort_order">최신순</string> <string name="pref_category_autofill_title">자동완성</string> <string name="pref_autofill_enable_title">자동완성 활성화</string> diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 815cf41c..6bdb966c 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -94,8 +94,6 @@ <string name="pref_directory_structure">Organizacja plików haseł</string> <string name="pref_sort_order_title">Kolejność sortowania haseł</string> <string name="pref_folder_first_sort_order">Najpierw foldery</string> - <string name="pref_file_first_sort_order">Najpierw pliki</string> - <string name="pref_type_independent_sort_order">Niezależnie od typu</string> <string name="pref_recently_used_sort_order">Ostatnio używane</string> <string name="pref_category_autofill_title">Autouzupełnianie</string> <string name="pref_autofill_enable_title">Włącz autouzupełnianie</string> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a1d1a38c..8f40c831 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -96,8 +96,6 @@ <string name="pref_recursive_filter_summary">Encontrar senhas do diretório corrente recursivamente.</string> <string name="pref_sort_order_title">Ordenação da Senha</string> <string name="pref_folder_first_sort_order">Pastas primeiro</string> - <string name="pref_file_first_sort_order">Arquivos primeiro</string> - <string name="pref_type_independent_sort_order">Tipo independente</string> <string name="pref_recently_used_sort_order">Usado recentemente</string> <string name="pref_category_autofill_title">Preenchimento Automático</string> <string name="pref_autofill_enable_title">Ativar preenchimento automático</string> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index d0934a4d..af3078bc 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -99,8 +99,6 @@ <string name="pref_recursive_filter_summary">Рекурсивный поиск паролей в текущей директории</string> <string name="pref_sort_order_title">Порядок сортировки паролей</string> <string name="pref_folder_first_sort_order">Сначала папки</string> - <string name="pref_file_first_sort_order">Сначала файлы</string> - <string name="pref_type_independent_sort_order">Типонезависимый</string> <string name="pref_recently_used_sort_order">Недавно использованные</string> <string name="pref_category_autofill_title">Автозаполнение</string> <string name="pref_autofill_enable_title">Включить автозаполнение</string> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ddcdda1a..959d5fac 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -95,8 +95,6 @@ <string name="pref_recursive_filter_summary">递归式寻找当前目录的密码</string> <string name="pref_sort_order_title">密码排序顺序</string> <string name="pref_folder_first_sort_order">目录优先</string> - <string name="pref_file_first_sort_order">文件优先</string> - <string name="pref_type_independent_sort_order">类型无关</string> <string name="pref_recently_used_sort_order">最近使用</string> <string name="pref_category_autofill_title">自动填充</string> <string name="pref_autofill_enable_title">启用自动填充</string> diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 9f44710b..2891987d 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -6,14 +6,10 @@ <resources> <string-array name="sort_order_entries"> <item>@string/pref_folder_first_sort_order</item> - <item>@string/pref_file_first_sort_order</item> - <item>@string/pref_type_independent_sort_order</item> <item>@string/pref_recently_used_sort_order</item> </string-array> <string-array name="sort_order_values"> <item>FOLDER_FIRST</item> - <item>FILE_FIRST</item> - <item>INDEPENDENT</item> <item>RECENTLY_USED</item> </string-array> <string-array name="pwgen_provider_labels"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 59ba74cb..dc9140f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -116,8 +116,6 @@ <string name="pref_directory_structure">Password file organization</string> <string name="pref_sort_order_title">Password sort order</string> <string name="pref_folder_first_sort_order">Folders first</string> - <string name="pref_file_first_sort_order">Files first</string> - <string name="pref_type_independent_sort_order">Type independent</string> <string name="pref_recently_used_sort_order">Recently used</string> <string name="pref_category_autofill_title">Autofill</string> <string name="pref_autofill_enable_title">Enable Autofill</string> diff --git a/app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt b/app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt new file mode 100644 index 00000000..693e07dd --- /dev/null +++ b/app/src/test/java/app/passwordstore/ui/settings/SettingsModelTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * SPDX-License-Identifier: GPL-3.0-only + */ + +package app.passwordstore.ui.settings + +import app.passwordstore.util.git.GitCommit +import java.io.File +import java.nio.file.Files +import kotlin.test.AfterTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNull +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.PersonIdent +import org.eclipse.jgit.revwalk.RevCommit +import org.eclipse.jgit.util.FS + +class SettingsModelTest { + + private val root = Files.createTempDirectory("aps-settings").toFile() + private val identity = PersonIdent("test", "test@example.org") + + init { + // Ignore the developer's ~/.gitconfig (default branch, commit signing). + FS.DETECTED.setUserHome(root) + } + + @AfterTest fun cleanUp() = root.deleteRecursively().let {} + + private fun repo(): Git = + Git.init().setInitialBranch("master").setDirectory(File(root, "store")).call() + + private fun Git.commitFile(name: String, message: String): RevCommit { + File(repository.workTree, name).writeText(message) + add().addFilepattern(name).call() + return commit() + .setMessage(message) + .setAuthor(identity) + .setCommitter(identity) + .setSign(false) + .call() + } + + @Test + fun proxyPorts() { + assertEquals(-1, proxyPort("")) + assertEquals(-1, proxyPort(" ")) + assertEquals(8080, proxyPort("8080")) + assertEquals(8080, proxyPort(" 8080 ")) + assertEquals(1, proxyPort("1")) + assertEquals(65535, proxyPort("65535")) + assertNull(proxyPort("0")) + assertNull(proxyPort("65536")) + assertNull(proxyPort("99999999999")) + assertNull(proxyPort("-1")) + assertNull(proxyPort("80a")) + } + + @Test + fun headStatusFollowsHead() { + repo().use { git -> + // A new repository: HEAD names a branch that has no commit yet. + assertEquals(HeadStatus.Branch("master"), headStatus(git.repository)) + val first = git.commitFile("a.gpg", "Add a") + git.commitFile("b.gpg", "Add b") + assertEquals(HeadStatus.Branch("master"), headStatus(git.repository)) + assertFalse(needsAbort(git.repository)) + git.checkout().setName(first.name).call() + assertEquals(HeadStatus.Detached(first.name.take(8)), headStatus(git.repository)) + } + } + + @Test + fun gitLogIsNewestFirst() { + assertEquals(emptyList<GitCommit>(), gitLog(null)) + repo().use { git -> + // No commit yet: JGit's NoHeadException is an empty log, not a crash. + assertEquals(emptyList<GitCommit>(), gitLog(git.repository)) + val first = git.commitFile("a.gpg", "Add a") + val second = git.commitFile("b.gpg", "Add b") + val log = gitLog(git.repository) + assertEquals(listOf("Add b", "Add a"), log.map(GitCommit::shortMessage)) + assertEquals(listOf(second.name, first.name), log.map(GitCommit::hash)) + assertEquals("test", log[0].authorName) + } + } +} diff --git a/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt b/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt new file mode 100644 index 00000000..6b30c248 --- /dev/null +++ b/app/src/test/java/app/passwordstore/util/settings/GitSettingsTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * SPDX-License-Identifier: GPL-3.0-only + */ + +package app.passwordstore.util.settings + +import app.passwordstore.util.extensions.getString +import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull + +class GitSettingsTest { + + @Test + fun proxyUsernameIsReadWhereItIsWritten() { + val settings = SPMockBuilder().createSharedPreferences() + val proxy = SPMockBuilder().createSharedPreferences() + val gitSettings = + GitSettings(settings, SPMockBuilder().createSharedPreferences(), proxy, "/nonexistent") + gitSettings.proxyUsername = "proxy-user" + assertEquals("proxy-user", gitSettings.proxyUsername) + assertEquals("proxy-user", proxy.getString(PreferenceKeys.PROXY_USERNAME)) + assertNull(settings.getString(PreferenceKeys.PROXY_USERNAME)) + } +} diff --git a/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt b/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt index 62109563..44bd12ba 100644 --- a/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt +++ b/app/src/test/java/app/passwordstore/util/settings/MigrationsTest.kt @@ -184,4 +184,29 @@ class MigrationsTest { assertFalse { sharedPrefs.contains(PreferenceKeys.GIT_EXTERNAL_REPO) } assertTrue { sharedPrefs.getBoolean(PreferenceKeys.GIT_EXTERNAL_MIGRATED, false) } } + + @Test + fun removedSortOrdersFallBackToFoldersFirst() { + val gitSettings = + GitSettings(sharedPrefs, encryptedSharedPreferences, proxySharedPreferences, filesDir) + for (removed in listOf("FILE_FIRST", "INDEPENDENT")) { + sharedPrefs.edit { putString(PreferenceKeys.SORT_ORDER, removed) } + runMigrations(filesDir, sharedPrefs, gitSettings) + assertEquals("FOLDER_FIRST", sharedPrefs.getString(PreferenceKeys.SORT_ORDER)) + } + sharedPrefs.edit { putString(PreferenceKeys.SORT_ORDER, "RECENTLY_USED") } + runMigrations(filesDir, sharedPrefs, gitSettings) + assertEquals("RECENTLY_USED", sharedPrefs.getString(PreferenceKeys.SORT_ORDER)) + } + + @Test + fun staleOpenKeystoreKeyIdIsRemoved() { + sharedPrefs.edit { putString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID, "0123456789ABCDEF") } + runMigrations( + filesDir, + sharedPrefs, + GitSettings(sharedPrefs, encryptedSharedPreferences, proxySharedPreferences, filesDir), + ) + assertFalse(sharedPrefs.contains(PreferenceKeys.SSH_OPENKEYSTORE_KEYID)) + } } diff --git a/app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt b/app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt new file mode 100644 index 00000000..34348692 --- /dev/null +++ b/app/src/test/java/app/passwordstore/util/settings/PasswordSortOrderTest.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2026 Danilo M. <danix@danix.xyz> + * SPDX-License-Identifier: GPL-3.0-only + */ + +package app.passwordstore.util.settings + +import androidx.core.content.edit +import com.github.ivanshafran.sharedpreferencesmock.SPMockBuilder +import kotlin.test.Test +import kotlin.test.assertEquals + +class PasswordSortOrderTest { + + @Test + fun unknownAndRemovedValuesReadAsFoldersFirst() { + val prefs = SPMockBuilder().createSharedPreferences() + assertEquals(PasswordSortOrder.FOLDER_FIRST, PasswordSortOrder.getSortOrder(prefs)) + for (value in listOf("FILE_FIRST", "INDEPENDENT", "bogus")) { + prefs.edit { putString(PreferenceKeys.SORT_ORDER, value) } + assertEquals(PasswordSortOrder.FOLDER_FIRST, PasswordSortOrder.getSortOrder(prefs)) + } + prefs.edit { putString(PreferenceKeys.SORT_ORDER, "RECENTLY_USED") } + assertEquals(PasswordSortOrder.RECENTLY_USED, PasswordSortOrder.getSortOrder(prefs)) + } +} |
