aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/test/java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-03 08:36:30 +0100
committerDanilo M. <danix@danix.xyz>2026-11-03 08:36:30 +0100
commit8dad89ec9a37ab53a0de8983afafb0ef82d6d2d5 (patch)
treeddb31c60c570a7a6353cbd8b104ae682b4431784 /app/src/test/java
parentba977fd26b28caa1c49e57993516359fe1afaa27 (diff)
downloadHardenedPass-8dad89ec9a37ab53a0de8983afafb0ef82d6d2d5.tar.gz
HardenedPass-8dad89ec9a37ab53a0de8983afafb0ef82d6d2d5.zip
feat(app): add the editor model and the picker's folder listing
Pure functions for the Compose editor: the form and the pass file it writes, validation, the target file, the overwrite check, an atomic write, OTP URI building and the random generator's options. The folder picker lists subfolders with the list's hidden filter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/test/java')
-rw-r--r--app/src/test/java/app/passwordstore/ui/editor/EditorModelTest.kt152
-rw-r--r--app/src/test/java/app/passwordstore/ui/list/ListModelTest.kt12
2 files changed, 164 insertions, 0 deletions
diff --git a/app/src/test/java/app/passwordstore/ui/editor/EditorModelTest.kt b/app/src/test/java/app/passwordstore/ui/editor/EditorModelTest.kt
new file mode 100644
index 00000000..81174d05
--- /dev/null
+++ b/app/src/test/java/app/passwordstore/ui/editor/EditorModelTest.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.ui.editor
+
+import app.passwordstore.R
+import app.passwordstore.data.passfile.PasswordEntry
+import app.passwordstore.passgen.random.PasswordOption
+import app.passwordstore.util.time.UserClock
+import app.passwordstore.util.totp.UriTotpFinder
+import java.io.File
+import java.nio.file.Files
+import kotlin.test.AfterTest
+import kotlin.test.Test
+import kotlin.test.assertContentEquals
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+
+class EditorModelTest {
+
+ private val root = Files.createTempDirectory("aps").toFile()
+
+ @AfterTest
+ fun cleanUp() {
+ root.deleteRecursively()
+ }
+
+ private fun entry(content: String) =
+ PasswordEntry(UserClock(), UriTotpFinder(), content.encodeToByteArray())
+
+ @Test
+ fun editingWithoutChangesKeepsThePassFile() {
+ val files =
+ listOf(
+ "hunter2\nusername: alice\nurl: https://example.org\n",
+ "s3cret\notpauth://totp/example.com:carol?secret=JBSWY3DPEHPK3PXP&issuer=example.com\n",
+ )
+ for (content in files) assertEquals(content, passContent(editorForm(entry(content), "", "x")))
+ }
+
+ @Test
+ fun formFromAnEntrySplitsItsFields() {
+ val form =
+ editorForm(entry("hunter2\nusername: alice\nurl: https://example.org\n"), "work", "mail")
+ assertEquals(EditorForm("mail", "work", "hunter2", "alice", "url: https://example.org\n"), form)
+ }
+
+ @Test
+ fun newEntryContent() {
+ assertEquals(
+ "pw\nusername: bob\nnote: x",
+ passContent(EditorForm(name = "n", password = "pw", username = "bob", extra = "note: x")),
+ )
+ assertEquals("pw\n", passContent(EditorForm(name = "n", password = "pw")))
+ }
+
+ @Test
+ fun formErrors() {
+ assertEquals(R.string.file_toast_text, formError(EditorForm(name = " ", password = "pw")))
+ assertEquals(
+ R.string.invalid_filename_text,
+ formError(EditorForm(name = "a/b", password = "pw")),
+ )
+ assertEquals(R.string.empty_toast_text, formError(EditorForm(name = "a", username = "bob")))
+ assertNull(formError(EditorForm(name = "a", extra = "note")))
+ assertNull(formError(EditorForm(name = "a", password = "pw")))
+ }
+
+ @Test
+ fun entryFileTrimsTheNameAndUsesTheFolder() {
+ assertEquals(
+ File(root, "work/mail.gpg"),
+ entryFile(root, EditorForm(name = " mail ", dir = "work")),
+ )
+ assertEquals(File(root, "mail.gpg"), entryFile(root, EditorForm(name = "mail")))
+ }
+
+ @Test
+ fun overwritesOnlyAnotherFile() {
+ val taken = File(root, "a.gpg").apply { writeText("x") }
+ assertFalse(overwrites(File(root, "work/b.gpg"), null))
+ assertTrue(overwrites(taken, null))
+ assertFalse(overwrites(taken, taken))
+ assertTrue(overwrites(taken, File(root, "work/b.gpg")))
+ }
+
+ @Test
+ fun writeEntryCreatesTheFolderAndReplacesTheFile() {
+ val target = File(root, "new/dir/a.gpg")
+ writeEntry(target, byteArrayOf(1))
+ writeEntry(target, byteArrayOf(2, 3))
+ assertContentEquals(byteArrayOf(2, 3), target.readBytes())
+ assertEquals(listOf("a.gpg"), target.parentFile!!.list()!!.toList())
+ }
+
+ @Test
+ fun linesGoOnTheirOwnLine() {
+ assertEquals("otpauth://x", withLine("", "otpauth://x"))
+ assertEquals("a\notpauth://x", withLine("a", "otpauth://x"))
+ assertEquals("a\notpauth://x", withLine("a\n", "otpauth://x"))
+ }
+
+ @Test
+ fun manualTotpUriIsReadBackByTheParser() {
+ val uri = totpUri(" JBSW Y3DP EHPK 3PXP ", "Example Co")!!
+ assertEquals("JBSWY3DPEHPK3PXP", UriTotpFinder().findSecret(uri))
+ assertEquals("Example Co", UriTotpFinder().findIssuer(uri))
+ assertTrue(entry("PLACEHOLDER\n$uri").hasTotp())
+ assertEquals("otpauth://totp?secret=JBSWY3DPEHPK3PXP", totpUri("JBSWY3DPEHPK3PXP", " "))
+ assertNull(totpUri(" ", "Example Co"))
+ }
+
+ @Test
+ fun randomOptionsFollowTheCheckboxes() {
+ assertEquals(listOf(PasswordOption.FullyRandom), RandomSpec().options())
+ assertEquals(
+ listOf(
+ PasswordOption.NoDigits,
+ PasswordOption.AtLeastOneSymbol,
+ PasswordOption.NoUppercaseLetters,
+ PasswordOption.NoLowercaseLetters,
+ PasswordOption.NoAmbiguousCharacters,
+ ),
+ RandomSpec(
+ numerals = false,
+ symbols = true,
+ uppercase = false,
+ lowercase = false,
+ ambiguous = false,
+ pronounceable = true,
+ )
+ .options(),
+ )
+ }
+
+ @Test
+ fun randomPasswordOrWhyNot() {
+ assertEquals(20, randomPassword(RandomSpec()).value.length)
+ assertEquals(
+ R.string.pwgen_no_chars_error,
+ randomPassword(RandomSpec(numerals = false, uppercase = false, lowercase = false)).error,
+ )
+ assertEquals(
+ R.string.pwgen_length_too_short_error,
+ randomPassword(RandomSpec(length = 2)).error,
+ )
+ }
+}
diff --git a/app/src/test/java/app/passwordstore/ui/list/ListModelTest.kt b/app/src/test/java/app/passwordstore/ui/list/ListModelTest.kt
index 60e3020a..cf12deef 100644
--- a/app/src/test/java/app/passwordstore/ui/list/ListModelTest.kt
+++ b/app/src/test/java/app/passwordstore/ui/list/ListModelTest.kt
@@ -90,4 +90,16 @@ class ListModelTest {
assertEquals(listOf(z.absolutePath, y.absolutePath), rows.folders.map { it.path })
assertEquals(listOf(b.absolutePath, a.absolutePath), rows.entries.map { it.path })
}
+
+ @Test
+ fun pickerOffersFoldersByName() {
+ folder("b")
+ folder("A")
+ folder(".hidden")
+ folder(".git")
+ file("c.gpg")
+ assertEquals(listOf("A", "b"), subfolders(root, showHidden = false).map { it.name })
+ assertEquals(listOf(".hidden", "A", "b"), subfolders(root, showHidden = true).map { it.name })
+ assertEquals(emptyList<File>(), subfolders(File(root, "missing"), showHidden = false))
+ }
}