spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / plugins / hookify / tests / conftest.py
208 lines · 5.4 KBRaw
1"""Pytest fixtures for hookify integration tests."""
2
3import os
4import sys
5import json
6import tempfile
7import shutil
8from pathlib import Path
9from typing import Generator, Dict, Any, List
10
11import pytest
12
13# Add parent directories to path for imports
14PLUGIN_ROOT = Path(__file__).parent.parent
15PLUGINS_DIR = PLUGIN_ROOT.parent
16sys.path.insert(0, str(PLUGINS_DIR))
17sys.path.insert(0, str(PLUGIN_ROOT))
18
19from hookify.core.config_loader import Rule, Condition, load_rules, extract_frontmatter
20from hookify.core.rule_engine import RuleEngine
21
22
23@pytest.fixture
24def rule_engine() -> RuleEngine:
25 """Create a RuleEngine instance."""
26 return RuleEngine()
27
28
29@pytest.fixture
30def temp_project_dir() -> Generator[Path, None, None]:
31 """Create a temporary project directory with .claude folder.
32
33 This fixture creates a clean temp directory and changes to it,
34 then restores the original directory after the test.
35 """
36 original_dir = os.getcwd()
37 temp_dir = tempfile.mkdtemp(prefix="hookify_test_")
38
39 # Create .claude directory for rule files
40 claude_dir = Path(temp_dir) / ".claude"
41 claude_dir.mkdir()
42
43 os.chdir(temp_dir)
44
45 yield Path(temp_dir)
46
47 os.chdir(original_dir)
48 shutil.rmtree(temp_dir)
49
50
51@pytest.fixture
52def sample_rule_file(temp_project_dir: Path) -> Path:
53 """Create a sample rule file for testing."""
54 rule_content = """---
55name: block-rm-rf
56enabled: true
57event: bash
58action: block
59conditions:
60 - field: command
61 operator: regex_match
62 pattern: rm\\s+-rf
63---
64
65**Dangerous command blocked!**
66
67The `rm -rf` command can permanently delete files. Please use safer alternatives.
68"""
69 rule_file = temp_project_dir / ".claude" / "hookify.dangerous-commands.local.md"
70 rule_file.write_text(rule_content)
71 return rule_file
72
73
74@pytest.fixture
75def create_rule_file(temp_project_dir: Path):
76 """Factory fixture to create rule files with custom content."""
77 def _create(name: str, content: str) -> Path:
78 rule_file = temp_project_dir / ".claude" / f"hookify.{name}.local.md"
79 rule_file.write_text(content)
80 return rule_file
81 return _create
82
83
84@pytest.fixture
85def sample_bash_input() -> Dict[str, Any]:
86 """Sample PreToolUse input for Bash tool."""
87 return {
88 "session_id": "test-session-123",
89 "hook_event_name": "PreToolUse",
90 "tool_name": "Bash",
91 "tool_input": {
92 "command": "ls -la"
93 },
94 "cwd": "/test/project"
95 }
96
97
98@pytest.fixture
99def sample_write_input() -> Dict[str, Any]:
100 """Sample PreToolUse input for Write tool."""
101 return {
102 "session_id": "test-session-123",
103 "hook_event_name": "PreToolUse",
104 "tool_name": "Write",
105 "tool_input": {
106 "file_path": "/test/project/src/main.py",
107 "content": "print('hello world')"
108 },
109 "cwd": "/test/project"
110 }
111
112
113@pytest.fixture
114def sample_edit_input() -> Dict[str, Any]:
115 """Sample PreToolUse input for Edit tool."""
116 return {
117 "session_id": "test-session-123",
118 "hook_event_name": "PreToolUse",
119 "tool_name": "Edit",
120 "tool_input": {
121 "file_path": "/test/project/src/main.py",
122 "old_string": "hello",
123 "new_string": "goodbye"
124 },
125 "cwd": "/test/project"
126 }
127
128
129@pytest.fixture
130def sample_multiedit_input() -> Dict[str, Any]:
131 """Sample PreToolUse input for MultiEdit tool."""
132 return {
133 "session_id": "test-session-123",
134 "hook_event_name": "PreToolUse",
135 "tool_name": "MultiEdit",
136 "tool_input": {
137 "file_path": "/test/project/src/main.py",
138 "edits": [
139 {"old_string": "foo", "new_string": "bar"},
140 {"old_string": "baz", "new_string": "qux"}
141 ]
142 },
143 "cwd": "/test/project"
144 }
145
146
147@pytest.fixture
148def sample_stop_input(temp_project_dir: Path) -> Dict[str, Any]:
149 """Sample Stop event input with transcript file."""
150 # Create a transcript file
151 transcript_file = temp_project_dir / "transcript.txt"
152 transcript_file.write_text("""
153User: Please implement the feature
154Assistant: I'll implement that feature now.
155[Uses Write tool to create file]
156User: Great, now run the tests
157Assistant: Running tests...
158[Uses Bash tool: npm test]
159All tests passed!
160""")
161
162 return {
163 "session_id": "test-session-123",
164 "hook_event_name": "Stop",
165 "reason": "Task completed",
166 "transcript_path": str(transcript_file),
167 "cwd": str(temp_project_dir)
168 }
169
170
171@pytest.fixture
172def sample_userprompt_input() -> Dict[str, Any]:
173 """Sample UserPromptSubmit event input."""
174 return {
175 "session_id": "test-session-123",
176 "hook_event_name": "UserPromptSubmit",
177 "user_prompt": "Please delete all files in the directory",
178 "cwd": "/test/project"
179 }
180
181
182def make_rule(
183 name: str,
184 event: str,
185 conditions: List[Dict[str, str]],
186 action: str = "warn",
187 message: str = "Test message",
188 enabled: bool = True,
189 tool_matcher: str = None
190) -> Rule:
191 """Helper function to create Rule objects for testing."""
192 cond_objects = [
193 Condition(
194 field=c.get("field", ""),
195 operator=c.get("operator", "regex_match"),
196 pattern=c.get("pattern", "")
197 )
198 for c in conditions
199 ]
200 return Rule(
201 name=name,
202 enabled=enabled,
203 event=event,
204 conditions=cond_objects,
205 action=action,
206 message=message,
207 tool_matcher=tool_matcher
208 )