Skip to content

Commit 616ab4a

Browse files
committed
Add tests
1 parent 73312be commit 616ab4a

File tree

6 files changed

+214
-1
lines changed

6 files changed

+214
-1
lines changed

pyproject.toml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,17 @@ dependencies = [
1212
"click>=8.1.8",
1313
"rich>=13.9.4",
1414
]
15+
1516
[project.scripts]
16-
wiz = "wiz:cli"
17+
wiz = "wiz:cli"
18+
19+
[project.optional-dependencies]
20+
dev = [
21+
"pytest>=7.3.1",
22+
"pytest-mock>=3.10.0",
23+
]
24+
25+
[tool.pytest]
26+
testpaths = ["tests"]
27+
python_files = "test_*.py"
28+
python_functions = "test_*"

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Package marker for tests

tests/conftest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Common pytest fixtures go here
2+
import pytest
3+
from click.testing import CliRunner
4+
5+
@pytest.fixture
6+
def runner():
7+
"""Create a CLI runner for testing commands."""
8+
return CliRunner()

tests/test_cli.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import pytest
2+
from unittest.mock import patch, MagicMock, mock_open
3+
from click.testing import CliRunner
4+
from wiz import cli, files, apply
5+
6+
def test_files_command(runner):
7+
"""Test the 'files' command."""
8+
with patch('wiz.get_file_table') as mock_get_table:
9+
# Mock the return value of get_file_table
10+
mock_table = MagicMock()
11+
mock_get_table.return_value = (mock_table, ['file1.py', 'file2.py'])
12+
13+
result = runner.invoke(files)
14+
15+
assert result.exit_code == 0
16+
# The function was called once
17+
mock_get_table.assert_called_once()
18+
19+
def test_apply_command_stdin(runner):
20+
"""Test the 'apply' command with stdin input."""
21+
stdin_content = (
22+
"[FILE test.py]\n"
23+
"print('hello world')\n"
24+
"[/FILE]\n"
25+
)
26+
27+
with patch('wiz.process_file_blocks') as mock_process:
28+
# Configure the mock to return a single file result
29+
mock_process.return_value = [('test.py', "print('hello world')", 1)]
30+
31+
# Mock file operations
32+
with patch('builtins.open', mock_open()):
33+
with patch('os.makedirs'):
34+
# Invoke the apply command with stdin input
35+
result = runner.invoke(apply, ['-'], input=stdin_content)
36+
37+
assert result.exit_code == 0
38+
# Verify process_file_blocks was called
39+
mock_process.assert_called_once()
40+
41+
def test_apply_command_file(runner):
42+
"""Test the 'apply' command with file input."""
43+
file_content = (
44+
"[FILE test.py]\n"
45+
"print('hello world')\n"
46+
"[/FILE]\n"
47+
)
48+
49+
with patch('wiz.process_file_blocks') as mock_process:
50+
# Configure the mock to return a single file result
51+
mock_process.return_value = [('test.py', "print('hello world')", 1)]
52+
53+
# Mock file operations
54+
with patch('builtins.open', mock_open(read_data=file_content)):
55+
with patch('os.makedirs'):
56+
# Invoke the apply command with file input
57+
result = runner.invoke(apply, ['.response.md'])
58+
59+
assert result.exit_code == 0
60+
# Verify process_file_blocks was called
61+
mock_process.assert_called_once()
62+
63+
def test_prompt_command(runner):
64+
"""Test the 'prompt' command."""
65+
with patch('wiz.reply') as mock_reply:
66+
mock_reply.return_value = "Response content"
67+
with patch('builtins.open', mock_open()):
68+
result = runner.invoke(cli, ['prompt', 'How can I improve this code?'])
69+
70+
assert result.exit_code == 0
71+
# Verify reply was called with the question
72+
mock_reply.assert_called_once()
73+
assert 'How can I improve this code?' in mock_reply.call_args[0][0]

tests/test_file_filtering.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import pytest
2+
from unittest.mock import patch, mock_open
3+
from wiz import project_files
4+
5+
@pytest.fixture
6+
def mock_glob():
7+
"""Mock glob to return predefined file list."""
8+
with patch('wiz.glob') as mock:
9+
mock.return_value = [
10+
'main.py',
11+
'utils.py',
12+
'test_file.py',
13+
'.hidden_file.py',
14+
'node_modules/some_file.js',
15+
'image.png',
16+
'document.pdf',
17+
'ignored_pattern.txt'
18+
]
19+
yield mock
20+
21+
def test_project_files_basic(mock_glob):
22+
"""Test basic file filtering without exclude pattern."""
23+
with patch('os.path.exists', return_value=False): # No .gitignore
24+
result = project_files()
25+
# Should exclude binary files, hidden files, and node_modules
26+
assert 'main.py' in result
27+
assert 'utils.py' in result
28+
assert 'test_file.py' in result
29+
assert '.hidden_file.py' not in result
30+
assert 'node_modules/some_file.js' not in result
31+
assert 'image.png' not in result
32+
assert 'document.pdf' not in result
33+
34+
def test_project_files_with_exclude(mock_glob):
35+
"""Test file filtering with exclude pattern."""
36+
with patch('os.path.exists', return_value=False): # No .gitignore
37+
result = project_files(exclude_pattern=r'test_.*\.py$')
38+
assert 'main.py' in result
39+
assert 'utils.py' in result
40+
assert 'test_file.py' not in result
41+
42+
def test_gitignore_patterns():
43+
"""Test respecting .gitignore patterns."""
44+
mock_gitignore_content = "ignored_pattern*\n*.log"
45+
46+
with patch('wiz.glob', return_value=['file.py', 'ignored_pattern.txt', 'debug.log']):
47+
with patch('os.path.exists', return_value=True):
48+
with patch('builtins.open', mock_open(read_data=mock_gitignore_content)):
49+
with patch('fnmatch.fnmatch') as mock_fnmatch:
50+
# Set up mock behavior
51+
def match_side_effect(filename, pattern):
52+
if pattern == "ignored_pattern*" and filename.startswith("ignored_pattern"):
53+
return True
54+
if pattern == "*.log" and filename.endswith(".log"):
55+
return True
56+
return False
57+
58+
mock_fnmatch.side_effect = match_side_effect
59+
60+
result = project_files()
61+
62+
# Only file.py should be included
63+
assert 'file.py' in result
64+
assert 'ignored_pattern.txt' not in result
65+
assert 'debug.log' not in result

tests/test_parse.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import pytest
2+
from wiz import process_file_blocks, TAG
3+
4+
def test_process_file_blocks_basic():
5+
"""Test basic file block processing."""
6+
input_lines = [
7+
f"[{TAG} test.py]",
8+
"print('hello world')",
9+
f"[/{TAG}]"
10+
]
11+
result = process_file_blocks(input_lines)
12+
assert len(result) == 1
13+
assert result[0][0] == "test.py" # file path
14+
assert result[0][1] == "print('hello world')" # content
15+
assert result[0][2] == 1 # line number
16+
17+
def test_process_file_blocks_with_code_fence():
18+
"""Test file block processing with code fences."""
19+
input_lines = [
20+
f"[{TAG} test.py]",
21+
"```python",
22+
"print('hello world')",
23+
"```",
24+
f"[/{TAG}]"
25+
]
26+
result = process_file_blocks(input_lines)
27+
assert len(result) == 1
28+
assert result[0][0] == "test.py"
29+
assert result[0][1] == "print('hello world')"
30+
assert result[0][2] == 1
31+
32+
def test_process_file_blocks_multiple():
33+
"""Test processing multiple file blocks."""
34+
input_lines = [
35+
f"[{TAG} file1.py]",
36+
"print('file1')",
37+
f"[/{TAG}]",
38+
"Some text in between",
39+
f"[{TAG} file2.py]",
40+
"print('file2')",
41+
f"[/{TAG}]"
42+
]
43+
result = process_file_blocks(input_lines)
44+
assert len(result) == 2
45+
assert result[0][0] == "file1.py"
46+
assert result[0][1] == "print('file1')"
47+
assert result[1][0] == "file2.py"
48+
assert result[1][1] == "print('file2')"
49+
50+
def test_process_file_blocks_empty():
51+
"""Test processing empty input."""
52+
input_lines = []
53+
result = process_file_blocks(input_lines)
54+
assert len(result) == 0

0 commit comments

Comments
 (0)