test: add tests for RDAgentLog debug() and LiteLLMAPIBackend

This commit is contained in:
TPTBusiness
2026-05-08 22:47:41 +02:00
parent f0ac999dbe
commit 4d9459a9f9
4 changed files with 208 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
"""Tests for rdagent/log/logger.py — RDAgentLog wrapper around loguru."""
from __future__ import annotations
import pytest
from rdagent.log.logger import RDAgentLog
class TestRDAgentLog:
def test_singleton(self):
a = RDAgentLog()
b = RDAgentLog()
assert a is b
def test_has_debug_method(self):
logger = RDAgentLog()
assert hasattr(logger, "debug")
assert callable(logger.debug)
def test_debug_accepts_args(self):
logger = RDAgentLog()
logger.debug("test message")
logger.debug("test message", tag="mytag")
logger.debug("test message", raw=True)
logger.debug("test message", tag="x", raw=False)
def test_info_warning_error_exist(self):
logger = RDAgentLog()
for method in ("info", "warning", "error", "debug"):
assert hasattr(logger, method), f"missing {method}"
assert callable(getattr(logger, method)), f"{method} not callable"
def test_log_object(self):
logger = RDAgentLog()
logger.log_object({"key": "value"})
logger.log_object(["a", "b"], tag="test")
def test_tag_context_manager(self):
logger = RDAgentLog()
with logger.tag("test_tag"):
logger.info("inside tag")
logger.info("outside tag")
def test_debug_does_not_raise_on_empty(self):
logger = RDAgentLog()
logger.debug("")
logger.debug("")
logger.debug("")
def test_debug_tag_propagation(self):
logger = RDAgentLog()
with logger.tag("debug_context"):
logger.debug("debug with tag", tag="inner")
logger.debug("debug outside")
class TestRDAgentLogMethods:
"""Verify all log-level methods exist and are callable."""
def test_all_methods_present(self):
logger = RDAgentLog()
expected = {"debug", "info", "warning", "error", "log_object"}
for name in expected:
assert hasattr(logger, name), f"RDAgentLog missing method: {name}"
def test_methods_are_bound(self):
logger = RDAgentLog()
for name in ("debug", "info", "warning", "error"):
method = getattr(logger, name)
assert callable(method)
# Should accept at minimum a string message
method("bound method test")
+136
View File
@@ -0,0 +1,136 @@
"""Tests for rdagent/oai/backend/litellm.py — LiteLLM API backend.
These are offline tests that don't require a running LLM server.
"""
from __future__ import annotations
from unittest.mock import MagicMock, patch
import pytest
from rdagent.oai.backend.litellm import LiteLLMAPIBackend
class TestLiteLLMAPIBackendInit:
def test_creates_without_crash(self):
backend = LiteLLMAPIBackend()
assert backend is not None
def test_has_inner_function(self):
backend = LiteLLMAPIBackend()
assert hasattr(backend, "_create_chat_completion_inner_function")
def test_complete_kwargs_returns_dict_like(self):
backend = LiteLLMAPIBackend()
kwargs = backend.get_complete_kwargs()
assert kwargs is not None
def test_supports_response_schema_returns_bool(self):
backend = LiteLLMAPIBackend()
result = backend.supports_response_schema()
assert isinstance(result, bool)
class TestLiteLLMAPIBackendTokenCounting:
@patch("rdagent.oai.backend.litellm.token_counter")
def test_calculate_token_from_messages_returns_int(self, mock_counter):
mock_counter.return_value = 42
backend = LiteLLMAPIBackend()
result = backend._calculate_token_from_messages(
[{"role": "user", "content": "hello"}]
)
assert isinstance(result, int)
assert result == 42
@patch("rdagent.oai.backend.litellm.token_counter")
def test_calculate_token_from_messages_handles_empty(self, mock_counter):
mock_counter.return_value = 0
backend = LiteLLMAPIBackend()
result = backend._calculate_token_from_messages([])
assert result == 0
class TestLiteLLMAPIBackendStreaming:
@patch("rdagent.oai.backend.litellm.completion")
@patch("rdagent.oai.backend.litellm.token_counter")
def test_non_streaming_response(self, mock_tokens, mock_completion):
mock_tokens.return_value = 10
from rdagent.oai.backend.litellm import LITELLM_SETTINGS
LITELLM_SETTINGS.chat_stream = False
try:
# Build a proper mock response structure
resp = MagicMock()
choice = MagicMock()
msg = MagicMock()
msg.content = '{"key": "value"}'
choice.message = msg
choice.finish_reason = "stop"
resp.choices = [choice]
mock_completion.return_value = resp
backend = LiteLLMAPIBackend()
content, finish = backend._create_chat_completion_inner_function(
messages=[{"role": "user", "content": "test"}],
)
assert '{"key": "value"}' in str(content)
assert finish == "stop"
finally:
LITELLM_SETTINGS.chat_stream = True
@patch("rdagent.oai.backend.litellm.completion")
@patch("rdagent.oai.backend.litellm.token_counter")
def test_streaming_response(self, mock_tokens, mock_completion):
mock_tokens.return_value = 5
chunk1 = {"choices": [{"finish_reason": None, "delta": {"content": "hello"}}]}
chunk2 = {"choices": [{"finish_reason": "stop", "delta": {}}]}
mock_completion.return_value = [chunk1, chunk2]
backend = LiteLLMAPIBackend()
from rdagent.oai.backend.litellm import LITELLM_SETTINGS
LITELLM_SETTINGS.chat_stream = True
try:
content, finish = backend._create_chat_completion_inner_function(
messages=[{"role": "user", "content": "hi"}],
)
assert "hello" in content
finally:
LITELLM_SETTINGS.chat_stream = False
class TestLiteLLMAPIBackendEdgeCases:
def test_empty_messages_token_count(self):
backend = LiteLLMAPIBackend()
with patch("rdagent.oai.backend.litellm.token_counter", return_value=0):
result = backend._calculate_token_from_messages([])
assert result == 0
def test_unicode_messages_token_count(self):
backend = LiteLLMAPIBackend()
messages = [{"role": "user", "content": "üéñ–—…€🦀"}]
with patch("rdagent.oai.backend.litellm.token_counter", return_value=5):
result = backend._calculate_token_from_messages(messages)
assert result == 5
def test_very_long_message_token_count(self):
backend = LiteLLMAPIBackend()
long_msg = "hello " * 10000
messages = [{"role": "user", "content": long_msg}]
with patch("rdagent.oai.backend.litellm.token_counter", return_value=20000):
result = backend._calculate_token_from_messages(messages)
assert result == 20000
def test_build_log_messages_returns_string(self):
backend = LiteLLMAPIBackend()
messages = [
{"role": "system", "content": "test system"},
{"role": "user", "content": "test user"},
]
result = backend._build_log_messages(messages)
assert isinstance(result, str)
def test_supports_response_schema_does_not_crash(self):
backend = LiteLLMAPIBackend()
for _ in range(10):
backend.supports_response_schema()