164 lines
5.8 KiB
Python
164 lines
5.8 KiB
Python
"""Gap closure integration tests — dark theme, @-mention API, LocalComputerUseSession."""
|
|
|
|
import pytest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from agentkit.quality.cascade_state_store import (
|
|
InMemoryCascadeStateStore,
|
|
create_cascade_state_store,
|
|
)
|
|
from agentkit.llm.providers.usage_store import (
|
|
InMemoryUsageStore,
|
|
create_usage_store,
|
|
)
|
|
from agentkit.tools.computer_use_session import (
|
|
InMemoryComputerUseSession,
|
|
LocalComputerUseSession,
|
|
ComputerUseSessionManager,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Dark theme: CSS token validation (smoke test)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestDarkThemeTokens:
|
|
"""Verify dark theme CSS tokens exist in tokens.css."""
|
|
|
|
def test_dark_theme_tokens_file_contains_dark_selector(self):
|
|
import pathlib
|
|
tokens_path = pathlib.Path(__file__).parent.parent.parent / (
|
|
"src/agentkit/server/frontend/src/styles/tokens.css"
|
|
)
|
|
if not tokens_path.exists():
|
|
pytest.skip("Frontend tokens.css not found")
|
|
content = tokens_path.read_text()
|
|
assert '[data-theme="dark"]' in content
|
|
assert '--bg-primary: #1a1a1a' in content
|
|
assert '--text-primary: #fbfbfa' in content
|
|
assert '--border-color: #3a3a3a' in content
|
|
|
|
def test_theme_store_exists(self):
|
|
import pathlib
|
|
store_path = pathlib.Path(__file__).parent.parent.parent / (
|
|
"src/agentkit/server/frontend/src/stores/theme.ts"
|
|
)
|
|
if not store_path.exists():
|
|
pytest.skip("Theme store not found")
|
|
content = store_path.read_text()
|
|
assert 'useThemeStore' in content
|
|
assert 'toggle' in content
|
|
assert 'resolvedMode' in content
|
|
assert 'localStorage' in content
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# @-mention API: mention-suggest endpoint
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestMentionSuggestAPI:
|
|
"""Test the /skills/mention-suggest endpoint logic."""
|
|
|
|
def _make_skill(self, name, description):
|
|
skill = MagicMock()
|
|
skill.name = name
|
|
skill.config.description = description
|
|
return skill
|
|
|
|
def test_mention_suggest_filters_by_name(self):
|
|
"""Verify the filtering logic used by the endpoint."""
|
|
skills = [
|
|
self._make_skill("geo_pipeline", "GEO pipeline skill"),
|
|
self._make_skill("code_reviewer", "Code review skill"),
|
|
self._make_skill("data_analyst", "Data analysis skill"),
|
|
]
|
|
query = "geo"
|
|
filtered = [
|
|
s for s in skills
|
|
if query in s.name.lower()
|
|
or (s.config.description and query in s.config.description.lower())
|
|
]
|
|
assert len(filtered) == 1
|
|
assert filtered[0].name == "geo_pipeline"
|
|
|
|
def test_mention_suggest_filters_by_description(self):
|
|
skills = [
|
|
self._make_skill("skill_a", "GEO pipeline skill"),
|
|
self._make_skill("skill_b", "Code review skill"),
|
|
]
|
|
query = "review"
|
|
filtered = [
|
|
s for s in skills
|
|
if query in s.name.lower()
|
|
or (s.config.description and query in s.config.description.lower())
|
|
]
|
|
assert len(filtered) == 1
|
|
assert filtered[0].name == "skill_b"
|
|
|
|
def test_mention_suggest_limits_to_8(self):
|
|
skills = [self._make_skill(f"skill_{i}", "") for i in range(20)]
|
|
result = skills[:8]
|
|
assert len(result) == 8
|
|
|
|
def test_mention_suggest_empty_query(self):
|
|
skills = [
|
|
self._make_skill("skill_a", "desc"),
|
|
self._make_skill("skill_b", "desc"),
|
|
]
|
|
query = ""
|
|
filtered = [
|
|
s for s in skills
|
|
if query in s.name.lower()
|
|
or (s.config.description and query in s.config.description.lower())
|
|
]
|
|
assert len(filtered) == 2
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# LocalComputerUseSession
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestLocalComputerUseSession:
|
|
"""Test LocalComputerUseSession (without actual pyautogui)."""
|
|
|
|
def test_inherits_from_base(self):
|
|
assert issubclass(LocalComputerUseSession, InMemoryComputerUseSession.__mro__[1])
|
|
|
|
def test_start_without_pyautogui_raises(self):
|
|
"""If pyautogui is not installed, start should raise RuntimeError."""
|
|
import asyncio
|
|
session = LocalComputerUseSession()
|
|
with patch.dict("sys.modules", {"pyautogui": None}):
|
|
with pytest.raises(RuntimeError, match="pyautogui"):
|
|
asyncio.run(session.start())
|
|
|
|
def test_session_manager_with_local_factory(self):
|
|
manager = ComputerUseSessionManager(session_factory=LocalComputerUseSession)
|
|
session = manager.get_or_create(session_id="test-local")
|
|
assert isinstance(session, LocalComputerUseSession)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Factory session_ttl propagation (regression test)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestFactorySessionTTL:
|
|
"""Ensure session_ttl is propagated through factories."""
|
|
|
|
def test_cascade_store_memory_with_custom_ttl(self):
|
|
store = create_cascade_state_store(backend="memory", session_ttl=7200)
|
|
assert isinstance(store, InMemoryCascadeStateStore)
|
|
assert store._session_ttl == 7200
|
|
|
|
def test_usage_store_memory_backend(self):
|
|
store = create_usage_store(backend="memory")
|
|
assert isinstance(store, InMemoryUsageStore)
|
|
|
|
def test_cascade_store_auto_backend(self):
|
|
store = create_cascade_state_store(backend="auto")
|
|
assert store._session_ttl == 86400 # default
|