Files
claude-code/plugins/hookify/tests/conftest.py

209 lines
5.4 KiB
Python
Raw Normal View History

"""Pytest fixtures for hookify integration tests."""
import os
import sys
import json
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any, List
import pytest
# Add parent directories to path for imports
PLUGIN_ROOT = Path(__file__).parent.parent
PLUGINS_DIR = PLUGIN_ROOT.parent
sys.path.insert(0, str(PLUGINS_DIR))
sys.path.insert(0, str(PLUGIN_ROOT))
from hookify.core.config_loader import Rule, Condition, load_rules, extract_frontmatter
from hookify.core.rule_engine import RuleEngine
@pytest.fixture
def rule_engine() -> RuleEngine:
"""Create a RuleEngine instance."""
return RuleEngine()
@pytest.fixture
def temp_project_dir() -> Generator[Path, None, None]:
"""Create a temporary project directory with .claude folder.
This fixture creates a clean temp directory and changes to it,
then restores the original directory after the test.
"""
original_dir = os.getcwd()
temp_dir = tempfile.mkdtemp(prefix="hookify_test_")
# Create .claude directory for rule files
claude_dir = Path(temp_dir) / ".claude"
claude_dir.mkdir()
os.chdir(temp_dir)
yield Path(temp_dir)
os.chdir(original_dir)
shutil.rmtree(temp_dir)
@pytest.fixture
def sample_rule_file(temp_project_dir: Path) -> Path:
"""Create a sample rule file for testing."""
rule_content = """---
name: block-rm-rf
enabled: true
event: bash
action: block
conditions:
- field: command
operator: regex_match
pattern: rm\\s+-rf
---
**Dangerous command blocked!**
The `rm -rf` command can permanently delete files. Please use safer alternatives.
"""
rule_file = temp_project_dir / ".claude" / "hookify.dangerous-commands.local.md"
rule_file.write_text(rule_content)
return rule_file
@pytest.fixture
def create_rule_file(temp_project_dir: Path):
"""Factory fixture to create rule files with custom content."""
def _create(name: str, content: str) -> Path:
rule_file = temp_project_dir / ".claude" / f"hookify.{name}.local.md"
rule_file.write_text(content)
return rule_file
return _create
@pytest.fixture
def sample_bash_input() -> Dict[str, Any]:
"""Sample PreToolUse input for Bash tool."""
return {
"session_id": "test-session-123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "ls -la"
},
"cwd": "/test/project"
}
@pytest.fixture
def sample_write_input() -> Dict[str, Any]:
"""Sample PreToolUse input for Write tool."""
return {
"session_id": "test-session-123",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/test/project/src/main.py",
"content": "print('hello world')"
},
"cwd": "/test/project"
}
@pytest.fixture
def sample_edit_input() -> Dict[str, Any]:
"""Sample PreToolUse input for Edit tool."""
return {
"session_id": "test-session-123",
"hook_event_name": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "/test/project/src/main.py",
"old_string": "hello",
"new_string": "goodbye"
},
"cwd": "/test/project"
}
@pytest.fixture
def sample_multiedit_input() -> Dict[str, Any]:
"""Sample PreToolUse input for MultiEdit tool."""
return {
"session_id": "test-session-123",
"hook_event_name": "PreToolUse",
"tool_name": "MultiEdit",
"tool_input": {
"file_path": "/test/project/src/main.py",
"edits": [
{"old_string": "foo", "new_string": "bar"},
{"old_string": "baz", "new_string": "qux"}
]
},
"cwd": "/test/project"
}
@pytest.fixture
def sample_stop_input(temp_project_dir: Path) -> Dict[str, Any]:
"""Sample Stop event input with transcript file."""
# Create a transcript file
transcript_file = temp_project_dir / "transcript.txt"
transcript_file.write_text("""
User: Please implement the feature
Assistant: I'll implement that feature now.
[Uses Write tool to create file]
User: Great, now run the tests
Assistant: Running tests...
[Uses Bash tool: npm test]
All tests passed!
""")
return {
"session_id": "test-session-123",
"hook_event_name": "Stop",
"reason": "Task completed",
"transcript_path": str(transcript_file),
"cwd": str(temp_project_dir)
}
@pytest.fixture
def sample_userprompt_input() -> Dict[str, Any]:
"""Sample UserPromptSubmit event input."""
return {
"session_id": "test-session-123",
"hook_event_name": "UserPromptSubmit",
"user_prompt": "Please delete all files in the directory",
"cwd": "/test/project"
}
def make_rule(
name: str,
event: str,
conditions: List[Dict[str, str]],
action: str = "warn",
message: str = "Test message",
enabled: bool = True,
tool_matcher: str = None
) -> Rule:
"""Helper function to create Rule objects for testing."""
cond_objects = [
Condition(
field=c.get("field", ""),
operator=c.get("operator", "regex_match"),
pattern=c.get("pattern", "")
)
for c in conditions
]
return Rule(
name=name,
enabled=enabled,
event=event,
conditions=cond_objects,
action=action,
message=message,
tool_matcher=tool_matcher
)