QA-001
- [x] Dateinamen-Tests vorhanden - [x] Fortsetzen-Tests vorhanden - [x] Blockstatus-Tests vorhanden - [x] Repository-Tests vorhanden
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package de.medapp.wunddoku.domain.usecase
|
||||
|
||||
import de.medapp.wunddoku.domain.model.CheckItem
|
||||
import de.medapp.wunddoku.domain.model.DocumentationBlock
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Blockstatus-Tests: Wann gilt ein Dokumentationsblock als "bearbeitet"?
|
||||
* Diese Regel steuert Fortschrittsanzeige UND PDF-Export und muss deshalb exakt stimmen.
|
||||
*/
|
||||
class BlockEditedPolicyTest {
|
||||
|
||||
private var idCounter = 0
|
||||
|
||||
private fun item(
|
||||
checked: Boolean,
|
||||
isFreeText: Boolean = false,
|
||||
freeTextValue: String = "",
|
||||
isStaticHint: Boolean = false,
|
||||
inputSpec: String = "",
|
||||
): CheckItem {
|
||||
idCounter++
|
||||
return CheckItem(
|
||||
checkItemStateId = "state-$idCounter",
|
||||
definitionId = "def-$idCounter",
|
||||
label = "Eintrag $idCounter",
|
||||
order = idCounter,
|
||||
checked = checked,
|
||||
isFreeText = isFreeText,
|
||||
freeTextValue = freeTextValue,
|
||||
isStaticHint = isStaticHint,
|
||||
inputSpec = inputSpec,
|
||||
)
|
||||
}
|
||||
|
||||
private fun block(vararg items: CheckItem) = DocumentationBlock(
|
||||
blockStateId = "block-state",
|
||||
definitionId = "block-def",
|
||||
title = "Testblock",
|
||||
order = 1,
|
||||
items = items.toList(),
|
||||
)
|
||||
|
||||
// --- nicht bearbeitet ----------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun block_ohne_eintraege_ist_nicht_bearbeitet() {
|
||||
assertFalse(BlockEditedPolicy.isEdited(block()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun block_ohne_angehakten_eintrag_ist_nicht_bearbeitet() {
|
||||
val b = block(item(checked = false), item(checked = false), item(checked = false, isFreeText = true))
|
||||
assertFalse(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nicht_angehakter_freitext_mit_text_zaehlt_nicht_als_bearbeitung() {
|
||||
// Das Freitextfeld ist in der UI gesperrt, solange die Checkbox nicht angehakt ist.
|
||||
val b = block(item(checked = false, isFreeText = true, freeTextValue = "Text ohne Haken"))
|
||||
assertFalse(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nur_hinweistexte_machen_einen_block_nicht_bearbeitet() {
|
||||
// Hinweistexte sind technisch immer checked = true, aber keine Nutzereingabe.
|
||||
val b = block(
|
||||
item(checked = true, isStaticHint = true),
|
||||
item(checked = true, isStaticHint = true),
|
||||
)
|
||||
assertFalse(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hinweistexte_plus_nicht_angehakte_eintraege_sind_nicht_bearbeitet() {
|
||||
val b = block(
|
||||
item(checked = true, isStaticHint = true),
|
||||
item(checked = false),
|
||||
item(checked = false, isFreeText = true),
|
||||
)
|
||||
assertFalse(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
// --- bearbeitet ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun ein_angehakter_normaler_eintrag_macht_den_block_bearbeitet() {
|
||||
assertTrue(BlockEditedPolicy.isEdited(block(item(checked = true))))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun angehakter_freitext_zaehlt_auch_ohne_eingegebenen_text() {
|
||||
val b = block(item(checked = true, isFreeText = true, freeTextValue = ""))
|
||||
assertTrue(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun angehakter_freitext_mit_text_ist_bearbeitet() {
|
||||
val b = block(item(checked = true, isFreeText = true, freeTextValue = "Rötung am Wundrand"))
|
||||
assertTrue(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun angehakter_eintrag_mit_strukturierter_eingabe_ist_bearbeitet() {
|
||||
val b = block(item(checked = true, freeTextValue = "3", inputSpec = "spec"))
|
||||
assertTrue(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hinweistext_wird_ignoriert_sobald_ein_echter_eintrag_angehakt_ist() {
|
||||
val b = block(
|
||||
item(checked = true, isStaticHint = true),
|
||||
item(checked = true),
|
||||
)
|
||||
assertTrue(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mehrere_angehakte_eintraege_sind_bearbeitet() {
|
||||
val b = block(item(checked = true), item(checked = true), item(checked = false))
|
||||
assertTrue(BlockEditedPolicy.isEdited(b))
|
||||
}
|
||||
|
||||
// --- Zusammenspiel mit DocumentationBlock.isEdited ------------------------------------
|
||||
|
||||
@Test
|
||||
fun documentation_block_isEdited_delegiert_an_die_policy() {
|
||||
val edited = block(item(checked = true))
|
||||
val untouched = block(item(checked = false), item(checked = true, isStaticHint = true))
|
||||
|
||||
assertEquals(BlockEditedPolicy.isEdited(edited), edited.isEdited)
|
||||
assertEquals(BlockEditedPolicy.isEdited(untouched), untouched.isEdited)
|
||||
assertTrue(edited.isEdited)
|
||||
assertFalse(untouched.isEdited)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isEdited_wird_aus_dem_aktuellen_inhalt_abgeleitet_und_nicht_gespeichert() {
|
||||
val untouched = block(item(checked = false))
|
||||
assertFalse(untouched.isEdited)
|
||||
|
||||
val nowChecked = untouched.copy(items = untouched.items.map { it.copy(checked = true) })
|
||||
assertTrue(nowChecked.isEdited)
|
||||
|
||||
val uncheckedAgain = nowChecked.copy(items = nowChecked.items.map { it.copy(checked = false) })
|
||||
assertFalse(uncheckedAgain.isEdited)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
package de.medapp.wunddoku.pdf
|
||||
|
||||
import de.medapp.wunddoku.domain.model.CheckItem
|
||||
import de.medapp.wunddoku.domain.model.Documentation
|
||||
import de.medapp.wunddoku.domain.model.DocumentationBlock
|
||||
import de.medapp.wunddoku.domain.model.DocumentationStatus
|
||||
import de.medapp.wunddoku.domain.model.PatientStammdatenSnapshot
|
||||
import de.medapp.wunddoku.domain.model.Photo
|
||||
import de.medapp.wunddoku.domain.model.Signature
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
|
||||
/**
|
||||
* Blockstatus im PDF-Export: Nur bearbeitete Blöcke und nur angehakte Optionen gelangen ins PDF,
|
||||
* alles wird aus den historischen Snapshots gebildet und stabil sortiert.
|
||||
*/
|
||||
class DocumentationToPdfExportModelMapperTest {
|
||||
|
||||
private val snapshot = PatientStammdatenSnapshot(
|
||||
firstName = "Max",
|
||||
lastName = "Mustermann",
|
||||
birthDate = LocalDate.of(1980, 3, 14),
|
||||
street = "Musterstraße 1",
|
||||
postalCode = "12345",
|
||||
city = "Musterstadt",
|
||||
)
|
||||
|
||||
// Mittag in der Systemzeitzone -> das lokale Datum ist zeitzonenunabhängig der 23.08.2026.
|
||||
private val createdAt: Instant =
|
||||
LocalDate.of(2026, 8, 23).atTime(12, 0).atZone(ZoneId.systemDefault()).toInstant()
|
||||
|
||||
private fun item(
|
||||
id: String,
|
||||
order: Int,
|
||||
checked: Boolean,
|
||||
label: String = "Label $id",
|
||||
isFreeText: Boolean = false,
|
||||
freeTextValue: String = "",
|
||||
isStaticHint: Boolean = false,
|
||||
inputSpec: String = "",
|
||||
) = CheckItem(
|
||||
checkItemStateId = "state-$id",
|
||||
definitionId = id,
|
||||
label = label,
|
||||
order = order,
|
||||
checked = checked,
|
||||
isFreeText = isFreeText,
|
||||
freeTextValue = freeTextValue,
|
||||
isStaticHint = isStaticHint,
|
||||
inputSpec = inputSpec,
|
||||
)
|
||||
|
||||
private fun block(id: String, order: Int, title: String = "Titel $id", items: List<CheckItem>) =
|
||||
DocumentationBlock("state-$id", id, title, order, items)
|
||||
|
||||
private fun documentation(
|
||||
blocks: List<DocumentationBlock>,
|
||||
photos: List<Photo> = emptyList(),
|
||||
signatures: List<Signature> = emptyList(),
|
||||
) = Documentation(
|
||||
id = "doc-1",
|
||||
patientId = "patient-1",
|
||||
patientSnapshot = snapshot,
|
||||
status = DocumentationStatus.IN_PROGRESS,
|
||||
createdAt = createdAt,
|
||||
updatedAt = createdAt,
|
||||
continuedFromDocumentationId = null,
|
||||
catalogVersion = 4,
|
||||
blocks = blocks,
|
||||
photos = photos,
|
||||
signatures = signatures,
|
||||
)
|
||||
|
||||
// --- Blockfilter ---------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun nur_bearbeitete_bloecke_werden_exportiert() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("a", 1, items = listOf(item("a1", 1, checked = true))),
|
||||
block("b", 2, items = listOf(item("b1", 1, checked = false))),
|
||||
block("c", 3, items = listOf(item("c1", 1, checked = true))),
|
||||
)
|
||||
)
|
||||
|
||||
val model = DocumentationToPdfExportModelMapper.map(doc)
|
||||
|
||||
assertEquals(listOf("Titel a", "Titel c"), model.blocks.map { it.title })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun block_mit_nur_hinweistexten_wird_nicht_exportiert() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block(
|
||||
"hint", 1,
|
||||
items = listOf(
|
||||
item("h1", 1, checked = true, isStaticHint = true),
|
||||
item("h2", 2, checked = false),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assertTrue(DocumentationToPdfExportModelMapper.map(doc).blocks.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dokumentation_ohne_bearbeiteten_block_ergibt_leere_blockliste() {
|
||||
val doc = documentation(listOf(block("a", 1, items = listOf(item("a1", 1, checked = false)))))
|
||||
assertTrue(DocumentationToPdfExportModelMapper.map(doc).blocks.isEmpty())
|
||||
}
|
||||
|
||||
// --- Itemfilter ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun nur_angehakte_optionen_erscheinen_im_pdf() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block(
|
||||
"a", 1,
|
||||
items = listOf(
|
||||
item("a1", 1, checked = true, label = "Ja"),
|
||||
item("a2", 2, checked = false, label = "Nein"),
|
||||
item("a3", 3, checked = true, label = "Auch ja"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val items = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items
|
||||
|
||||
assertEquals(listOf("Ja", "Auch ja"), items.map { it.label })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hinweistexte_eines_bearbeiteten_blocks_bleiben_im_pdf_erhalten() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block(
|
||||
"a", 1,
|
||||
items = listOf(
|
||||
item("hint", 1, checked = true, label = "Reinigen mit:", isStaticHint = true),
|
||||
item("a2", 2, checked = true, label = "Ringer-Lösung"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val items = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items
|
||||
|
||||
assertEquals(listOf("Reinigen mit:", "Ringer-Lösung"), items.map { it.label })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun freitext_und_strukturierte_eingabe_werden_unveraendert_durchgereicht() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block(
|
||||
"a", 1,
|
||||
items = listOf(
|
||||
item("f", 1, checked = true, label = "Freitext", isFreeText = true, freeTextValue = "Rötung"),
|
||||
item("s", 2, checked = true, label = "Maße", freeTextValue = "3|4", inputSpec = "SPEC"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val items = DocumentationToPdfExportModelMapper.map(doc).blocks.single().items
|
||||
|
||||
assertEquals(PdfExportItem("Freitext", isFreeText = true, freeTextValue = "Rötung", inputSpec = ""), items[0])
|
||||
assertEquals(PdfExportItem("Maße", isFreeText = false, freeTextValue = "3|4", inputSpec = "SPEC"), items[1])
|
||||
}
|
||||
|
||||
// --- Sortierung ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun bloecke_und_optionen_werden_nach_order_sortiert() {
|
||||
val doc = documentation(
|
||||
listOf(
|
||||
block("zweiter", 2, items = listOf(item("z1", 1, checked = true))),
|
||||
block(
|
||||
"erster", 1,
|
||||
items = listOf(
|
||||
item("e2", 2, checked = true, label = "B"),
|
||||
item("e1", 1, checked = true, label = "A"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
val blocks = DocumentationToPdfExportModelMapper.map(doc).blocks
|
||||
|
||||
assertEquals(listOf("Titel erster", "Titel zweiter"), blocks.map { it.title })
|
||||
assertEquals(listOf("A", "B"), blocks.first().items.map { it.label })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fotos_und_signaturen_werden_nach_orderIndex_sortiert() {
|
||||
val photos = listOf(
|
||||
Photo("p2", "doc-1", "zweites.jpg", 1, createdAt),
|
||||
Photo("p1", "doc-1", "erstes.jpg", 0, createdAt),
|
||||
)
|
||||
val signatures = listOf(
|
||||
Signature("s2", "doc-1", "Berta", "b.png", 1, createdAt),
|
||||
Signature("s1", "doc-1", "Anna", "a.png", 0, createdAt),
|
||||
)
|
||||
|
||||
val model = DocumentationToPdfExportModelMapper.map(documentation(emptyList(), photos, signatures))
|
||||
|
||||
assertEquals(listOf("erstes.jpg", "zweites.jpg"), model.photos.map { it.fileName })
|
||||
assertEquals(listOf("Anna", "Berta"), model.signatures.map { it.signerName })
|
||||
assertEquals(listOf("a.png", "b.png"), model.signatures.map { it.fileName })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun alle_signaturen_werden_exportiert_ohne_obergrenze() {
|
||||
val signatures = (0 until 5).map { Signature("s$it", "doc-1", "Person $it", "s$it.png", it, createdAt) }
|
||||
val model = DocumentationToPdfExportModelMapper.map(documentation(emptyList(), signatures = signatures))
|
||||
assertEquals(5, model.signatures.size)
|
||||
}
|
||||
|
||||
// --- Kopfdaten (Snapshot) ------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun kopfdaten_stammen_aus_dem_patienten_snapshot_der_dokumentation() {
|
||||
val model = DocumentationToPdfExportModelMapper.map(documentation(emptyList()))
|
||||
|
||||
assertEquals("doc-1", model.documentationId)
|
||||
assertEquals("Max Mustermann", model.patientFullName)
|
||||
assertEquals(LocalDate.of(1980, 3, 14), model.patientBirthDate)
|
||||
assertEquals("Musterstraße 1, 12345 Musterstadt", model.patientDisplayAddress)
|
||||
assertEquals(LocalDate.of(2026, 8, 23), model.createdAt)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dateiname_folgt_dem_schema_und_nutzt_snapshot_und_erstelldatum() {
|
||||
val model = DocumentationToPdfExportModelMapper.map(documentation(emptyList()))
|
||||
assertEquals("2026-08-23_Max_Mustermann_1980-03-14.pdf", model.fileName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package de.medapp.wunddoku.pdf
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import java.time.LocalDate
|
||||
|
||||
/**
|
||||
* Reine JVM-Tests (keine Android-Abhängigkeit) für das Dateinamensschema
|
||||
* `Erstelldatum_Vorname_Nachname_Geburtsdatum.pdf`.
|
||||
*/
|
||||
class PdfFileNameGeneratorTest {
|
||||
|
||||
private val created = LocalDate.of(2026, 8, 23)
|
||||
private val birth = LocalDate.of(1980, 3, 14)
|
||||
|
||||
private fun name(
|
||||
first: String,
|
||||
last: String,
|
||||
createdAt: LocalDate = created,
|
||||
birthDate: LocalDate = birth,
|
||||
): String = PdfFileNameGenerator.generate(createdAt, first, last, birthDate)
|
||||
|
||||
// --- Grundschema ---------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun standardfall_folgt_dem_vereinbarten_schema() {
|
||||
assertEquals("2026-08-23_Max_Mustermann_1980-03-14.pdf", name("Max", "Mustermann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun datumsteile_werden_mit_fuehrenden_nullen_im_iso_format_ausgegeben() {
|
||||
val result = name("Max", "Mustermann", createdAt = LocalDate.of(2026, 1, 5), birthDate = LocalDate.of(1999, 2, 3))
|
||||
assertEquals("2026-01-05_Max_Mustermann_1999-02-03.pdf", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun schaltjahr_geburtsdatum_wird_korrekt_ausgegeben() {
|
||||
val result = name("Max", "Mustermann", birthDate = LocalDate.of(2000, 2, 29))
|
||||
assertEquals("2026-08-23_Max_Mustermann_2000-02-29.pdf", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ergebnis_ist_deterministisch() {
|
||||
assertEquals(name("Max", "Mustermann"), name("Max", "Mustermann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verschiedene_erstelldaten_fuehren_zu_verschiedenen_dateinamen() {
|
||||
val a = name("Max", "Mustermann", createdAt = LocalDate.of(2026, 8, 23))
|
||||
val b = name("Max", "Mustermann", createdAt = LocalDate.of(2026, 8, 24))
|
||||
assertNotEquals(a, b)
|
||||
}
|
||||
|
||||
// --- Umlaute / Diakritika ------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun deutsche_umlaute_werden_transliteriert_statt_entfernt() {
|
||||
assertEquals("2026-08-23_Max_Mueller_1980-03-14.pdf", name("Max", "Müller"))
|
||||
assertEquals("2026-08-23_Aenne_Oezdemir_1980-03-14.pdf", name("Änne", "Özdemir"))
|
||||
assertEquals("2026-08-23_Uwe_Ueberall_1980-03-14.pdf", name("Uwe", "Überall"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun eszett_wird_zu_doppel_s() {
|
||||
assertEquals("2026-08-23_Max_Weiss_1980-03-14.pdf", name("Max", "Weiß"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mueller_und_mller_bleiben_unterscheidbar() {
|
||||
assertNotEquals(name("Max", "Müller"), name("Max", "Mller"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun andere_diakritika_werden_entfernt() {
|
||||
assertEquals("2026-08-23_Jose_Celik_1980-03-14.pdf", name("José", "Çelik"))
|
||||
}
|
||||
|
||||
// --- Leerzeichen / Sonderzeichen -----------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun leerzeichen_werden_zu_unterstrichen() {
|
||||
assertEquals("2026-08-23_Anna_Maria_Mustermann_1980-03-14.pdf", name("Anna Maria", "Mustermann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mehrfaches_whitespace_wird_zu_einem_unterstrich_zusammengefasst() {
|
||||
assertEquals("2026-08-23_Anna_Maria_Mustermann_1980-03-14.pdf", name("Anna Maria", "Mustermann"))
|
||||
assertEquals("2026-08-23_Anna_Maria_Mustermann_1980-03-14.pdf", name("Anna\t\nMaria", "Mustermann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mehrfache_unterstriche_werden_zusammengefasst() {
|
||||
assertEquals("2026-08-23_Max_Muster_mann_1980-03-14.pdf", name("Max", "Muster__mann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuehrende_und_nachgestellte_trennzeichen_werden_entfernt() {
|
||||
val expected = "2026-08-23_Max_Mustermann_1980-03-14.pdf"
|
||||
assertEquals(expected, name(" Max ", "Mustermann"))
|
||||
assertEquals(expected, name("_Max_", "Mustermann"))
|
||||
assertEquals(expected, name("-Max-", "Mustermann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindestrich_im_doppelnamen_bleibt_erhalten() {
|
||||
assertEquals("2026-08-23_Aenne-Marie_Meyer-Luedenscheidt_1980-03-14.pdf", name("Änne-Marie", "Meyer-Lüdenscheidt"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun apostroph_und_punkt_werden_entfernt() {
|
||||
assertEquals("2026-08-23_Sean_OBrien_1980-03-14.pdf", name("Sean", "O'Brien"))
|
||||
assertEquals("2026-08-23_Max_Dr_Mustermann_1980-03-14.pdf", name("Max", "Dr. Mustermann"))
|
||||
}
|
||||
|
||||
// --- Sicherheit (Pfad-/Dateisystemzeichen) --------------------------------------------
|
||||
|
||||
@Test
|
||||
fun pfadseparatoren_und_punkte_koennen_keinen_pfad_erzeugen() {
|
||||
assertEquals(
|
||||
"2026-08-23_Unbekannt_etcpasswd_1980-03-14.pdf",
|
||||
name("../..", "../etc/passwd"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fuer_dateinamen_ungueltige_zeichen_werden_entfernt() {
|
||||
assertEquals(
|
||||
"2026-08-23_MaxMuster_Mustermann_1980-03-14.pdf",
|
||||
name("Max:Mus*ter?", "Mustermann"),
|
||||
)
|
||||
assertEquals(
|
||||
"2026-08-23_Max_Mustermann_1980-03-14.pdf",
|
||||
name("Ma<x>|\"", "Muster\\mann"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emojis_werden_entfernt() {
|
||||
assertEquals("2026-08-23_Max_Mustermann_1980-03-14.pdf", name("Max😀", "Mustermann"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dateiname_besteht_immer_nur_aus_sicheren_zeichen_und_endet_auf_pdf() {
|
||||
val inputs = listOf(
|
||||
"Max" to "Mustermann",
|
||||
"Änne-Marie" to "Özdemir Weiß",
|
||||
"José" to "O'Brien",
|
||||
"../../x" to "a\\b/c",
|
||||
" " to "",
|
||||
"😀" to "???",
|
||||
"Ma<x>|\"" to "Muster\\mann",
|
||||
)
|
||||
val allowed = Regex("^[A-Za-z0-9_.-]+$")
|
||||
for ((first, last) in inputs) {
|
||||
val result = name(first, last)
|
||||
assertTrue("Unsicherer Dateiname: $result", allowed.matches(result))
|
||||
assertTrue("Falsche Endung: $result", result.endsWith(".pdf"))
|
||||
}
|
||||
}
|
||||
|
||||
// --- Platzhalter für leere Namen -----------------------------------------------------
|
||||
|
||||
@Test
|
||||
fun leerer_oder_blanker_name_wird_zu_unbekannt() {
|
||||
assertEquals("2026-08-23_Unbekannt_Mustermann_1980-03-14.pdf", name("", "Mustermann"))
|
||||
assertEquals("2026-08-23_Max_Unbekannt_1980-03-14.pdf", name("Max", " "))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun name_nur_aus_ungueltigen_zeichen_wird_zu_unbekannt() {
|
||||
assertEquals("2026-08-23_Unbekannt_Unbekannt_1980-03-14.pdf", name("???", "***"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user