bot_host / tests /test_validators.py
ItsBounvy's picture
deploy: english UI + admin creds + dataset wiring
76946d3 verified
Raw History Blame Contribute Delete
6.11 kB
"""Unit tests for hosting_panel.validators."""
from __future__ import annotations
import os
os.environ.setdefault("PANEL_SECRET_KEY", "x" * 64)
os.environ.setdefault("PANEL_ENCRYPTION_KEY",
"90mbg1KLmFKBpoSs5nstm7Ew-p3Q-J3ya3Zbuhwbtho=")
import pytest
from fastapi import HTTPException
from hosting_panel import validators as v
# ---- slug -----------------------------------------------------------------
def test_slug_accepts_normal():
assert v.validate_slug("my-bot") == "my-bot"
def test_slug_lowercases_and_strips():
assert v.validate_slug(" My-Bot ") == "my-bot"
@pytest.mark.parametrize("bad", [
"", # empty
"a", # too short
"-lead", # leading hyphen
"trail-", # trailing hyphen
"has spaces",
"has/slash",
"has\\slash",
"x" * 200, # too long
None,
])
def test_slug_rejects_invalid(bad):
with pytest.raises(HTTPException):
v.validate_slug(bad)
# ---- bot token (Telegram format: <bot_id>:<secret>) ----------------------
@pytest.mark.parametrize("ok", [
"1234567890:AAH_bot_token_secret_value_long_enough",
])
def test_bot_token_accepts(ok):
assert v.validate_bot_token(ok) == ok
@pytest.mark.parametrize("bad", [
"short", # too short
"x" * 61, # too long
"x" * 34, # one short
"", # empty
"noseparator-here-35+chars", # missing colon
"1234:spaces inside token-padding-padding-padding", # has spaces -> rejected
])
def test_bot_token_rejects(bad):
with pytest.raises(HTTPException):
v.validate_bot_token(bad)
def test_bot_token_with_colon_35_chars_works():
# bot_id:secret, total length 35
tok = "1234:" + "A" * 30 # 5 + 30 = 35
assert v.validate_bot_token(tok) == tok
# ---- hf token (hf_... length 30-60) --------------------------------------
def test_hf_token_30_chars_works():
tok = "hf_" + "x" * 27 # 3 + 27 = 30
assert v.validate_hf_token(tok) == tok
def test_hf_token_60_chars_works():
tok = "hf_" + "x" * 57
assert v.validate_hf_token(tok) == tok
def test_hf_token_rejects_short():
with pytest.raises(HTTPException):
v.validate_hf_token("hf_" + "x" * 5) # 8 chars
def test_hf_token_rejects_long():
with pytest.raises(HTTPException):
v.validate_hf_token("hf_" + "x" * 60)
# ---- proxy token ----------------------------------------------------------
def test_proxy_token_accepts():
assert v.validate_proxy_token("proxy-token-long-enough-1234567890") == "proxy-token-long-enough-1234567890"
# ---- source repo ----------------------------------------------------------
def test_source_repo_hf_format():
assert v.validate_source_repo("owner/name") == "owner/name"
assert v.validate_source_repo("owner-name/repo-name") == "owner-name/repo-name"
def test_source_repo_hf_rejects_no_slash():
with pytest.raises(HTTPException):
v.validate_source_repo("noslash")
def test_source_repo_github_format():
assert v.validate_source_repo(
"https://github.com/owner/repo.git",
source_type="github",
) == "https://github.com/owner/repo.git"
def test_source_repo_github_rejects_bare_name():
with pytest.raises(HTTPException):
v.validate_source_repo("owner/repo", source_type="github")
# ---- source type ----------------------------------------------------------
def test_source_type_accepts_hf_default():
# Empty -> "hf"
assert v.validate_source_type("") == "hf"
assert v.validate_source_type("hf") == "hf"
assert v.validate_source_type("github") == "github"
@pytest.mark.parametrize("bad", ["ftp", "http", "spaces", "BOGUS"])
def test_source_type_rejects(bad):
with pytest.raises(HTTPException):
v.validate_source_type(bad)
# ---- framework ------------------------------------------------------------
def test_framework_default_is_docker():
assert v.validate_framework("") == "docker"
def test_framework_accepts():
assert v.validate_framework("python") == "python"
assert v.validate_framework("node") == "node"
assert v.validate_framework("fastapi/aiogram") == "fastapi/aiogram"
def test_framework_lowercases():
"""Validator lowercases; this is intentional -- reject via special chars only."""
assert v.validate_framework("Python") == "python"
def test_framework_rejects_special_chars():
with pytest.raises(HTTPException):
v.validate_framework("python@3.11")
# ---- branch ---------------------------------------------------------------
def test_branch_default_is_main():
assert v.validate_branch("") == "main"
def test_branch_accepts():
assert v.validate_branch("feature/auth") == "feature/auth"
assert v.validate_branch("main") == "main"
def test_branch_rejects_special_chars():
with pytest.raises(HTTPException):
v.validate_branch("feature; rm -rf /")
# ---- nvidia api key -------------------------------------------------------
def test_nvidia_key_accepts_60_chars():
key = "nvapi-" + "x" * 54 # 60 chars
assert v.validate_nvidia_api_key(key) == key
def test_nvidia_key_rejects_short():
with pytest.raises(HTTPException):
v.validate_nvidia_api_key("nvapi-x")
# ---- url ------------------------------------------------------------------
def test_url_optional_empty():
assert v.validate_optional_url("", "render_url") == ""
def test_url_accepts_https():
assert v.validate_optional_url("https://example.com", "url") == "https://example.com"
def test_url_rejects_no_scheme():
with pytest.raises(HTTPException):
v.validate_optional_url("example.com", "url")
# ---- model ----------------------------------------------------------------
def test_model_default():
assert v.validate_optional_nvidia_model("") == "minimaxai/minimax-m3"
def test_model_accepts_custom():
assert v.validate_optional_nvidia_model("openai/gpt-4") == "openai/gpt-4"
def test_model_rejects_special_chars():
with pytest.raises(HTTPException):
v.validate_optional_nvidia_model("model@name")