chore: fix ruff linting and async patterns in tests/

This commit is contained in:
Jan Piotrowski
2026-03-19 16:16:25 +01:00
parent 81316ffb2d
commit 2fdf961fee
9 changed files with 73 additions and 76 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ class TestCheckCcproxyAuth:
mock_run.return_value = MagicMock(
returncode=0, stdout="Authenticated", stderr=""
)
valid, msg = check_ccproxy_auth("codex")
valid, _msg = check_ccproxy_auth("codex")
assert valid is True
cmd = mock_run.call_args[0][0]
assert cmd[1:] == ["auth", "status", "codex"]
+3 -3
View File
@@ -249,7 +249,7 @@ class TestRetryAsync:
)
# With 50% jitter, not all delays should be identical
if len(delays) > 1:
assert len(set(f"{d:.4f}" for d in delays)) > 1
assert len({f"{d:.4f}" for d in delays}) > 1
# ═══════════════════════════════════════════════════════════════════
@@ -1438,10 +1438,10 @@ class TestInboundConsumer:
async def _test():
consumer = self._make_consumer()
# Start and immediately stop
asyncio.create_task(consumer.run())
task = asyncio.create_task(consumer.run())
await asyncio.sleep(0.1)
await consumer.stop()
await asyncio.sleep(0.1)
await task
assert consumer._stopping is True
_run(_test())
+10 -19
View File
@@ -1,38 +1,29 @@
"""Tests for the /compact command (compact_conversation helper)."""
import asyncio
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
@pytest.fixture
def _run():
"""Helper to run async tests."""
loop = asyncio.new_event_loop()
yield loop.run_until_complete
loop.close()
from tests.conftest import run_async as _run
class TestCompactGuards:
"""Guard conditions that return early without touching the middleware."""
def test_no_agent(self, _run):
def test_no_agent(self):
from EvoScientist.cli.commands import compact_conversation
result = _run(compact_conversation(agent=None, thread_id="abc"))
assert result.status == "noop"
assert "Nothing to compact" in result.message
def test_no_thread_id(self, _run):
def test_no_thread_id(self):
from EvoScientist.cli.commands import compact_conversation
result = _run(compact_conversation(agent=MagicMock(), thread_id=None))
assert result.status == "noop"
assert "Nothing to compact" in result.message
def test_empty_messages(self, _run):
def test_empty_messages(self):
from EvoScientist.cli.commands import compact_conversation
agent = MagicMock()
@@ -43,7 +34,7 @@ class TestCompactGuards:
assert result.status == "noop"
assert "no messages" in result.message
def test_state_read_failure(self, _run):
def test_state_read_failure(self):
from EvoScientist.cli.commands import compact_conversation
agent = MagicMock()
@@ -57,7 +48,7 @@ class TestCompactGuards:
class TestCompactCutoffZero:
"""When cutoff == 0, conversation is within retention budget."""
def test_nothing_to_compact_short_conversation(self, _run):
def test_nothing_to_compact_short_conversation(self):
from EvoScientist.cli.commands import compact_conversation
agent = MagicMock()
@@ -102,7 +93,7 @@ class TestCompactCutoffZero:
class TestCompactNegligibleSavings:
"""When cutoff > 0 but savings are too small to be worth it."""
def test_skip_when_few_messages_and_low_tokens(self, _run):
def test_skip_when_few_messages_and_low_tokens(self):
from EvoScientist.cli.commands import compact_conversation
agent = MagicMock()
@@ -151,7 +142,7 @@ class TestCompactNegligibleSavings:
# No LLM call should have been made
mock_middleware_inst._acreate_summary.assert_not_called()
def test_still_compacts_when_few_messages_but_high_tokens(self, _run):
def test_still_compacts_when_few_messages_but_high_tokens(self):
"""2 messages but they account for >2% of tokens — should compact."""
from langchain_core.messages import HumanMessage
@@ -211,7 +202,7 @@ class TestCompactNegligibleSavings:
class TestCompactSuccess:
"""Normal compaction flow."""
def test_successful_compaction(self, _run):
def test_successful_compaction(self):
from langchain_core.messages import HumanMessage
from EvoScientist.cli.commands import compact_conversation
@@ -281,7 +272,7 @@ class TestCompactSuccess:
assert "_summarization_event" in event_data
assert event_data["_summarization_event"]["cutoff_index"] == 15
def test_offload_failure_non_fatal(self, _run):
def test_offload_failure_non_fatal(self):
"""Offload failure should not prevent compaction."""
from langchain_core.messages import HumanMessage
+2 -2
View File
@@ -283,11 +283,11 @@ class TestDingTalkProbe:
def test_missing_client_id(self):
from EvoScientist.channels.dingtalk.probe import validate_dingtalk
ok, msg = _run(validate_dingtalk("", "secret"))
ok, _msg = _run(validate_dingtalk("", "secret"))
assert ok is False
def test_missing_client_secret(self):
from EvoScientist.channels.dingtalk.probe import validate_dingtalk
ok, msg = _run(validate_dingtalk("id", ""))
ok, _msg = _run(validate_dingtalk("id", ""))
assert ok is False
+1 -1
View File
@@ -580,7 +580,7 @@ class TestMiniMaxProvider:
def test_minimax_model_ids_correct(self):
"""MiniMax model IDs should match the official API model names."""
minimax_models = get_models_for_provider("minimax")
model_dict = {name: mid for name, mid in minimax_models}
model_dict = dict(minimax_models)
assert model_dict["minimax-m2.7"] == "MiniMax-M2.7"
assert model_dict["minimax-m2.5"] == "MiniMax-M2.5"
assert model_dict["minimax-m2.5-highspeed"] == "MiniMax-M2.5-highspeed"
+2 -1
View File
@@ -556,7 +556,8 @@ class TestAddMcpServer:
add_mcp_server("a", "stdio", command="cmd1")
add_mcp_server("b", "http", url="http://x")
data = yaml.safe_load(user_mcp_dir.read_text())
assert "a" in data and "b" in data
assert "a" in data
assert "b" in data
class TestRemoveMcpServer:
+37 -37
View File
@@ -25,7 +25,7 @@ from tests.conftest import run_async as _run
class TestGenerateThreadId(unittest.TestCase):
def test_length(self):
tid = generate_thread_id()
self.assertEqual(len(tid), 8)
assert len(tid) == 8
def test_hex(self):
tid = generate_thread_id()
@@ -33,58 +33,58 @@ class TestGenerateThreadId(unittest.TestCase):
def test_uniqueness(self):
ids = {generate_thread_id() for _ in range(100)}
self.assertEqual(len(ids), 100)
assert len(ids) == 100
class TestGetDbPath(unittest.TestCase):
def test_uses_config_dir(self):
path = get_db_path()
self.assertTrue(str(path).endswith("sessions.db"))
self.assertIn(".config", str(path))
self.assertIn("evoscientist", str(path))
assert str(path).endswith("sessions.db")
assert ".config" in str(path)
assert "evoscientist" in str(path)
class TestFormatRelativeTime(unittest.TestCase):
def test_none(self):
self.assertEqual(_format_relative_time(None), "")
assert _format_relative_time(None) == ""
def test_invalid(self):
self.assertEqual(_format_relative_time("not-a-date"), "")
assert _format_relative_time("not-a-date") == ""
def test_recent(self):
from datetime import datetime
now = datetime.now(UTC).isoformat()
result = _format_relative_time(now)
self.assertIn("just now", result)
assert "just now" in result
def test_minutes(self):
from datetime import datetime, timedelta
ts = (datetime.now(UTC) - timedelta(minutes=5)).isoformat()
result = _format_relative_time(ts)
self.assertIn("min ago", result)
assert "min ago" in result
def test_hours(self):
from datetime import datetime, timedelta
ts = (datetime.now(UTC) - timedelta(hours=2)).isoformat()
result = _format_relative_time(ts)
self.assertIn("hour", result)
assert "hour" in result
def test_days(self):
from datetime import datetime, timedelta
ts = (datetime.now(UTC) - timedelta(days=3)).isoformat()
result = _format_relative_time(ts)
self.assertIn("day", result)
assert "day" in result
def test_months(self):
from datetime import datetime, timedelta
ts = (datetime.now(UTC) - timedelta(days=65)).isoformat()
result = _format_relative_time(ts)
self.assertIn("month", result)
assert "month" in result
class TestThreadFunctions(unittest.TestCase):
@@ -183,44 +183,44 @@ class TestThreadFunctions(unittest.TestCase):
def test_list_threads(self):
threads = _run(list_threads(limit=10))
# Should only contain EvoScientist threads
self.assertEqual(len(threads), 3)
assert len(threads) == 3
# Most recent first
self.assertEqual(threads[0]["thread_id"], "def00001")
assert threads[0]["thread_id"] == "def00001"
def test_list_threads_with_message_count(self):
threads = _run(list_threads(limit=10, include_message_count=True))
self.assertIn("message_count", threads[0])
assert "message_count" in threads[0]
def test_thread_exists_true(self):
self.assertTrue(_run(thread_exists("abc12345")))
assert _run(thread_exists("abc12345"))
def test_thread_exists_false(self):
self.assertFalse(_run(thread_exists("nonexist")))
assert not _run(thread_exists("nonexist"))
def test_find_similar(self):
similar = _run(find_similar_threads("abc1"))
self.assertEqual(len(similar), 2)
self.assertIn("abc12345", similar)
self.assertIn("abc12399", similar)
assert len(similar) == 2
assert "abc12345" in similar
assert "abc12399" in similar
def test_find_similar_no_match(self):
similar = _run(find_similar_threads("xyz"))
self.assertEqual(len(similar), 0)
assert len(similar) == 0
def test_get_most_recent(self):
recent = _run(get_most_recent())
self.assertIsNotNone(recent)
self.assertEqual(recent, "def00001")
assert recent is not None
assert recent == "def00001"
def test_get_thread_metadata(self):
meta = _run(get_thread_metadata("abc12345"))
self.assertIsNotNone(meta)
self.assertEqual(meta["workspace_dir"], "/tmp/ws_abc12345")
self.assertEqual(meta["model"], "claude-sonnet-4-5")
assert meta is not None
assert meta["workspace_dir"] == "/tmp/ws_abc12345"
assert meta["model"] == "claude-sonnet-4-5"
def test_get_thread_metadata_missing(self):
meta = _run(get_thread_metadata("nonexist"))
self.assertIsNone(meta)
assert meta is None
def test_delete_thread(self):
# Insert a thread to delete
@@ -242,29 +242,29 @@ class TestThreadFunctions(unittest.TestCase):
_run(_insert())
self.assertTrue(_run(thread_exists("todelete")))
self.assertTrue(_run(delete_thread("todelete")))
self.assertFalse(_run(thread_exists("todelete")))
assert _run(thread_exists("todelete"))
assert _run(delete_thread("todelete"))
assert not _run(thread_exists("todelete"))
def test_delete_nonexistent(self):
self.assertFalse(_run(delete_thread("nope1234")))
assert not _run(delete_thread("nope1234"))
# -- Agent isolation: OtherAgent data should never be visible --
def test_thread_exists_ignores_other_agent(self):
self.assertFalse(_run(thread_exists("zzz99999")))
assert not _run(thread_exists("zzz99999"))
def test_find_similar_ignores_other_agent(self):
similar = _run(find_similar_threads("zzz"))
self.assertEqual(len(similar), 0)
assert len(similar) == 0
def test_get_metadata_ignores_other_agent(self):
meta = _run(get_thread_metadata("zzz99999"))
self.assertIsNone(meta)
assert meta is None
def test_delete_ignores_other_agent(self):
# Should not delete OtherAgent's data
self.assertFalse(_run(delete_thread("zzz99999")))
assert not _run(delete_thread("zzz99999"))
def test_delete_thread_preserves_other_agent_writes(self):
"""Deleting a shared thread_id must only remove writes linked to
@@ -329,8 +329,8 @@ class TestThreadFunctions(unittest.TestCase):
return [r[0] for r in rows]
remaining = _run(_check())
self.assertIn("cp_other_shared", remaining)
self.assertNotIn("cp_evo_shared", remaining)
assert "cp_other_shared" in remaining
assert "cp_evo_shared" not in remaining
if __name__ == "__main__":
+16 -11
View File
@@ -203,10 +203,11 @@ class TestExtractSummarizationText:
def test_content_blocks(self):
class Msg:
content = [
{"type": "text", "text": "part1"},
{"type": "text", "text": "part2"},
]
def __init__(self):
self.content = [
{"type": "text", "text": "part1"},
{"type": "text", "text": "part2"},
]
assert _extract_summarization_text(Msg()) == "part1part2"
@@ -214,13 +215,15 @@ class TestExtractSummarizationText:
"""Content blocks may include 'index' field — should still extract text."""
class Msg:
content = [{"type": "text", "text": " vs", "index": 1}]
def __init__(self):
self.content = [{"type": "text", "text": " vs", "index": 1}]
assert _extract_summarization_text(Msg()) == " vs"
def test_empty_list(self):
class Msg:
content = []
def __init__(self):
self.content = []
assert _extract_summarization_text(Msg()) == ""
@@ -232,15 +235,17 @@ class TestExtractSummarizationText:
def test_mixed_block_types(self):
class Msg:
content = [
{"type": "text", "text": "hello"},
{"type": "image", "url": "..."},
]
def __init__(self):
self.content = [
{"type": "text", "text": "hello"},
{"type": "image", "url": "..."},
]
assert _extract_summarization_text(Msg()) == "hello"
def test_string_blocks_in_list(self):
class Msg:
content = ["hello", "world"]
def __init__(self):
self.content = ["hello", "world"]
assert _extract_summarization_text(Msg()) == "helloworld"
+1 -1
View File
@@ -306,7 +306,7 @@ class TestWeChatCrypto:
# Parse and verify the encrypted content decrypts back
root = ET.fromstring(xml_reply)
encrypt = root.find("Encrypt").text
decrypted, app_id = crypto.decrypt(encrypt)
decrypted, _app_id = crypto.decrypt(encrypt)
assert decrypted == msg