Public Access
DEVX-124: feat: extract shared utilities from infra and grm into devx
Post-merge / detect-type (push) Successful in 13s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / configure-repo (push) Successful in 28s
Post-merge / vikunja (push) Successful in 44s
Post-merge / sync-wiki (push) Successful in 58s
Post-merge / release (push) Successful in 1m7s
Post-merge / publish (push) Successful in 44s
Post-merge / badges (push) Successful in 1m6s
Post-merge / detect-type (push) Successful in 13s
Post-merge / validate-commit-msg (push) Successful in 10s
Post-merge / configure-repo (push) Successful in 28s
Post-merge / vikunja (push) Successful in 44s
Post-merge / sync-wiki (push) Successful in 58s
Post-merge / release (push) Successful in 1m7s
Post-merge / publish (push) Successful in 44s
Post-merge / badges (push) Successful in 1m6s
This commit was merged in pull request #188.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""Unit tests for devx.utils.crypto."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from devx.utils.crypto import (
|
||||
_DIGITS,
|
||||
_LOWER,
|
||||
_SYMBOLS,
|
||||
_UPPER,
|
||||
generate_hex_secret,
|
||||
generate_password,
|
||||
generate_secret,
|
||||
)
|
||||
|
||||
|
||||
class TestGenerateSecret:
|
||||
def test_returns_url_safe_string(self) -> None:
|
||||
secret = generate_secret()
|
||||
assert isinstance(secret, str)
|
||||
assert len(secret) > 0
|
||||
# URL-safe base64 characters only
|
||||
assert re.match(r"^[A-Za-z0-9_-]+$", secret)
|
||||
|
||||
def test_never_starts_with_dash(self) -> None:
|
||||
for _ in range(1000):
|
||||
secret = generate_secret()
|
||||
assert not secret.startswith("-")
|
||||
|
||||
|
||||
class TestGeneratePassword:
|
||||
def test_default_length(self) -> None:
|
||||
pw = generate_password()
|
||||
assert len(pw) == 32
|
||||
|
||||
def test_custom_length(self) -> None:
|
||||
pw = generate_password(length=64)
|
||||
assert len(pw) == 64
|
||||
|
||||
def test_contains_all_char_classes(self) -> None:
|
||||
pw = generate_password(length=32)
|
||||
assert any(c in _UPPER for c in pw), "Missing uppercase"
|
||||
assert any(c in _LOWER for c in pw), "Missing lowercase"
|
||||
assert any(c in _DIGITS for c in pw), "Missing digits"
|
||||
assert any(c in _SYMBOLS for c in pw), "Missing symbols"
|
||||
|
||||
def test_first_char_alphanumeric(self) -> None:
|
||||
for _ in range(1000):
|
||||
pw = generate_password()
|
||||
assert pw[0] not in _SYMBOLS, f"First char '{pw[0]}' is a symbol"
|
||||
|
||||
def test_minimum_length_4(self) -> None:
|
||||
pw = generate_password(length=4)
|
||||
assert len(pw) == 4
|
||||
|
||||
|
||||
class TestGenerateHexSecret:
|
||||
def test_returns_hex_string(self) -> None:
|
||||
secret = generate_hex_secret(length=32)
|
||||
assert re.match(r"^[0-9a-f]+$", secret)
|
||||
|
||||
def test_correct_length(self) -> None:
|
||||
secret = generate_hex_secret(length=20)
|
||||
assert len(secret) == 20
|
||||
|
||||
def test_empty_for_zero(self) -> None:
|
||||
secret = generate_hex_secret(length=0)
|
||||
assert secret == ""
|
||||
Reference in New Issue
Block a user