Files
goext/cryptext/pronouncablePassword_additional_test.go
T
Mikescher 02d6894ec6
Build Docker and Deploy / Run goext test-suite (push) Successful in 1m34s
[🤖] Add Unit-Tests
2026-04-27 16:31:29 +02:00

181 lines
4.7 KiB
Go

package cryptext
import (
"git.blackforestbytes.com/BlackForestBytes/goext/tst"
mathrand "math/rand"
"strings"
"testing"
"unicode"
)
func TestPronouncablePasswordLength(t *testing.T) {
for _, n := range []int{1, 2, 3, 5, 8, 13, 21, 50, 128} {
pw := PronouncablePassword(n)
tst.AssertEqual(t, len(pw), n)
}
}
func TestPronouncablePasswordZeroOrNegative(t *testing.T) {
tst.AssertEqual(t, PronouncablePassword(0), "")
tst.AssertEqual(t, PronouncablePassword(-1), "")
tst.AssertEqual(t, PronouncablePassword(-1000), "")
}
func TestPronouncablePasswordSeededDeterministic(t *testing.T) {
pw1 := PronouncablePasswordSeeded(42, 16)
pw2 := PronouncablePasswordSeeded(42, 16)
tst.AssertEqual(t, pw1, pw2)
tst.AssertEqual(t, len(pw1), 16)
}
func TestPronouncablePasswordSeededDifferentSeeds(t *testing.T) {
pw1 := PronouncablePasswordSeeded(1, 16)
pw2 := PronouncablePasswordSeeded(2, 16)
tst.AssertNotEqual(t, pw1, pw2)
}
func TestPronouncablePasswordExtEntropy(t *testing.T) {
rng := mathrand.New(mathrand.NewSource(1))
pw, entropy := PronouncablePasswordExt(rng, 32)
tst.AssertEqual(t, len(pw), 32)
if entropy <= 0 {
t.Errorf("expected positive entropy, got %f", entropy)
}
}
func TestPronouncablePasswordExtZeroLen(t *testing.T) {
rng := mathrand.New(mathrand.NewSource(1))
pw, entropy := PronouncablePasswordExt(rng, 0)
tst.AssertEqual(t, pw, "")
tst.AssertEqual(t, entropy, float64(0))
}
func TestPronouncablePasswordCharacters(t *testing.T) {
// Output should be only ASCII letters
for i := range 50 {
pw := PronouncablePasswordSeeded(int64(i), 32)
for _, c := range pw {
if !unicode.IsLetter(c) || c > unicode.MaxASCII {
t.Errorf("non-letter or non-ASCII rune %q in password %q", c, pw)
break
}
}
}
}
func TestPronouncablePasswordStartsUpper(t *testing.T) {
for i := range 50 {
pw := PronouncablePasswordSeeded(int64(i), 16)
if pw == "" {
continue
}
first := rune(pw[0])
if !unicode.IsUpper(first) {
t.Errorf("expected first letter uppercase in %q (seed %d)", pw, i)
}
if !strings.ContainsRune(ppStartChar, first) {
t.Errorf("expected first letter from start-set in %q (seed %d)", pw, i)
}
}
}
func TestPpMakeSet(t *testing.T) {
set := ppMakeSet("ABC")
tst.AssertTrue(t, set['A'])
tst.AssertTrue(t, set['B'])
tst.AssertTrue(t, set['C'])
tst.AssertFalse(t, set['D'])
tst.AssertEqual(t, len(set), 3)
}
func TestPpMakeSetEmpty(t *testing.T) {
set := ppMakeSet("")
tst.AssertEqual(t, len(set), 0)
}
func TestPpCharType(t *testing.T) {
v, c := ppCharType('A')
tst.AssertTrue(t, v)
tst.AssertFalse(t, c)
v, c = ppCharType('B')
tst.AssertFalse(t, v)
tst.AssertTrue(t, c)
v, c = ppCharType('Y')
tst.AssertTrue(t, v)
tst.AssertFalse(t, c)
v, c = ppCharType('1')
tst.AssertFalse(t, v)
tst.AssertFalse(t, c)
}
func TestPpCharsetRemove(t *testing.T) {
set := ppMakeSet("AEIOU")
out := ppCharsetRemove("ABCDEFG", set, false)
tst.AssertEqual(t, out, "BCDFG")
}
func TestPpCharsetRemoveEmptyDisallowed(t *testing.T) {
set := ppMakeSet("AB")
out := ppCharsetRemove("AB", set, false)
// when result would be empty and allowEmpty=false, it returns the original
tst.AssertEqual(t, out, "AB")
}
func TestPpCharsetRemoveEmptyAllowed(t *testing.T) {
set := ppMakeSet("AB")
out := ppCharsetRemove("AB", set, true)
tst.AssertEqual(t, out, "")
}
func TestPpCharsetFilter(t *testing.T) {
set := ppMakeSet("AEIOU")
out := ppCharsetFilter("ABCDEFG", set, false)
tst.AssertEqual(t, out, "AE")
}
func TestPpCharsetFilterEmptyDisallowed(t *testing.T) {
set := ppMakeSet("XYZ")
out := ppCharsetFilter("ABC", set, false)
tst.AssertEqual(t, out, "ABC") // returns original when result empty & not allowed
}
func TestPpCharsetFilterEmptyAllowed(t *testing.T) {
set := ppMakeSet("XYZ")
out := ppCharsetFilter("ABC", set, true)
tst.AssertEqual(t, out, "")
}
func TestPronouncablePasswordContinuationFollowsRules(t *testing.T) {
// Make sure each continuation pair (lowercased) appears in ppContinuation
// Note: when a new segment starts (uppercase letter mid-string), the continuation
// check does not apply across the segment boundary.
for s := range 30 {
seed := int64(s)
pw := PronouncablePasswordSeeded(seed, 32)
if len(pw) < 2 {
continue
}
runes := []byte(strings.ToUpper(pw))
for i := 1; i < len(runes); i++ {
// Detect new segment (original char was uppercase and it's not the first char)
origUpper := pw[i] >= 'A' && pw[i] <= 'Z'
if origUpper && i > 0 {
continue
}
prev := runes[i-1]
cur := runes[i]
cont, ok := ppContinuation[prev]
if !ok {
t.Errorf("no continuation map for %q (pw=%q)", prev, pw)
continue
}
if !strings.ContainsRune(cont, rune(cur)) {
t.Errorf("invalid continuation %q -> %q in %q (seed %d)", prev, cur, pw, seed)
}
}
}
}