from devplacepy.utils import hash_password, verify_password, generate_uid, slugify, time_ago, level_for_xp, badge_info from datetime import datetime, timedelta, timezone def test_hash_password_returns_string(): result = hash_password("test123") assert isinstance(result, str) assert len(result) > 20 def test_verify_password_correct(): hashed = hash_password("correct-password") assert verify_password("correct-password", hashed) is True def test_verify_password_wrong(): hashed = hash_password("correct-password") assert verify_password("wrong-password", hashed) is False def test_generate_uid_is_unique(): uid1 = generate_uid() uid2 = generate_uid() assert uid1 != uid2 def test_generate_uid_format(): uid = generate_uid() assert isinstance(uid, str) assert len(uid) == 36 # UUID v4 format def test_slugify_basic(): assert slugify("Hello World") == "hello-world" def test_slugify_special_chars(): assert slugify("Hello! World???") == "hello-world" def test_slugify_multiple_dashes(): assert slugify("hello---world") == "hello-world" def test_slugify_leading_trailing(): assert slugify("--hello--") == "hello" def test_time_ago_just_now(): now = datetime.now(timezone.utc).isoformat() result = time_ago(now) assert result == "just now" def test_time_ago_minutes(): dt = (datetime.now(timezone.utc) - timedelta(minutes=5)).isoformat() result = time_ago(dt) assert "m ago" in result def test_time_ago_hours(): dt = (datetime.now(timezone.utc) - timedelta(hours=3)).isoformat() result = time_ago(dt) assert "h ago" in result def test_time_ago_days(): dt = (datetime.now(timezone.utc) - timedelta(days=5)).isoformat() result = time_ago(dt) assert "d ago" in result def test_time_ago_months(): dt = (datetime.now(timezone.utc) - timedelta(days=60)).isoformat() result = time_ago(dt) assert "/" in result def test_time_ago_years(): dt = (datetime.now(timezone.utc) - timedelta(days=400)).isoformat() result = time_ago(dt) assert "/" in result def test_level_for_xp_boundaries(): assert level_for_xp(0) == 1 assert level_for_xp(99) == 1 assert level_for_xp(100) == 2 assert level_for_xp(250) == 3 assert level_for_xp(450) == 5 def test_level_for_xp_negative(): assert level_for_xp(-50) == 1 def test_badge_info_known(): meta = badge_info("Star Author") assert meta["icon"] assert meta["description"] == "Earned 100 stars" def test_badge_info_unknown_fallback(): meta = badge_info("Nonexistent Badge") assert meta["icon"] assert meta["description"] == "Nonexistent Badge" from devplacepy.utils import ( safe_next, strip_html, extract_mentions, award_badge, award_xp, check_milestone_badges, create_mention_notifications, ) from devplacepy.database import get_table def _seed_user(role="Member", xp=0, level=1): uid = generate_uid() username = f"ut_{uid[:8]}" get_table("users").insert({ "uid": uid, "username": username, "email": f"{username}@t.dev", "role": role, "xp": xp, "level": level, "created_at": datetime.now(timezone.utc).isoformat(), }) return uid, username def test_safe_next_allows_internal_rejects_external(): assert safe_next("/gists") == "/gists" assert safe_next("//evil.com") == "/feed" assert safe_next("http://evil.com") == "/feed" assert safe_next(None) == "/feed" def test_strip_html_removes_tags_and_unescapes(): assert strip_html("Hi & bye") == "Hi & bye" assert strip_html("") == "" def test_extract_mentions_finds_usernames(): mentions = extract_mentions("hi @alice and (@bob_1), mail a@b.com") assert mentions == ["alice", "bob_1"] def test_award_badge_is_idempotent(local_db): uid, _ = _seed_user() assert award_badge(uid, "First Post") is True assert award_badge(uid, "First Post") is False def test_award_xp_levels_up_and_notifies(local_db): uid, _ = _seed_user(xp=95, level=1) result = award_xp(uid, 10) assert result["xp"] == 105 assert result["level"] == 2 assert result["leveled_up"] is True assert get_table("notifications").count(user_uid=uid, type="level") == 1 def test_check_milestone_badges_awards_prolific(local_db): uid, _ = _seed_user() posts = get_table("posts") for index in range(10): post_uid = generate_uid() posts.insert({"uid": post_uid, "user_uid": uid, "slug": f"{post_uid[:8]}-p", "title": None, "content": f"milestone post {index}", "topic": "random", "project_uid": None, "image": None, "stars": 0, "created_at": datetime.now(timezone.utc).isoformat()}) awarded = check_milestone_badges(uid) assert "Prolific" in awarded assert get_table("badges").find_one(user_uid=uid, badge_name="Prolific") is not None def test_create_mention_notifications_targets_known_users(local_db): actor_uid, actor_name = _seed_user() target_uid, target_name = _seed_user() create_mention_notifications( f"hey @{target_name} and @{actor_name} and @ghost_zzz", actor_uid, "/posts/x", ) assert get_table("notifications").count(user_uid=target_uid, type="mention") == 1 assert get_table("notifications").count(user_uid=actor_uid, type="mention") == 0