un-inception/clients/python/async/tests/test_credentials.py
russell@unturf.com 1e01d09883 feat: per-client Makefile infrastructure for 4-mode testing
Add per-client Makefiles for C, Python, and Go with:
- CLI mode: Tests --help, arg parsing, syntax validation
- Library mode: Unit tests, import verification
- Integration mode: API contract validation (with credentials)
- Functional mode: Real-world scenario tests

C client:
- 22 library tests (SHA-256, HMAC-SHA256, detect_language)
- Full unsandbox.c implementation with examples

Python client (sync + async):
- Delegates to sync/ and async/ subdirectories
- pytest-based test suites with coverage
- Examples for concurrent execution, streaming

Go client:
- Delegates to sync/ and async/ subdirectories
- go test integration with vet and fmt

Also update detect-changes.sh to detect changes in
both root-level un.* files AND clients/ directory.
2026-01-15 16:39:56 -05:00

104 lines
3.5 KiB
Python

"""
Tests for credential resolution system
"""
import pytest
import os
import tempfile
from pathlib import Path
import sys
# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from un_async import (
CredentialsError,
_resolve_credentials,
_load_credentials_from_csv,
_get_unsandbox_dir,
)
class TestCredentialResolution:
"""Test 4-tier credential resolution system."""
def test_resolve_credentials_from_arguments(self):
"""Test Tier 1: Function arguments take highest priority."""
pk, sk = _resolve_credentials("arg_pk", "arg_sk")
assert pk == "arg_pk"
assert sk == "arg_sk"
def test_resolve_credentials_from_env(self, monkeypatch):
"""Test Tier 2: Environment variables."""
monkeypatch.setenv("UNSANDBOX_PUBLIC_KEY", "env_pk")
monkeypatch.setenv("UNSANDBOX_SECRET_KEY", "env_sk")
# Clear any CSV-based credentials by not providing arguments
pk, sk = _resolve_credentials(None, None)
assert pk == "env_pk"
assert sk == "env_sk"
def test_credentials_error_no_sources(self, monkeypatch):
"""Test CredentialsError when no credentials found."""
# Clear environment
monkeypatch.delenv("UNSANDBOX_PUBLIC_KEY", raising=False)
monkeypatch.delenv("UNSANDBOX_SECRET_KEY", raising=False)
with pytest.raises(CredentialsError) as exc_info:
_resolve_credentials(None, None)
assert "No credentials found" in str(exc_info.value)
def test_arguments_override_env(self, monkeypatch):
"""Test that function arguments override environment variables."""
monkeypatch.setenv("UNSANDBOX_PUBLIC_KEY", "env_pk")
monkeypatch.setenv("UNSANDBOX_SECRET_KEY", "env_sk")
pk, sk = _resolve_credentials("arg_pk", "arg_sk")
assert pk == "arg_pk"
assert sk == "arg_sk"
class TestCSVCredentials:
"""Test CSV credential loading."""
def test_load_csv_valid(self):
"""Test loading valid CSV file."""
with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as f:
f.write("public_key_1,secret_key_1\n")
f.write("public_key_2,secret_key_2\n")
temp_path = f.name
try:
# Load first account
creds = _load_credentials_from_csv(Path(temp_path), 0)
assert creds == ("public_key_1", "secret_key_1")
# Load second account
creds = _load_credentials_from_csv(Path(temp_path), 1)
assert creds == ("public_key_2", "secret_key_2")
finally:
os.unlink(temp_path)
def test_load_csv_nonexistent(self):
"""Test loading from nonexistent file."""
creds = _load_credentials_from_csv(Path("/nonexistent/path.csv"), 0)
assert creds is None
def test_load_csv_with_comments(self):
"""Test loading CSV with comment lines."""
with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as f:
f.write("# This is a comment\n")
f.write("public_key_1,secret_key_1\n")
temp_path = f.name
try:
creds = _load_credentials_from_csv(Path(temp_path), 0)
assert creds == ("public_key_1", "secret_key_1")
finally:
os.unlink(temp_path)
def test_get_unsandbox_dir(self):
"""Test getting ~/.unsandbox directory."""
unsandbox_dir = _get_unsandbox_dir()
assert unsandbox_dir.exists()
assert unsandbox_dir.name == ".unsandbox"