mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
9be155b97a
* 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)
100 lines
3.1 KiB
Python
100 lines
3.1 KiB
Python
"""Tests for turnstone.core.sandbox — validate_math_code and auto_print_wrap."""
|
|
|
|
from turnstone.core.sandbox import auto_print_wrap, validate_math_code
|
|
|
|
|
|
class TestValidateMathCode:
|
|
def test_safe_code_no_errors(self):
|
|
assert validate_math_code("x = 1 + 2\nprint(x)") == []
|
|
|
|
def test_safe_math_import(self):
|
|
assert validate_math_code("import math\nprint(math.pi)") == []
|
|
|
|
def test_blocked_import_os(self):
|
|
errors = validate_math_code("import os")
|
|
assert len(errors) == 1
|
|
assert "os" in errors[0]
|
|
|
|
def test_blocked_import_sys(self):
|
|
errors = validate_math_code("import sys")
|
|
assert len(errors) == 1
|
|
assert "sys" in errors[0]
|
|
|
|
def test_blocked_import_subprocess(self):
|
|
errors = validate_math_code("import subprocess")
|
|
assert len(errors) == 1
|
|
assert "subprocess" in errors[0]
|
|
|
|
def test_blocked_from_import(self):
|
|
errors = validate_math_code("from os.path import join")
|
|
assert len(errors) == 1
|
|
assert "os" in errors[0]
|
|
|
|
def test_blocked_builtin_exec(self):
|
|
errors = validate_math_code("exec('print(1)')")
|
|
assert len(errors) == 1
|
|
assert "exec" in errors[0]
|
|
|
|
def test_blocked_builtin_eval(self):
|
|
errors = validate_math_code("eval('1+1')")
|
|
assert len(errors) == 1
|
|
assert "eval" in errors[0]
|
|
|
|
def test_blocked_builtin_open(self):
|
|
errors = validate_math_code("open('file.txt')")
|
|
assert len(errors) == 1
|
|
assert "open" in errors[0]
|
|
|
|
def test_blocked_dunder_access(self):
|
|
errors = validate_math_code("x.__dict__")
|
|
assert len(errors) == 1
|
|
assert "__dict__" in errors[0]
|
|
|
|
def test_allowed_dunder_name(self):
|
|
# __name__, __doc__, __class__ are allowed
|
|
assert validate_math_code("print(int.__name__)") == []
|
|
|
|
def test_syntax_error_caught(self):
|
|
errors = validate_math_code("def f(\n")
|
|
assert len(errors) == 1
|
|
assert "Syntax error" in errors[0]
|
|
|
|
def test_multiple_violations(self):
|
|
code = "import os\nimport sys\nexec('x')"
|
|
errors = validate_math_code(code)
|
|
assert len(errors) == 3
|
|
|
|
|
|
class TestAutoPrintWrap:
|
|
def test_bare_expression_wrapped(self):
|
|
result = auto_print_wrap("1 + 2")
|
|
assert "print(" in result
|
|
assert "1 + 2" in result
|
|
|
|
def test_assignment_not_wrapped(self):
|
|
code = "x = 1 + 2"
|
|
assert auto_print_wrap(code) == code
|
|
|
|
def test_code_with_print_not_wrapped(self):
|
|
code = "x = 1\nprint(x)"
|
|
assert auto_print_wrap(code) == code
|
|
|
|
def test_code_with_result_assignment_not_wrapped(self):
|
|
code = "result = 42"
|
|
assert auto_print_wrap(code) == code
|
|
|
|
def test_multiline_with_bare_expression_last(self):
|
|
code = "x = 2\ny = 3\nx + y"
|
|
result = auto_print_wrap(code)
|
|
assert "print(" in result
|
|
# The assignments should still be there
|
|
assert "x = 2" in result
|
|
assert "y = 3" in result
|
|
|
|
def test_empty_code(self):
|
|
assert auto_print_wrap("") == ""
|
|
|
|
def test_syntax_error_returns_original(self):
|
|
code = "def f(\n"
|
|
assert auto_print_wrap(code) == code
|