Files
foxhunt/tests/runpod/test_config.py

199 lines
7.2 KiB
Python

"""
Tests for runpod.config module.
Tests configuration validation, environment variable loading, and error handling.
"""
import pytest
from pathlib import Path
from runpod.config import RunPodConfig
class TestRunPodConfig:
"""Tests for RunPodConfig class."""
def test_config_creation_with_valid_values(self):
"""Test creating config with valid values."""
config = RunPodConfig(
api_key="test_key_" + "x" * 32,
volume_id="test_volume_123"
)
assert config.api_key == "test_key_" + "x" * 32
assert config.volume_id == "test_volume_123"
assert config.datacenters == ["EUR-IS-1"]
assert config.default_image == "jgrusewski/foxhunt:latest"
assert config.default_container_disk_gb == 50
def test_config_creation_with_optional_values(self):
"""Test creating config with optional values."""
config = RunPodConfig(
api_key="test_key",
volume_id="test_volume",
container_registry_auth_id="auth_123",
default_image="custom/image:tag",
default_container_disk_gb=100,
datacenters=["EUR-IS-1", "EUR-IS-2"]
)
assert config.container_registry_auth_id == "auth_123"
assert config.default_image == "custom/image:tag"
assert config.default_container_disk_gb == 100
assert config.datacenters == ["EUR-IS-1", "EUR-IS-2"]
def test_config_validation_missing_api_key(self):
"""Test validation fails with missing API key."""
with pytest.raises(ValueError, match="RUNPOD_API_KEY is required"):
RunPodConfig(api_key="", volume_id="test_volume")
def test_config_validation_missing_volume_id(self):
"""Test validation fails with missing volume ID."""
with pytest.raises(ValueError, match="RUNPOD_VOLUME_ID is required"):
RunPodConfig(api_key="test_key", volume_id="")
def test_validate_method_returns_errors(self):
"""Test validate() method returns list of errors."""
config = RunPodConfig(
api_key="short", # Too short
volume_id="invalid-id!", # Invalid characters
default_container_disk_gb=5, # Too small
datacenters=[] # Empty
)
errors = config.validate()
assert len(errors) == 4
assert any("too short" in err.lower() for err in errors)
assert any("invalid" in err.lower() for err in errors)
assert any("10GB" in err for err in errors)
assert any("datacenter" in err.lower() for err in errors)
def test_validate_method_empty_for_valid_config(self, sample_config):
"""Test validate() returns empty list for valid config."""
errors = sample_config.validate()
assert errors == []
@pytest.mark.parametrize("api_key,expected_valid", [
("test_key_" + "x" * 32, True), # Valid length
("short", False), # Too short
])
def test_api_key_validation(self, api_key, expected_valid):
"""Test API key validation with different inputs."""
config = RunPodConfig(
api_key=api_key,
volume_id="valid_volume_id"
)
errors = config.validate()
if expected_valid:
assert not any("api_key" in err.lower() for err in errors)
else:
assert any("api_key" in err.lower() for err in errors)
@pytest.mark.parametrize("volume_id,expected_valid", [
("se3zdnb5o4", True), # Valid alphanumeric
("volume123", True), # Valid alphanumeric
("invalid-volume!", False), # Invalid characters
])
def test_volume_id_validation(self, volume_id, expected_valid):
"""Test volume ID validation with different inputs."""
config = RunPodConfig(
api_key="test_key_" + "x" * 32,
volume_id=volume_id
)
errors = config.validate()
if expected_valid:
assert not any("volume_id" in err.lower() for err in errors)
else:
assert any("volume_id" in err.lower() for err in errors)
def test_from_env_with_mock_env(self, mock_env, monkeypatch):
"""Test loading config from environment variables."""
# Mock the Path resolution to avoid needing actual .env.runpod
monkeypatch.setattr(
"runpod.config.Path.exists",
lambda self: False # Pretend .env.runpod doesn't exist
)
config = RunPodConfig.from_env()
assert config.api_key == mock_env["RUNPOD_API_KEY"]
assert config.volume_id == mock_env["RUNPOD_VOLUME_ID"]
assert config.container_registry_auth_id == mock_env["RUNPOD_CONTAINER_REGISTRY_AUTH_ID"]
def test_from_env_with_file(self, create_env_file):
"""Test loading config from .env.runpod file."""
config = RunPodConfig.from_env(str(create_env_file))
# Test that config was loaded (values may differ based on actual .env.runpod)
assert config.api_key # Should not be empty
assert config.volume_id
assert len(config.api_key) >= 32 # Should be valid length
def test_from_env_missing_file(self, temp_directory):
"""Test from_env raises error for missing file."""
missing_file = temp_directory / "nonexistent.env"
with pytest.raises(FileNotFoundError, match="Environment file not found"):
RunPodConfig.from_env(str(missing_file))
def test_from_env_missing_credentials(self, monkeypatch):
"""Test from_env with missing environment variables."""
# Clear environment variables
monkeypatch.delenv("RUNPOD_API_KEY", raising=False)
monkeypatch.delenv("RUNPOD_VOLUME_ID", raising=False)
# Mock Path.exists to avoid loading actual .env.runpod
monkeypatch.setattr(
"runpod.config.Path.exists",
lambda self: False
)
with pytest.raises(ValueError, match="RUNPOD_API_KEY is required"):
RunPodConfig.from_env()
def test_default_datacenters(self):
"""Test default datacenters are set correctly."""
config = RunPodConfig(
api_key="test_key",
volume_id="test_volume"
)
assert config.datacenters == ["EUR-IS-1"]
def test_custom_datacenters(self):
"""Test custom datacenters override default."""
config = RunPodConfig(
api_key="test_key",
volume_id="test_volume",
datacenters=["US-WEST-1", "US-EAST-1"]
)
assert config.datacenters == ["US-WEST-1", "US-EAST-1"]
@pytest.mark.parametrize("disk_size,expected_valid", [
(50, True), # Valid
(100, True), # Valid
(10, True), # Minimum valid
(5, False), # Too small
(0, False), # Zero
])
def test_container_disk_validation(self, disk_size, expected_valid):
"""Test container disk size validation."""
config = RunPodConfig(
api_key="test_key_" + "x" * 32,
volume_id="valid_volume",
default_container_disk_gb=disk_size
)
errors = config.validate()
if expected_valid:
assert not any("disk" in err.lower() for err in errors)
else:
assert any("disk" in err.lower() for err in errors)