test: comprehensive test suite

Unit tests:
- test_security_boundaries.py: vault block, vault lock sentinel
- test_security_injection.py: all 4 injection categories, case-insensitive
- test_vault_rw.py: roundtrip, file permissions (chmod 400), no key in config
- test_config.py: schema roundtrip, no api_key field, chmod 600 on config.yaml
- test_memory_reader.py: list, read, sandboxing, context loading
- test_memory_writer.py: write, append, index update, traversal blocked, chmod 600
- test_providers.py: required fields, unique IDs, litellm prefix format
- test_renderer.py: key redaction for sk-ant-, sk-, AIza patterns

Security tests:
- test_vault_ai_isolation.py: 7 traversal patterns blocked via memory read/write
- test_path_traversal.py: 20+ traversal patterns — all rejected for read and write
- test_prompt_injection.py: 21-item corpus + 5 clean texts (no false positives)

Integration tests:
- test_lmstudio.py: live call to localhost:1234, streaming, full stack session,
  injection scan on real output (skips if LM Studio not running)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-17 12:55:06 +02:00
parent e792c5e0c9
commit 251e509ee0
13 changed files with 746 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
import pytest
from pyra.config.schema import PyraConfig, ProviderConfig
def test_config_saves_no_api_key(tmp_pyra_home):
from pyra.config.manager import save_config, load_config
cfg = PyraConfig(ai=ProviderConfig(provider_id="anthropic", model="claude-sonnet-4-6"))
save_config(cfg)
config_text = (tmp_pyra_home / "config.yaml").read_text()
assert "sk-" not in config_text
assert "api_key" not in config_text.lower()
def test_config_round_trip(tmp_pyra_home):
from pyra.config.manager import save_config, load_config
cfg = PyraConfig(
ai=ProviderConfig(
provider_id="lmstudio",
model="gemma-4-e4b",
base_url="http://localhost:1234/v1",
)
)
save_config(cfg)
loaded = load_config()
assert loaded.ai.provider_id == "lmstudio"
assert loaded.ai.model == "gemma-4-e4b"
assert loaded.ai.base_url == "http://localhost:1234/v1"
def test_config_file_permissions(tmp_pyra_home):
import os
from pyra.config.manager import save_config
cfg = PyraConfig(ai=ProviderConfig(provider_id="ollama", model="llama3"))
save_config(cfg)
config_file = tmp_pyra_home / "config.yaml"
if os.name != "nt":
mode = oct(config_file.stat().st_mode)[-3:]
assert mode == "600", f"Expected 600, got {mode}"
def test_load_config_missing_raises(tmp_pyra_home):
from pyra.config.manager import load_config
with pytest.raises(FileNotFoundError):
load_config()