File size: 4,593 Bytes
f9609df | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | """
Unit tests for auth.py — pure/stdlib-only, no camera or webview needed.
Every test gets its own tmp_path as data_dir, so nothing here touches the
real users.json on disk.
"""
import os
import pytest
import auth
# --- password hashing -------------------------------------------------------
def test_hash_password_generates_random_salt():
salt1, hash1 = auth._hash_password("hunter2")
salt2, hash2 = auth._hash_password("hunter2")
assert salt1 != salt2
assert hash1 != hash2 # different salt -> different hash for the same password
def test_hash_password_reproducible_with_given_salt():
salt, hash1 = auth._hash_password("hunter2")
_, hash2 = auth._hash_password("hunter2", salt=salt)
assert hash1 == hash2
def test_verify_password_accepts_correct_password():
salt, hash_hex = auth._hash_password("correct horse battery staple")
assert auth.verify_password("correct horse battery staple", salt, hash_hex) is True
def test_verify_password_rejects_wrong_password():
salt, hash_hex = auth._hash_password("correct horse battery staple")
assert auth.verify_password("wrong password", salt, hash_hex) is False
# --- create_user validation --------------------------------------------------
def test_create_user_rejects_empty_name(tmp_path):
with pytest.raises(auth.AuthError):
auth.create_user(str(tmp_path), "", "a@b.com", "password123")
def test_create_user_rejects_invalid_email(tmp_path):
with pytest.raises(auth.AuthError):
auth.create_user(str(tmp_path), "Aman", "not-an-email", "password123")
def test_create_user_rejects_short_password(tmp_path):
with pytest.raises(auth.AuthError):
auth.create_user(str(tmp_path), "Aman", "a@b.com", "short")
def test_create_user_rejects_duplicate_email_case_insensitive(tmp_path):
auth.create_user(str(tmp_path), "Aman", "Aman@Example.com", "password123")
with pytest.raises(auth.AuthError):
auth.create_user(str(tmp_path), "Someone Else", "aman@example.com", "different123")
def test_create_user_returns_public_profile_without_secrets(tmp_path):
profile = auth.create_user(str(tmp_path), "Aman", "aman@example.com", "password123")
assert profile["name"] == "Aman"
assert profile["email"] == "aman@example.com"
assert "salt" not in profile
assert "hash" not in profile
assert "id" in profile
assert profile["avatar"] == "A"
def test_create_user_persists_across_loads(tmp_path):
auth.create_user(str(tmp_path), "Aman", "aman@example.com", "password123")
users = auth.load_users(str(tmp_path))
assert len(users) == 1
record = next(iter(users.values()))
assert record["email"] == "aman@example.com"
assert "hash" in record # stored on disk, just never returned to callers
# --- login --------------------------------------------------------------
def test_verify_login_succeeds_with_correct_password(tmp_path):
created = auth.create_user(str(tmp_path), "Aman", "aman@example.com", "password123")
profile = auth.verify_login(str(tmp_path), created["id"], "password123")
assert profile["id"] == created["id"]
def test_verify_login_fails_with_wrong_password(tmp_path):
created = auth.create_user(str(tmp_path), "Aman", "aman@example.com", "password123")
with pytest.raises(auth.AuthError):
auth.verify_login(str(tmp_path), created["id"], "wrong-password")
def test_verify_login_fails_for_unknown_user_id(tmp_path):
with pytest.raises(auth.AuthError):
auth.verify_login(str(tmp_path), "does-not-exist", "whatever")
def test_verify_login_fails_for_empty_password(tmp_path):
created = auth.create_user(str(tmp_path), "Aman", "aman@example.com", "password123")
with pytest.raises(auth.AuthError):
auth.verify_login(str(tmp_path), created["id"], "")
# --- list_profiles / user_dir ------------------------------------------------
def test_list_profiles_sorted_by_name_and_excludes_secrets(tmp_path):
auth.create_user(str(tmp_path), "Zara", "zara@example.com", "password123")
auth.create_user(str(tmp_path), "Aman", "aman@example.com", "password123")
profiles = auth.list_profiles(str(tmp_path))
assert [p["name"] for p in profiles] == ["Aman", "Zara"]
assert all("salt" not in p and "hash" not in p for p in profiles)
def test_list_profiles_empty_when_no_users_file(tmp_path):
assert auth.list_profiles(str(tmp_path)) == []
def test_user_dir_is_nested_under_users(tmp_path):
path = auth.user_dir(str(tmp_path), "abc123")
assert path == os.path.join(str(tmp_path), "users", "abc123")
|