Files
turnstone/tests/test_tools_schema.py
T
Patrick Buckley 9be155b97a Quality overhaul: code tooling, CI/CD, architecture diagrams, UI rede… (#1)
* Quality overhaul: code tooling, CI/CD, architecture diagrams, UI redesign, and legacy cleanup

- Add ruff (lint+format) and mypy (strict) with zero errors across 37 source files
- Add GitHub Actions CI (lint, typecheck, test matrix 3.11/3.12/3.13) and PyPI publish workflow
- Create 12 PlantUML architecture diagrams with PNG renders covering all subsystems
- Refresh README and docs with badges, diagram links, and current descriptions
- Refactor test_server_live.py with mock streaming helpers for deterministic CI testing
- Update dependencies to current versions (openai>=2.24, httpx>=0.28, redis>=7.2)

Console dashboard:
- Move state indicators from top cards to fixed bottom status bar with cluster metrics
- Replace flat 50-node list with hostname-prefix grouped nodes (expand/collapse, up to 1000)
- Apply "Instrument Panel" visual redesign: IBM Plex Mono + Outfit fonts, warm amber accent,
  LED glow state indicators, deep charcoal surfaces, WCAG AA contrast compliance
- Add render cache, stale indicator, active filter highlight, loading states

Server web UI:
- Apply matching Instrument Panel aesthetic for visual consistency with console
- Fix branding (pcode → turnstone), extract inline styles to CSS classes
- Rename pcode localStorage keys and history state to turnstone

Legacy cleanup:
- Remove persona-model-specific --persona flag and /persona slash command
- Remove model_identity from chat_template_kwargs (vLLM-specific mechanism)
- Refactor plan agent to use standard developer message instead of model_identity
- Remove dead code (unused date/has_tools variables, noqa suppressions)

* Fix CI typecheck: add mypy overrides for optional sympy/numpy imports

The math sandbox optionally imports sympy and numpy at runtime (try/except
ImportError). In CI these packages are not installed, so mypy raises
import-not-found rather than import-untyped. Add mypy overrides to
ignore missing imports for these optional dependencies.

* Fix Copilot review findings: ARIA role, status bar cache, and pulse opacity

- Change #node-table from role="tree" to role="list" and group elements
  from role="treeitem" to role="listitem" (proper ARIA semantics)
- Include currentView and currentFilter.state in renderStatusBar cache key
  so active pill highlight updates when switching views
- Align pulse animation to 0.35 opacity (already applied in CSS)
2026-03-02 16:55:12 -08:00

118 lines
4.0 KiB
Python

"""Tests for turnstone.core.tools — JSON auto-loading and schema validation."""
from turnstone.core.tools import (
_META,
AGENT_AUTO_TOOLS,
AGENT_TOOLS,
PRIMARY_KEY_MAP,
TASK_AGENT_TOOLS,
TASK_AUTO_TOOLS,
TOOLS,
)
class TestToolsSchema:
def test_all_tools_have_function_type(self):
for tool in TOOLS:
assert tool["type"] == "function", f"Tool missing type='function': {tool}"
def test_all_tools_have_name(self):
for tool in TOOLS:
assert "name" in tool["function"], f"Tool missing name: {tool}"
assert isinstance(tool["function"]["name"], str)
def test_all_tools_have_description(self):
for tool in TOOLS:
assert "description" in tool["function"], f"Tool missing description: {tool}"
assert len(tool["function"]["description"]) > 0
def test_all_tools_have_parameters(self):
for tool in TOOLS:
params = tool["function"]["parameters"]
assert params["type"] == "object"
assert "properties" in params
def test_required_fields_exist_in_properties(self):
for tool in TOOLS:
func = tool["function"]
params = func["parameters"]
required = params.get("required", [])
properties = params["properties"]
for field in required:
assert field in properties, (
f"Tool '{func['name']}': required field '{field}' not in properties"
)
def test_tool_names_unique(self):
names = [t["function"]["name"] for t in TOOLS]
assert len(names) == len(set(names)), f"Duplicate tool names: {names}"
def test_agent_tools_subset(self):
tool_names = {t["function"]["name"] for t in TOOLS}
agent_names = {t["function"]["name"] for t in AGENT_TOOLS}
assert agent_names.issubset(tool_names), (
f"AGENT_TOOLS has names not in TOOLS: {agent_names - tool_names}"
)
def test_task_agent_tools_subset(self):
tool_names = {t["function"]["name"] for t in TOOLS}
task_names = {t["function"]["name"] for t in TASK_AGENT_TOOLS}
assert task_names.issubset(tool_names), (
f"TASK_AGENT_TOOLS has names not in TOOLS: {task_names - tool_names}"
)
def test_agent_tools_not_empty(self):
assert len(AGENT_TOOLS) > 0
def test_task_agent_tools_not_empty(self):
assert len(TASK_AGENT_TOOLS) > 0
class TestToolsMetadata:
"""Validate the metadata extracted from JSON files."""
def test_tool_count(self):
assert len(TOOLS) == 14
def test_agent_tools_count(self):
assert len(AGENT_TOOLS) == 6
def test_task_agent_tools_count(self):
assert len(TASK_AGENT_TOOLS) == 9
def test_auto_approve_sets_match(self):
expected = {"read_file", "search", "math", "man", "web_fetch", "web_search"}
assert expected == AGENT_AUTO_TOOLS
assert expected == TASK_AUTO_TOOLS
def test_primary_key_map(self):
expected = {
"bash": "command",
"math": "code",
"read_file": "path",
"search": "query",
"write_file": "content",
"edit_file": "old_string",
"man": "page",
"web_fetch": "url",
"web_search": "query",
"task": "prompt",
"plan": "prompt",
"remember": "key",
"recall": "query",
"forget": "key",
}
assert expected == PRIMARY_KEY_MAP
def test_no_metadata_in_function_dicts(self):
"""Ensure turnstone metadata keys are stripped from the OpenAI schema."""
meta_keys = {"agent", "task_agent", "auto_approve", "primary_key"}
for tool in TOOLS:
func = tool["function"]
leaked = meta_keys & set(func)
assert not leaked, f"Tool '{func['name']}' leaks metadata into function dict: {leaked}"
def test_meta_has_all_tools(self):
tool_names = {t["function"]["name"] for t in TOOLS}
assert set(_META.keys()) == tool_names