forked from Rockachopa/Timmy-time-dashboard
Compare commits
1 Commits
test/chat-
...
kimi/issue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2d662257a |
@@ -242,64 +242,6 @@ def produce_agent_state(agent_id: str, presence: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _get_agents_online() -> int:
|
||||
"""Return the count of agents with a non-offline status."""
|
||||
try:
|
||||
from timmy.agents.loader import list_agents
|
||||
|
||||
agents = list_agents()
|
||||
return sum(1 for a in agents if a.get("status", "") not in ("offline", ""))
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to count agents: %s", exc)
|
||||
return 0
|
||||
|
||||
|
||||
def _get_visitors() -> int:
|
||||
"""Return the count of active WebSocket visitor clients."""
|
||||
try:
|
||||
from dashboard.routes.world import _ws_clients
|
||||
|
||||
return len(_ws_clients)
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to count visitors: %s", exc)
|
||||
return 0
|
||||
|
||||
|
||||
def _get_uptime_seconds() -> int:
|
||||
"""Return seconds elapsed since application start."""
|
||||
try:
|
||||
from config import APP_START_TIME
|
||||
|
||||
return int((datetime.now(UTC) - APP_START_TIME).total_seconds())
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to calculate uptime: %s", exc)
|
||||
return 0
|
||||
|
||||
|
||||
def _get_thinking_active() -> bool:
|
||||
"""Return True if the thinking engine is enabled and running."""
|
||||
try:
|
||||
from config import settings
|
||||
from timmy.thinking import thinking_engine
|
||||
|
||||
return settings.thinking_enabled and thinking_engine is not None
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to check thinking status: %s", exc)
|
||||
return False
|
||||
|
||||
|
||||
def _get_memory_count() -> int:
|
||||
"""Return total entries in the vector memory store."""
|
||||
try:
|
||||
from timmy.memory_system import get_memory_stats
|
||||
|
||||
stats = get_memory_stats()
|
||||
return stats.get("total_entries", 0)
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to count memories: %s", exc)
|
||||
return 0
|
||||
|
||||
|
||||
def produce_system_status() -> dict:
|
||||
"""Generate a system_status message for the Matrix.
|
||||
|
||||
@@ -328,14 +270,64 @@ def produce_system_status() -> dict:
|
||||
"ts": 1742529600,
|
||||
}
|
||||
"""
|
||||
# Count agents with status != offline
|
||||
agents_online = 0
|
||||
try:
|
||||
from timmy.agents.loader import list_agents
|
||||
|
||||
agents = list_agents()
|
||||
agents_online = sum(1 for a in agents if a.get("status", "") not in ("offline", ""))
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to count agents: %s", exc)
|
||||
|
||||
# Count visitors from WebSocket clients
|
||||
visitors = 0
|
||||
try:
|
||||
from dashboard.routes.world import _ws_clients
|
||||
|
||||
visitors = len(_ws_clients)
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to count visitors: %s", exc)
|
||||
|
||||
# Calculate uptime
|
||||
uptime_seconds = 0
|
||||
try:
|
||||
from datetime import UTC
|
||||
|
||||
from config import APP_START_TIME
|
||||
|
||||
uptime_seconds = int((datetime.now(UTC) - APP_START_TIME).total_seconds())
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to calculate uptime: %s", exc)
|
||||
|
||||
# Check thinking engine status
|
||||
thinking_active = False
|
||||
try:
|
||||
from config import settings
|
||||
from timmy.thinking import thinking_engine
|
||||
|
||||
thinking_active = settings.thinking_enabled and thinking_engine is not None
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to check thinking status: %s", exc)
|
||||
|
||||
# Count memories in vector store
|
||||
memory_count = 0
|
||||
try:
|
||||
from timmy.memory_system import get_memory_stats
|
||||
|
||||
stats = get_memory_stats()
|
||||
memory_count = stats.get("total_entries", 0)
|
||||
except Exception as exc:
|
||||
logger.debug("Failed to count memories: %s", exc)
|
||||
|
||||
return {
|
||||
"type": "system_status",
|
||||
"data": {
|
||||
"agents_online": _get_agents_online(),
|
||||
"visitors": _get_visitors(),
|
||||
"uptime_seconds": _get_uptime_seconds(),
|
||||
"thinking_active": _get_thinking_active(),
|
||||
"memory_count": _get_memory_count(),
|
||||
"agents_online": agents_online,
|
||||
"visitors": visitors,
|
||||
"uptime_seconds": uptime_seconds,
|
||||
"thinking_active": thinking_active,
|
||||
"memory_count": memory_count,
|
||||
},
|
||||
"ts": int(time.time()),
|
||||
}
|
||||
|
||||
@@ -528,18 +528,14 @@ class CascadeRouter:
|
||||
|
||||
return True
|
||||
|
||||
def _filter_providers(self, cascade_tier: str | None) -> list["Provider"]:
|
||||
"""Return the provider list filtered by tier.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If a tier is specified but no matching providers exist.
|
||||
"""
|
||||
def _get_providers_for_tier(self, cascade_tier: str | None) -> list[Provider]:
|
||||
"""Filter providers by tier, returning eligible providers."""
|
||||
if cascade_tier == "frontier_required":
|
||||
providers = [p for p in self.providers if p.type == "anthropic"]
|
||||
if not providers:
|
||||
raise RuntimeError("No Anthropic provider configured for 'frontier_required' tier.")
|
||||
return providers
|
||||
if cascade_tier:
|
||||
elif cascade_tier:
|
||||
providers = [p for p in self.providers if p.tier == cascade_tier]
|
||||
if not providers:
|
||||
raise RuntimeError(f"No providers found for tier: {cascade_tier}")
|
||||
@@ -548,18 +544,19 @@ class CascadeRouter:
|
||||
|
||||
async def _try_single_provider(
|
||||
self,
|
||||
provider: "Provider",
|
||||
provider: Provider,
|
||||
messages: list[dict],
|
||||
model: str | None,
|
||||
temperature: float,
|
||||
max_tokens: int | None,
|
||||
content_type: ContentType,
|
||||
errors: list[str],
|
||||
) -> dict | None:
|
||||
"""Attempt one provider, returning a result dict on success or None on failure.
|
||||
"""Attempt a single provider request.
|
||||
|
||||
On failure the error string is appended to *errors* and the provider's
|
||||
failure metrics are updated so the caller can move on to the next provider.
|
||||
Returns:
|
||||
Response dict on success, None if provider should be skipped.
|
||||
Raises:
|
||||
RuntimeError: If the provider attempt fails.
|
||||
"""
|
||||
if not self._is_provider_available(provider):
|
||||
return None
|
||||
@@ -575,14 +572,14 @@ class CascadeRouter:
|
||||
|
||||
selected_model, is_fallback_model = self._select_model(provider, model, content_type)
|
||||
|
||||
try:
|
||||
result = await self._attempt_with_retry(
|
||||
provider, messages, selected_model, temperature, max_tokens, content_type
|
||||
)
|
||||
except RuntimeError as exc:
|
||||
errors.append(str(exc))
|
||||
self._record_failure(provider)
|
||||
return None
|
||||
result = await self._attempt_with_retry(
|
||||
provider,
|
||||
messages,
|
||||
selected_model,
|
||||
temperature,
|
||||
max_tokens,
|
||||
content_type,
|
||||
)
|
||||
|
||||
self._record_success(provider, result.get("latency_ms", 0))
|
||||
return {
|
||||
@@ -627,14 +624,23 @@ class CascadeRouter:
|
||||
logger.debug("Detected %s content, selecting appropriate model", content_type.value)
|
||||
|
||||
errors: list[str] = []
|
||||
providers = self._filter_providers(cascade_tier)
|
||||
providers = self._get_providers_for_tier(cascade_tier)
|
||||
|
||||
for provider in providers:
|
||||
result = await self._try_single_provider(
|
||||
provider, messages, model, temperature, max_tokens, content_type, errors
|
||||
)
|
||||
if result is not None:
|
||||
return result
|
||||
try:
|
||||
result = await self._try_single_provider(
|
||||
provider,
|
||||
messages,
|
||||
model,
|
||||
temperature,
|
||||
max_tokens,
|
||||
content_type,
|
||||
)
|
||||
if result is not None:
|
||||
return result
|
||||
except RuntimeError as exc:
|
||||
errors.append(str(exc))
|
||||
self._record_failure(provider)
|
||||
|
||||
raise RuntimeError(f"All providers failed: {'; '.join(errors)}")
|
||||
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
"""Unit tests for src/infrastructure/chat_store.py."""
|
||||
|
||||
import sqlite3
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from src.infrastructure.chat_store import MAX_MESSAGES, Message, MessageLog, _get_conn
|
||||
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def tmp_db(tmp_path: Path) -> Path:
|
||||
"""Return a temporary database path."""
|
||||
return tmp_path / "test_chat.db"
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def log(tmp_db: Path) -> MessageLog:
|
||||
"""Return a MessageLog backed by a temp database."""
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
yield ml
|
||||
ml.close()
|
||||
|
||||
|
||||
# ── Message dataclass ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestMessage:
|
||||
def test_default_source(self):
|
||||
m = Message(role="user", content="hi", timestamp="2026-01-01T00:00:00")
|
||||
assert m.source == "browser"
|
||||
|
||||
def test_custom_source(self):
|
||||
m = Message(role="agent", content="ok", timestamp="t1", source="telegram")
|
||||
assert m.source == "telegram"
|
||||
|
||||
def test_fields(self):
|
||||
m = Message(role="error", content="boom", timestamp="t2", source="api")
|
||||
assert m.role == "error"
|
||||
assert m.content == "boom"
|
||||
assert m.timestamp == "t2"
|
||||
|
||||
|
||||
# ── _get_conn context manager ──────────────────────────────────────────
|
||||
|
||||
|
||||
class TestGetConn:
|
||||
def test_creates_db_and_table(self, tmp_db: Path):
|
||||
with _get_conn(tmp_db) as conn:
|
||||
tables = conn.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table'"
|
||||
).fetchall()
|
||||
names = [t["name"] for t in tables]
|
||||
assert "chat_messages" in names
|
||||
|
||||
def test_creates_parent_dirs(self, tmp_path: Path):
|
||||
deep = tmp_path / "a" / "b" / "c" / "chat.db"
|
||||
with _get_conn(deep) as conn:
|
||||
assert deep.parent.exists()
|
||||
|
||||
def test_connection_closed_after_context(self, tmp_db: Path):
|
||||
with _get_conn(tmp_db) as conn:
|
||||
conn.execute("SELECT 1")
|
||||
# Connection should be closed — operations should fail
|
||||
with pytest.raises(Exception):
|
||||
conn.execute("SELECT 1")
|
||||
|
||||
|
||||
# ── MessageLog core operations ─────────────────────────────────────────
|
||||
|
||||
|
||||
class TestMessageLogAppendAndAll:
|
||||
def test_append_and_all(self, log: MessageLog):
|
||||
log.append("user", "hello", "t1")
|
||||
log.append("agent", "hi back", "t2", source="api")
|
||||
msgs = log.all()
|
||||
assert len(msgs) == 2
|
||||
assert msgs[0].role == "user"
|
||||
assert msgs[0].content == "hello"
|
||||
assert msgs[0].source == "browser"
|
||||
assert msgs[1].role == "agent"
|
||||
assert msgs[1].source == "api"
|
||||
|
||||
def test_all_returns_ordered_by_id(self, log: MessageLog):
|
||||
for i in range(5):
|
||||
log.append("user", f"msg{i}", f"t{i}")
|
||||
msgs = log.all()
|
||||
assert [m.content for m in msgs] == [f"msg{i}" for i in range(5)]
|
||||
|
||||
def test_all_empty_store(self, log: MessageLog):
|
||||
assert log.all() == []
|
||||
|
||||
|
||||
class TestMessageLogRecent:
|
||||
def test_recent_returns_newest(self, log: MessageLog):
|
||||
for i in range(10):
|
||||
log.append("user", f"msg{i}", f"t{i}")
|
||||
recent = log.recent(limit=3)
|
||||
assert len(recent) == 3
|
||||
assert recent[0].content == "msg7"
|
||||
assert recent[2].content == "msg9"
|
||||
|
||||
def test_recent_oldest_first(self, log: MessageLog):
|
||||
for i in range(5):
|
||||
log.append("user", f"msg{i}", f"t{i}")
|
||||
recent = log.recent(limit=3)
|
||||
# Should be oldest-first within the window
|
||||
assert recent[0].content == "msg2"
|
||||
assert recent[1].content == "msg3"
|
||||
assert recent[2].content == "msg4"
|
||||
|
||||
def test_recent_more_than_exists(self, log: MessageLog):
|
||||
log.append("user", "only", "t0")
|
||||
recent = log.recent(limit=100)
|
||||
assert len(recent) == 1
|
||||
|
||||
def test_recent_empty_store(self, log: MessageLog):
|
||||
assert log.recent() == []
|
||||
|
||||
|
||||
class TestMessageLogClear:
|
||||
def test_clear_removes_all(self, log: MessageLog):
|
||||
for i in range(5):
|
||||
log.append("user", f"msg{i}", f"t{i}")
|
||||
assert len(log) == 5
|
||||
log.clear()
|
||||
assert len(log) == 0
|
||||
assert log.all() == []
|
||||
|
||||
def test_clear_empty_store(self, log: MessageLog):
|
||||
log.clear() # Should not raise
|
||||
assert len(log) == 0
|
||||
|
||||
|
||||
class TestMessageLogLen:
|
||||
def test_len_empty(self, log: MessageLog):
|
||||
assert len(log) == 0
|
||||
|
||||
def test_len_after_appends(self, log: MessageLog):
|
||||
for i in range(7):
|
||||
log.append("user", f"msg{i}", f"t{i}")
|
||||
assert len(log) == 7
|
||||
|
||||
|
||||
class TestMessageLogClose:
|
||||
def test_close_sets_conn_none(self, tmp_db: Path):
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
ml.append("user", "x", "t0")
|
||||
ml.close()
|
||||
assert ml._conn is None
|
||||
|
||||
def test_close_idempotent(self, tmp_db: Path):
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
ml.close()
|
||||
ml.close() # Should not raise
|
||||
|
||||
def test_reopen_after_close(self, tmp_db: Path):
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
ml.append("user", "before", "t0")
|
||||
ml.close()
|
||||
# Should reconnect on next use
|
||||
ml.append("user", "after", "t1")
|
||||
assert len(ml) == 2
|
||||
ml.close()
|
||||
|
||||
|
||||
# ── Pruning ────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestPrune:
|
||||
def test_prune_keeps_max_messages(self, tmp_db: Path):
|
||||
with patch("src.infrastructure.chat_store.MAX_MESSAGES", 5):
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
for i in range(10):
|
||||
ml.append("user", f"msg{i}", f"t{i}")
|
||||
# Should have pruned to 5
|
||||
assert len(ml) == 5
|
||||
msgs = ml.all()
|
||||
# Oldest should be pruned, newest kept
|
||||
assert msgs[0].content == "msg5"
|
||||
assert msgs[-1].content == "msg9"
|
||||
ml.close()
|
||||
|
||||
def test_no_prune_under_limit(self, tmp_db: Path):
|
||||
with patch("src.infrastructure.chat_store.MAX_MESSAGES", 100):
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
for i in range(10):
|
||||
ml.append("user", f"msg{i}", f"t{i}")
|
||||
assert len(ml) == 10
|
||||
ml.close()
|
||||
|
||||
|
||||
# ── Thread safety ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestThreadSafety:
|
||||
def test_concurrent_appends(self, tmp_db: Path):
|
||||
ml = MessageLog(db_path=tmp_db)
|
||||
errors = []
|
||||
|
||||
def writer(start: int):
|
||||
try:
|
||||
for i in range(20):
|
||||
ml.append("user", f"msg{start + i}", f"t{start + i}")
|
||||
except Exception as e:
|
||||
errors.append(e)
|
||||
|
||||
threads = [threading.Thread(target=writer, args=(i * 20,)) for i in range(5)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
assert not errors, f"Thread errors: {errors}"
|
||||
assert len(ml) == 100
|
||||
ml.close()
|
||||
|
||||
|
||||
# ── Edge cases ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestEdgeCases:
|
||||
def test_empty_content(self, log: MessageLog):
|
||||
log.append("user", "", "t0")
|
||||
msgs = log.all()
|
||||
assert len(msgs) == 1
|
||||
assert msgs[0].content == ""
|
||||
|
||||
def test_unicode_content(self, log: MessageLog):
|
||||
log.append("user", "こんにちは 🎉 مرحبا", "t0")
|
||||
msgs = log.all()
|
||||
assert msgs[0].content == "こんにちは 🎉 مرحبا"
|
||||
|
||||
def test_multiline_content(self, log: MessageLog):
|
||||
content = "line1\nline2\nline3"
|
||||
log.append("user", content, "t0")
|
||||
assert log.all()[0].content == content
|
||||
|
||||
def test_special_sql_characters(self, log: MessageLog):
|
||||
log.append("user", "Robert'; DROP TABLE chat_messages;--", "t0")
|
||||
msgs = log.all()
|
||||
assert len(msgs) == 1
|
||||
assert "DROP TABLE" in msgs[0].content
|
||||
@@ -1376,141 +1376,3 @@ class TestIsProviderAvailable:
|
||||
result = router._is_provider_available(provider)
|
||||
assert result is True
|
||||
assert provider.circuit_state == CircuitState.HALF_OPEN
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestFilterProviders:
|
||||
"""Test _filter_providers helper extracted from complete()."""
|
||||
|
||||
def _router(self) -> CascadeRouter:
|
||||
router = CascadeRouter(config_path=Path("/nonexistent"))
|
||||
router.providers = [
|
||||
Provider(
|
||||
name="anthropic-p",
|
||||
type="anthropic",
|
||||
enabled=True,
|
||||
priority=1,
|
||||
api_key="key",
|
||||
tier="frontier",
|
||||
),
|
||||
Provider(
|
||||
name="ollama-p",
|
||||
type="ollama",
|
||||
enabled=True,
|
||||
priority=2,
|
||||
tier="local",
|
||||
),
|
||||
]
|
||||
return router
|
||||
|
||||
def test_no_tier_returns_all_providers(self):
|
||||
router = self._router()
|
||||
result = router._filter_providers(None)
|
||||
assert result is router.providers
|
||||
|
||||
def test_frontier_required_returns_only_anthropic(self):
|
||||
router = self._router()
|
||||
result = router._filter_providers("frontier_required")
|
||||
assert len(result) == 1
|
||||
assert result[0].type == "anthropic"
|
||||
|
||||
def test_frontier_required_no_anthropic_raises(self):
|
||||
router = CascadeRouter(config_path=Path("/nonexistent"))
|
||||
router.providers = [
|
||||
Provider(name="ollama-p", type="ollama", enabled=True, priority=1)
|
||||
]
|
||||
with pytest.raises(RuntimeError, match="No Anthropic provider configured"):
|
||||
router._filter_providers("frontier_required")
|
||||
|
||||
def test_named_tier_filters_by_tier(self):
|
||||
router = self._router()
|
||||
result = router._filter_providers("local")
|
||||
assert len(result) == 1
|
||||
assert result[0].name == "ollama-p"
|
||||
|
||||
def test_named_tier_not_found_raises(self):
|
||||
router = self._router()
|
||||
with pytest.raises(RuntimeError, match="No providers found for tier"):
|
||||
router._filter_providers("nonexistent")
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.asyncio
|
||||
class TestTrySingleProvider:
|
||||
"""Test _try_single_provider helper extracted from complete()."""
|
||||
|
||||
def _router(self) -> CascadeRouter:
|
||||
return CascadeRouter(config_path=Path("/nonexistent"))
|
||||
|
||||
def _provider(self, name: str = "test", ptype: str = "ollama") -> Provider:
|
||||
return Provider(
|
||||
name=name,
|
||||
type=ptype,
|
||||
enabled=True,
|
||||
priority=1,
|
||||
models=[{"name": "llama3.2", "default": True}],
|
||||
)
|
||||
|
||||
async def test_unavailable_provider_returns_none(self):
|
||||
router = self._router()
|
||||
provider = self._provider()
|
||||
provider.enabled = False
|
||||
errors: list[str] = []
|
||||
result = await router._try_single_provider(
|
||||
provider, [], None, 0.7, None, ContentType.TEXT, errors
|
||||
)
|
||||
assert result is None
|
||||
assert errors == []
|
||||
|
||||
async def test_quota_blocked_cloud_provider_returns_none(self):
|
||||
router = self._router()
|
||||
provider = self._provider(ptype="anthropic")
|
||||
errors: list[str] = []
|
||||
with patch("infrastructure.router.cascade._quota_monitor") as mock_qm:
|
||||
mock_qm.select_model.return_value = "qwen3:14b" # non-cloud → ACTIVE tier
|
||||
mock_qm.check.return_value = None
|
||||
result = await router._try_single_provider(
|
||||
provider, [], None, 0.7, None, ContentType.TEXT, errors
|
||||
)
|
||||
assert result is None
|
||||
assert errors == []
|
||||
|
||||
async def test_success_returns_result_dict(self):
|
||||
router = self._router()
|
||||
provider = self._provider()
|
||||
errors: list[str] = []
|
||||
with patch.object(router, "_call_ollama") as mock_call:
|
||||
mock_call.return_value = {"content": "hi", "model": "llama3.2"}
|
||||
result = await router._try_single_provider(
|
||||
provider,
|
||||
[{"role": "user", "content": "hi"}],
|
||||
None,
|
||||
0.7,
|
||||
None,
|
||||
ContentType.TEXT,
|
||||
errors,
|
||||
)
|
||||
assert result is not None
|
||||
assert result["content"] == "hi"
|
||||
assert result["provider"] == "test"
|
||||
assert errors == []
|
||||
|
||||
async def test_failure_appends_error_and_returns_none(self):
|
||||
router = self._router()
|
||||
provider = self._provider()
|
||||
errors: list[str] = []
|
||||
with patch.object(router, "_call_ollama") as mock_call:
|
||||
mock_call.side_effect = RuntimeError("boom")
|
||||
result = await router._try_single_provider(
|
||||
provider,
|
||||
[{"role": "user", "content": "hi"}],
|
||||
None,
|
||||
0.7,
|
||||
None,
|
||||
ContentType.TEXT,
|
||||
errors,
|
||||
)
|
||||
assert result is None
|
||||
assert len(errors) == 1
|
||||
assert "boom" in errors[0]
|
||||
assert provider.metrics.failed_requests == 1
|
||||
|
||||
@@ -6,12 +6,7 @@ import pytest
|
||||
|
||||
from infrastructure.presence import (
|
||||
DEFAULT_PIP_STATE,
|
||||
_get_agents_online,
|
||||
_get_familiar_state,
|
||||
_get_memory_count,
|
||||
_get_thinking_active,
|
||||
_get_uptime_seconds,
|
||||
_get_visitors,
|
||||
produce_agent_state,
|
||||
produce_bark,
|
||||
produce_system_status,
|
||||
@@ -505,36 +500,3 @@ class TestProduceSystemStatus:
|
||||
"""produce_system_status always returns a plain dict."""
|
||||
result = produce_system_status()
|
||||
assert isinstance(result, dict)
|
||||
|
||||
|
||||
class TestSystemStatusHelpers:
|
||||
"""Tests for the helper functions extracted from produce_system_status()."""
|
||||
|
||||
def test_get_agents_online_returns_int(self):
|
||||
"""_get_agents_online returns a non-negative int."""
|
||||
result = _get_agents_online()
|
||||
assert isinstance(result, int)
|
||||
assert result >= 0
|
||||
|
||||
def test_get_visitors_returns_int(self):
|
||||
"""_get_visitors returns a non-negative int."""
|
||||
result = _get_visitors()
|
||||
assert isinstance(result, int)
|
||||
assert result >= 0
|
||||
|
||||
def test_get_uptime_seconds_returns_int(self):
|
||||
"""_get_uptime_seconds returns a non-negative int."""
|
||||
result = _get_uptime_seconds()
|
||||
assert isinstance(result, int)
|
||||
assert result >= 0
|
||||
|
||||
def test_get_thinking_active_returns_bool(self):
|
||||
"""_get_thinking_active returns a bool."""
|
||||
result = _get_thinking_active()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
def test_get_memory_count_returns_int(self):
|
||||
"""_get_memory_count returns a non-negative int."""
|
||||
result = _get_memory_count()
|
||||
assert isinstance(result, int)
|
||||
assert result >= 0
|
||||
|
||||
Reference in New Issue
Block a user