test: add Cortex test suite (77 tests, no LLM calls)
Tests cover: - Smoke: /health, /auth/status, /distill/status (test_health.py) - Persona validation: path traversal, bad names, list_personas (test_persona.py) - Chat API: persona routing, session persistence, error handling (test_api_chat.py) - Files API: ALLOWED set enforcement, read/write, missing files (test_api_files.py) - Webhooks: NC Talk HMAC accept/reject, Google Chat JWT (test_webhooks.py) - Tools: scratch read/write/append/clear, tasks CRUD, cron parser + tools (test_tools.py) - Security: path traversal, replay attack, known gaps documented (test_security.py) All LLM calls mocked — suite runs in ~1.4s. Run: cd cortex && .venv/bin/pytest Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
94
cortex/tests/test_persona.py
Normal file
94
cortex/tests/test_persona.py
Normal file
@@ -0,0 +1,94 @@
|
||||
"""
|
||||
Unit tests for persona.py — validation, routing, path traversal.
|
||||
No HTTP involved.
|
||||
"""
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
def _make_temp_personas(tmp_path: Path) -> Path:
|
||||
root = tmp_path / "personas"
|
||||
for name in ("alice", "bob"):
|
||||
p = root / name
|
||||
p.mkdir(parents=True)
|
||||
(p / "IDENTITY.md").write_text(f"# {name}")
|
||||
# A directory WITHOUT IDENTITY.md — should not appear in list_personas()
|
||||
(root / "incomplete").mkdir()
|
||||
return root
|
||||
|
||||
|
||||
def test_validate_good(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
assert persona.validate("alice") == "alice"
|
||||
assert persona.validate("bob") == "bob"
|
||||
|
||||
|
||||
def test_validate_unknown(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
with pytest.raises(ValueError, match="Unknown persona"):
|
||||
persona.validate("charlie")
|
||||
|
||||
|
||||
def test_validate_path_traversal(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
with pytest.raises(ValueError, match="Invalid persona name"):
|
||||
persona.validate("../../etc/passwd")
|
||||
with pytest.raises(ValueError, match="Invalid persona name"):
|
||||
persona.validate("../alice")
|
||||
with pytest.raises(ValueError, match="Invalid persona name"):
|
||||
persona.validate("alice/../../etc")
|
||||
|
||||
|
||||
def test_validate_special_chars(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
for bad in ("alice bob", "alice;bob", "alice\x00bob", "A" * 33, ""):
|
||||
with pytest.raises(ValueError):
|
||||
persona.validate(bad)
|
||||
|
||||
|
||||
def test_validate_allows_hyphen_underscore(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
# Create a persona with hyphen and underscore in name
|
||||
p = root / "my_ai-agent"
|
||||
p.mkdir(parents=True)
|
||||
(p / "IDENTITY.md").write_text("# My Agent")
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
assert persona.validate("my_ai-agent") == "my_ai-agent"
|
||||
|
||||
|
||||
def test_list_personas(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
names = persona.list_personas()
|
||||
assert "alice" in names
|
||||
assert "bob" in names
|
||||
assert "incomplete" not in names # no IDENTITY.md
|
||||
|
||||
|
||||
def test_persona_path_uses_contextvar(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
persona.set_persona("alice")
|
||||
assert persona.persona_path() == root / "alice"
|
||||
persona.set_persona("bob")
|
||||
assert persona.persona_path() == root / "bob"
|
||||
|
||||
|
||||
def test_persona_path_explicit_name(tmp_path):
|
||||
root = _make_temp_personas(tmp_path)
|
||||
import config, persona
|
||||
with patch.object(config.settings, "personas_dir", root):
|
||||
persona.set_persona("alice")
|
||||
assert persona.persona_path("bob") == root / "bob"
|
||||
Reference in New Issue
Block a user