1package com.peachpasswords.autofill23import org.junit.Assert.assertEquals4import org.junit.Assert.assertNull5import org.junit.Assert.assertTrue6import org.junit.Test78class PasswordPolicyTest {910 @Test fun reads_minlength_and_maxlength_attributes() {11 val p = PasswordPolicy.fromHtmlAttributes(mapOf("minlength" to "8", "maxlength" to "64"))12 assertEquals(8, p.minLength)13 assertEquals(64, p.maxLength)14 }1516 @Test fun ignores_non_positive_lengths() {17 val p = PasswordPolicy.fromHtmlAttributes(mapOf("minlength" to "0", "maxlength" to "-1"))18 assertNull(p.minLength)19 assertNull(p.maxLength)20 }2122 @Test fun pattern_range_fills_both_bounds() {23 val p = PasswordPolicy.fromHtmlAttributes(mapOf("pattern" to ".{8,20}"))24 assertEquals(8, p.minLength)25 assertEquals(20, p.maxLength)26 }2728 @Test fun pattern_min_only() {29 val p = PasswordPolicy.fromHtmlAttributes(mapOf("pattern" to ".{12,}"))30 assertEquals(12, p.minLength)31 assertNull(p.maxLength)32 }3334 @Test fun pattern_exact_sets_both() {35 val p = PasswordPolicy.fromHtmlAttributes(mapOf("pattern" to "[a-z]{10}"))36 assertEquals(10, p.minLength)37 assertEquals(10, p.maxLength)38 }3940 @Test fun attributes_win_over_pattern() {41 val p = PasswordPolicy.fromHtmlAttributes(42 mapOf("minlength" to "6", "maxlength" to "30", "pattern" to ".{8,20}"),43 )44 assertEquals(6, p.minLength)45 assertEquals(30, p.maxLength)46 }4748 @Test fun no_constraints_yields_empty_policy() {49 val p = PasswordPolicy.fromHtmlAttributes(mapOf("autocomplete" to "new-password"))50 assertNull(p.minLength)51 assertNull(p.maxLength)52 }5354 @Test fun generation_prefers_max_length() {55 56 assertEquals(64, PasswordPolicy(minLength = 8, maxLength = 64).generationLength())57 }5859 @Test fun generation_caps_at_hard_max() {60 assertEquals(256, PasswordPolicy(maxLength = 500).generationLength())61 }6263 @Test fun generation_snaps_up_to_min() {64 65 assertEquals(25, PasswordPolicy(minLength = 25).generationLength())66 }6768 @Test fun generation_default_when_unconstrained() {69 assertEquals(PasswordPolicy.DEFAULT_LENGTH, PasswordPolicy().generationLength())70 }7172 @Test fun generation_never_below_the_floor() {73 74 75 assertEquals(PasswordPolicy.MIN_GENERATION_LENGTH, PasswordPolicy(maxLength = 0).generationLength())76 assertEquals(PasswordPolicy.MIN_GENERATION_LENGTH, PasswordPolicy(maxLength = 1).generationLength())77 assertTrue(PasswordPolicy(maxLength = 1).isWeaklyConstrained)78 }7980 @Test fun constraint_note_max() {81 val p = PasswordPolicy(maxLength = 20)82 assertEquals("This site allows max 20 characters", p.constraintNote(20))83 }8485 @Test fun constraint_note_min() {86 val p = PasswordPolicy(minLength = 12)87 assertEquals("This site requires at least 12 characters", p.constraintNote(12))88 }8990 @Test fun constraint_note_empty_when_interior() {91 val p = PasswordPolicy(minLength = 8, maxLength = 64)92 assertEquals("", p.constraintNote(30))93 }94}