236 lines
9.1 KiB
Python
236 lines
9.1 KiB
Python
"""
|
|
Conscience Validator Integration Tests — Issue #88
|
|
|
|
Validates SOUL.md enforcement across the codebase:
|
|
- @soul tag discovery
|
|
- Crisis detection apparatus
|
|
- Refusal apparatus for "What I Will Not Do"
|
|
- Honesty apparatus mapping
|
|
"""
|
|
|
|
import importlib.util
|
|
import os
|
|
import re
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from agent.input_sanitizer import CRISIS_PATTERNS
|
|
|
|
# Load ConscienceValidator without triggering tools/__init__.py heavy imports
|
|
_cv_spec = importlib.util.spec_from_file_location(
|
|
"conscience_validator",
|
|
str(Path(__file__).parent.parent / "tools" / "conscience_validator.py"),
|
|
)
|
|
_conscience_validator_mod = importlib.util.module_from_spec(_cv_spec)
|
|
_cv_spec.loader.exec_module(_conscience_validator_mod)
|
|
ConscienceValidator = _conscience_validator_mod.ConscienceValidator
|
|
|
|
|
|
class TestConscienceValidatorScan:
|
|
"""Tests for ConscienceValidator.scan() and regex fix."""
|
|
|
|
def test_scan_finds_soul_tags(self, tmp_path):
|
|
"""ConscienceValidator.scan() correctly finds @soul tags (regex fix)."""
|
|
test_file = tmp_path / "test_module.py"
|
|
test_file.write_text(
|
|
'# @soul:honesty.grounding Always verify before answering.\n'
|
|
'# @soul:crisis.safety_question Ask if the user is safe.\n'
|
|
)
|
|
validator = ConscienceValidator(str(tmp_path))
|
|
result = validator.scan()
|
|
|
|
assert "honesty.grounding" in result
|
|
assert "crisis.safety_question" in result
|
|
assert result["honesty.grounding"][0]["description"] == "Always verify before answering."
|
|
assert result["crisis.safety_question"][0]["description"] == "Ask if the user is safe."
|
|
|
|
def test_scan_honesty_tags_in_conscience_mapping(self):
|
|
"""conscience_mapping.py contains expected honesty @soul tags."""
|
|
root = Path(__file__).parent.parent
|
|
validator = ConscienceValidator(str(root))
|
|
result = validator.scan()
|
|
|
|
expected_tags = [
|
|
"honesty.grounding",
|
|
"honesty.source_distinction",
|
|
"honesty.audit_trail",
|
|
"honesty.refusal_over_fabrication",
|
|
"service",
|
|
"crisis.safety_question",
|
|
"crisis.lifeline",
|
|
"sovereignty",
|
|
]
|
|
for tag in expected_tags:
|
|
assert tag in result, f"Expected @soul tag '{tag}' not found in codebase"
|
|
assert any(
|
|
"agent/conscience_mapping.py" in entry["file"]
|
|
for entry in result[tag]
|
|
), f"Tag '{tag}' should originate from conscience_mapping.py"
|
|
|
|
def test_scan_skips_node_modules(self, tmp_path):
|
|
"""scan() ignores node_modules directories."""
|
|
node_modules = tmp_path / "node_modules"
|
|
node_modules.mkdir()
|
|
bad_file = node_modules / "bad.py"
|
|
bad_file.write_text("# @soul:test.skip This should be ignored\n")
|
|
|
|
validator = ConscienceValidator(str(tmp_path))
|
|
result = validator.scan()
|
|
assert "test.skip" not in result
|
|
|
|
def test_scan_empty_directory(self, tmp_path):
|
|
"""scan() returns empty map for empty directory."""
|
|
validator = ConscienceValidator(str(tmp_path))
|
|
result = validator.scan()
|
|
assert result == {}
|
|
|
|
def test_scan_bad_encoding_file(self, tmp_path):
|
|
"""scan() gracefully skips files with bad encoding."""
|
|
bad_file = tmp_path / "garbage.py"
|
|
bad_file.write_bytes(b"\xff\xfe# @soul:test.bad \xc0\x80\n")
|
|
|
|
validator = ConscienceValidator(str(tmp_path))
|
|
result = validator.scan()
|
|
assert "test.bad" not in result
|
|
|
|
|
|
class TestCrisisApparatus:
|
|
"""Tests validating crisis response ('When a Man Is Dying') infrastructure."""
|
|
|
|
def test_input_sanitizer_has_crisis_patterns(self):
|
|
"""input_sanitizer.py contains CRISIS_PATTERNS matching SOUL.md."""
|
|
assert len(CRISIS_PATTERNS) > 0
|
|
combined = " ".join(CRISIS_PATTERNS)
|
|
assert "suicid" in combined.lower()
|
|
assert any(
|
|
term in combined.lower()
|
|
for term in ["kill", "self-harm", "self harm", "end", "life"]
|
|
)
|
|
|
|
def test_shield_detector_has_crisis_and_988(self):
|
|
"""shield/detector.py contains crisis detection and 988 references."""
|
|
root = Path(__file__).parent.parent
|
|
detector_path = root / "tools" / "shield" / "detector.py"
|
|
content = detector_path.read_text(encoding="utf-8")
|
|
|
|
assert "988" in content
|
|
assert "CRISIS_SYSTEM_PROMPT" in content
|
|
assert "CRISIS_DETECTED" in content
|
|
assert "suicidal" in content.lower() or "suicide" in content.lower()
|
|
|
|
def test_ultraplinian_router_has_crisis_routing(self):
|
|
"""ultraplinian_router.py routes crises with CRISIS_SYSTEM_PROMPT."""
|
|
root = Path(__file__).parent.parent
|
|
router_path = root / "agent" / "ultraplinian_router.py"
|
|
content = router_path.read_text(encoding="utf-8")
|
|
|
|
assert "988" in content
|
|
assert "CRISIS_SYSTEM_PROMPT" in content
|
|
|
|
def test_validate_crisis_apparatus(self):
|
|
"""validate_crisis_apparatus() reports crisis infrastructure as present."""
|
|
root = Path(__file__).parent.parent
|
|
validator = ConscienceValidator(str(root))
|
|
report = validator.validate_crisis_apparatus()
|
|
|
|
assert report["checks"]["input_sanitizer_crisis_patterns"] is True
|
|
assert report["checks"]["shield_detector_988"] is True
|
|
assert report["checks"]["shield_detector_crisis_prompt"] is True
|
|
assert report["checks"]["router_988"] is True
|
|
assert "input_sanitizer crisis detection" in report["present"]
|
|
assert "shield/detector crisis apparatus" in report["present"]
|
|
assert "ultraplinian_router crisis routing" in report["present"]
|
|
assert report["missing"] == []
|
|
|
|
|
|
class TestRefusalApparatus:
|
|
"""Tests validating 'What I Will Not Do' safety infrastructure."""
|
|
|
|
def test_validate_refusal_apparatus_runs(self):
|
|
"""validate_refusal_apparatus() executes and returns structured results."""
|
|
root = Path(__file__).parent.parent
|
|
validator = ConscienceValidator(str(root))
|
|
report = validator.validate_refusal_apparatus()
|
|
|
|
expected_categories = [
|
|
"weapons",
|
|
"child_exploitation",
|
|
"coercion_enslavement",
|
|
"deception",
|
|
"pretend_human",
|
|
]
|
|
for category in expected_categories:
|
|
assert category in report["checks"], f"Missing check for {category}"
|
|
|
|
# deception_hide pattern exists in prompt_builder.py
|
|
assert "deception" in report["present"]
|
|
|
|
def test_prompt_builder_has_deception_guard(self):
|
|
"""agent/prompt_builder.py guards against deception instructions."""
|
|
root = Path(__file__).parent.parent
|
|
pb_path = root / "agent" / "prompt_builder.py"
|
|
content = pb_path.read_text(encoding="utf-8")
|
|
assert "deception_hide" in content or "do not tell the user" in content.lower()
|
|
|
|
|
|
class TestHonestyApparatus:
|
|
"""Tests validating 'What Honesty Requires' infrastructure."""
|
|
|
|
def test_validate_honesty_apparatus(self):
|
|
"""validate_honesty_apparatus() reports all expected honesty tags."""
|
|
root = Path(__file__).parent.parent
|
|
validator = ConscienceValidator(str(root))
|
|
report = validator.validate_honesty_apparatus()
|
|
|
|
expected_tags = [
|
|
"honesty.grounding",
|
|
"honesty.source_distinction",
|
|
"honesty.audit_trail",
|
|
"honesty.refusal_over_fabrication",
|
|
"service",
|
|
"crisis.safety_question",
|
|
"crisis.lifeline",
|
|
"sovereignty",
|
|
]
|
|
for tag in expected_tags:
|
|
assert report["checks"][tag] is True, f"Missing honesty tag: {tag}"
|
|
assert tag in report["present"]
|
|
assert report["missing"] == []
|
|
|
|
|
|
class TestReportGeneration:
|
|
"""Tests for report generation and edge cases."""
|
|
|
|
def test_generate_report_is_non_empty(self):
|
|
"""Validate the conscience validator generates a non-empty report."""
|
|
root = Path(__file__).parent.parent
|
|
validator = ConscienceValidator(str(root))
|
|
report = validator.generate_report()
|
|
assert report.startswith("# Sovereign Conscience Report")
|
|
assert "honesty > grounding" in report.lower()
|
|
|
|
def test_full_validation_report_structure(self):
|
|
"""full_validation_report() returns a unified structured report."""
|
|
root = Path(__file__).parent.parent
|
|
validator = ConscienceValidator(str(root))
|
|
report = validator.full_validation_report()
|
|
|
|
assert "crisis" in report
|
|
assert "refusal" in report
|
|
assert "honesty" in report
|
|
assert "soul_tags" in report
|
|
assert isinstance(report["crisis"]["present"], list)
|
|
assert isinstance(report["crisis"]["missing"], list)
|
|
assert isinstance(report["honesty"]["checks"], dict)
|
|
|
|
def test_validator_uses_missing_directory_gracefully(self):
|
|
"""Validator handles a missing root directory without crashing."""
|
|
validator = ConscienceValidator("/nonexistent/path/that/does/not/exist")
|
|
result = validator.scan()
|
|
assert result == {}
|
|
|
|
report = validator.validate_honesty_apparatus()
|
|
assert "conscience_mapping.py not found" in report["missing"]
|