Skip to main content
Glama
test_grep_tool_steps.py18.2 kB
"""Step definitions for grep_tool.feature tests.""" import os import pytest import tempfile import shutil import re from pathlib import Path from pytest_bdd import given, when, then, parsers from typing import Dict, List from mcp_grep.core import MCPGrep @pytest.fixture def test_file_content(): """Fixture to store test file content.""" return "" @pytest.fixture def test_file_path(): """Fixture to create a temporary test file.""" temp_dir = tempfile.mkdtemp() temp_file = os.path.join(temp_dir, "test_file.txt") yield temp_file # Cleanup shutil.rmtree(temp_dir) @pytest.fixture def grep_results(): """Fixture to store grep results.""" return {} @pytest.fixture def mcp_connection(): """Mock fixture for MCP connection.""" # In real implementation, this would connect to the MCP server return {"connected": True} @given("I'm connected to the MCP grep server") def connected_to_server(mcp_connection): """Verify connection to MCP grep server.""" assert mcp_connection["connected"] is True @given(parsers.parse('a file with content "{content}"')) def create_test_file(content, test_file_path): """Create a test file with specified content.""" # Replace escaped newlines with actual newlines actual_content = content.replace('\\n', '\n') # Create the file with the specified content with open(test_file_path, 'w', encoding='utf-8') as f: f.write(actual_content) return actual_content @given("a directory with multiple files containing the word \"secret\"") def create_test_directory_with_files(test_dir): """Create a test directory with multiple files containing 'secret'.""" # Create a few files with the word "secret" files = { "file1.txt": "This file has a secret in it", "file2.txt": "No secrets here", "subdir/file3.txt": "Another secret document", } for path, content in files.items(): full_path = os.path.join(test_dir, path) os.makedirs(os.path.dirname(full_path), exist_ok=True) with open(full_path, 'w', encoding='utf-8') as f: f.write(content) return test_dir @given("multiple files with extensions \".txt\" and \".log\"") def create_files_with_extensions(test_dir): """Create test files with different extensions.""" # Create files with different extensions files = { "file1.txt": "Text file content", "file2.log": "Log file with error", "file3.txt": "Another text file", "file4.log": "Another log with error", } for path, content in files.items(): full_path = os.path.join(test_dir, path) with open(full_path, 'w', encoding='utf-8') as f: f.write(content) return test_dir @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and the file path')) def invoke_grep_with_pattern_and_path(pattern, test_file_path, grep_results): """Invoke grep with a pattern and file path.""" # Use the actual MCPGrep implementation grep = MCPGrep(pattern) # Perform the search results = list(grep.search_file(test_file_path)) # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and ignore_case=True')) def invoke_grep_with_pattern_and_ignore_case(pattern, test_file_path, grep_results): """Invoke grep with case-insensitive pattern matching.""" # Use the actual MCPGrep implementation with ignore_case=True grep = MCPGrep(pattern, ignore_case=True) # Perform the search results = list(grep.search_file(test_file_path)) # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and before_context={before:d} and after_context={after:d}')) def invoke_grep_with_context(pattern, before, after, test_file_path, grep_results): """Invoke grep with context lines.""" # Use the MCPGrep implementation with context settings grep = MCPGrep( pattern=pattern, before_context=before, after_context=after ) # Perform the search results = list(grep.search_file(test_file_path)) # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and fixed_strings=True')) def invoke_grep_with_fixed_strings(pattern, test_file_path, grep_results): """Invoke grep with fixed string matching.""" # For fixed strings, we'll escape all regex special characters escaped_pattern = re.escape(pattern) grep = MCPGrep(escaped_pattern) # Perform the search results = list(grep.search_file(test_file_path)) # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and recursive=True')) def invoke_grep_with_recursive(pattern, test_dir, grep_results): """Invoke grep with recursive search.""" grep = MCPGrep(pattern) # Collect all files in the directory recursively all_files = [] for root, _, files in os.walk(test_dir): for file in files: all_files.append(os.path.join(root, file)) # Search all files all_results = [] for file_path in all_files: try: results = list(grep.search_file(file_path)) all_results.extend(results) except Exception as e: print(f"Error searching {file_path}: {e}") # Store results for verification grep_results["results"] = all_results grep_results["match_count"] = len(all_results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and max_count={count:d}')) def invoke_grep_with_max_count(pattern, count, test_file_path, grep_results): """Invoke grep with maximum result count.""" grep = MCPGrep(pattern) # Perform the search but limit results results = [] for result in grep.search_file(test_file_path): results.append(result) if len(results) >= count: break # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and regexp=True')) def invoke_grep_with_regexp(pattern, test_file_path, grep_results): """Invoke grep with regular expression pattern.""" # MCPGrep already uses regex by default grep = MCPGrep(pattern) # Perform the search results = list(grep.search_file(test_file_path)) # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and invert_match=True')) def invoke_grep_with_invert_match(pattern, test_file_path, grep_results): """Invoke grep with inverted matching.""" grep = MCPGrep(pattern, invert_match=True) # Perform the search with proper invert_match flag results = list(grep.search_file(test_file_path)) # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and line_number=False')) def invoke_grep_without_line_numbers(pattern, test_file_path, grep_results): """Invoke grep without line numbers.""" grep = MCPGrep(pattern) # Perform the search results = list(grep.search_file(test_file_path)) # Remove line numbers from results for result in results: if 'line_num' in result: del result['line_num'] # Store results for verification grep_results["results"] = results grep_results["match_count"] = len(results) grep_results["no_line_numbers"] = True @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and file_pattern="{file_pattern}"')) def invoke_grep_with_file_pattern(pattern, file_pattern, test_dir, grep_results): """Invoke grep with a file pattern filter.""" grep = MCPGrep(pattern) # Find files matching the pattern import fnmatch matching_files = [] for root, _, files in os.walk(test_dir): for file in files: if fnmatch.fnmatch(file, file_pattern): matching_files.append(os.path.join(root, file)) # Search in matching files all_results = [] for file_path in matching_files: try: results = list(grep.search_file(file_path)) all_results.extend(results) except Exception as e: print(f"Error searching {file_path}: {e}") # Store results for verification grep_results["results"] = all_results grep_results["match_count"] = len(all_results) @when(parsers.parse('I invoke the grep tool with pattern "{pattern}" and context={context:d}')) def invoke_grep_with_equal_context(pattern, context, test_file_path, grep_results): """Invoke grep with equal before and after context.""" # Reuse the same logic from invoke_grep_with_context invoke_grep_with_context(pattern, context, context, test_file_path, grep_results) @then(parsers.parse("I should receive results with {count:d} matching line")) @then(parsers.parse("I should receive results with {count:d} matching lines")) def verify_match_count(count, grep_results): """Verify the number of matching lines in the results.""" assert grep_results["match_count"] == count, \ f"Expected {count} matches, got {grep_results['match_count']}" @then(parsers.parse("I should receive results with no more than {count:d} matching lines")) def verify_max_match_count(count, grep_results): """Verify the number of matching lines doesn't exceed the limit.""" assert grep_results["match_count"] <= count, \ f"Expected no more than {count} matches, got {grep_results['match_count']}" @then(parsers.parse("the result should include line number {line_num:d}")) def verify_line_number(line_num, grep_results): """Verify a specific line number is in the results.""" found = False for result in grep_results["results"]: # Handle context-style results if isinstance(result, dict) and "match" in result: if result["match"]["line_num"] == line_num: found = True break # Handle simple results elif isinstance(result, dict) and "line_num" in result: if result["line_num"] == line_num: found = True break assert found, f"Line number {line_num} not found in results" @then(parsers.parse('the result should contain "{text}"')) def verify_result_contains_text(text, grep_results): """Verify the result contains specific text.""" found = False for result in grep_results["results"]: # Handle context-style results if isinstance(result, dict) and "match" in result: if text in result["match"]["line"]: found = True break # Handle simple results elif isinstance(result, dict) and "line" in result: if text in result["line"]: found = True break assert found, f"Text '{text}' not found in any result" @then("each match should include context lines") def verify_context_lines(grep_results): """Verify that matches include context lines.""" for match_with_context in grep_results["results"]: assert "before_context" in match_with_context, "Before context not found" assert "after_context" in match_with_context, "After context not found" # At least one of before or after context should have lines has_context = len(match_with_context["before_context"]) > 0 or len(match_with_context["after_context"]) > 0 assert has_context, "No context lines found" @then("I should receive results from multiple files") def verify_multiple_file_results(grep_results): """Verify that results come from multiple files.""" files = set() for result in grep_results["results"]: # Handle context-style results if isinstance(result, dict) and "match" in result: files.add(result["match"]["file"]) # Handle simple results elif isinstance(result, dict) and "file" in result: files.add(result["file"]) assert len(files) > 1, f"Results only from {len(files)} file(s): {files}" @then('the results should contain "{text1}" and "{text2}"') def verify_results_contain_multiple_texts(text1, text2, grep_results): """Verify results contain multiple specific texts.""" found_text1 = False found_text2 = False for result in grep_results["results"]: # Handle context-style results if isinstance(result, dict) and "match" in result: if text1 in result["match"]["line"]: found_text1 = True if text2 in result["match"]["line"]: found_text2 = True # Handle simple results elif isinstance(result, dict) and "line" in result: if text1 in result["line"]: found_text1 = True if text2 in result["line"]: found_text2 = True assert found_text1, f"Text '{text1}' not found in any result" assert found_text2, f"Text '{text2}' not found in any result" @then('the results should contain "{text1}", "{text2}", and "{text3}"') def verify_results_contain_three_texts(text1, text2, text3, grep_results): """Verify results contain three specific texts.""" found_text1 = False found_text2 = False found_text3 = False for result in grep_results["results"]: # Handle context-style results if isinstance(result, dict) and "match" in result: if text1 in result["match"]["line"]: found_text1 = True if text2 in result["match"]["line"]: found_text2 = True if text3 in result["match"]["line"]: found_text3 = True # Handle simple results elif isinstance(result, dict) and "line" in result: if text1 in result["line"]: found_text1 = True if text2 in result["line"]: found_text2 = True if text3 in result["line"]: found_text3 = True assert found_text1, f"Text '{text1}' not found in any result" assert found_text2, f"Text '{text2}' not found in any result" assert found_text3, f"Text '{text3}' not found in any result" @then("the result should not include line numbers") def verify_no_line_numbers(grep_results): """Verify that results don't include line numbers.""" assert grep_results.get("no_line_numbers", False), "Results should not include line numbers" for result in grep_results["results"]: assert "line_num" not in result, "Line number found in result" @then("I should receive results only from log files") def verify_results_from_log_files_only(grep_results): """Verify that results only come from files matching a pattern.""" for result in grep_results["results"]: file_path = result["file"] if "file" in result else result["match"]["file"] assert file_path.endswith(".log"), f"Result from non-matching file: {file_path}" @then(parsers.parse("the result should include {count:d} lines before and {count:d} lines after the match")) def verify_equal_context_lines(count, grep_results): """Verify that results include the specified number of context lines before and after.""" for match_with_context in grep_results["results"]: # Using <= because there might be fewer lines available in the file assert len(match_with_context["before_context"]) <= count, \ f"Expected {count} or fewer lines before, got {len(match_with_context['before_context'])}" assert len(match_with_context["after_context"]) <= count, \ f"Expected {count} or fewer lines after, got {len(match_with_context['after_context'])}" # Alternative step definition for two-text results with exact wording match @then('the results should contain "apple123" and "apple789"') def verify_results_contain_apple_results(grep_results): """Verify results contain apple123 and apple789.""" found_apple123 = False found_apple789 = False for result in grep_results["results"]: # Handle various result formats line = result.get("line", "") if "match" in result and "line" in result["match"]: line = result["match"]["line"] if "apple123" in line: found_apple123 = True if "apple789" in line: found_apple789 = True assert found_apple123, "Text 'apple123' not found in any result" assert found_apple789, "Text 'apple789' not found in any result" # Alternative step definition for three-text results with exact wording match @then('the results should contain "banana", "orange", and "grape"') def verify_results_contain_fruit_results(grep_results): """Verify results contain banana, orange, and grape.""" found_banana = False found_orange = False found_grape = False for result in grep_results["results"]: # Handle various result formats line = result.get("line", "") if "match" in result and "line" in result["match"]: line = result["match"]["line"] if "banana" in line: found_banana = True if "orange" in line: found_orange = True if "grape" in line: found_grape = True assert found_banana, "Text 'banana' not found in any result" assert found_orange, "Text 'orange' not found in any result" assert found_grape, "Text 'grape' not found in any result"

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/erniebrodeur/mcp-grep'

If you have feedback or need assistance with the MCP directory API, please join our Discord server