Deskaid
by ezyang
- codemcp
- e2e
#!/usr/bin/env python3
import os
import unittest
from pathlib import Path
from codemcp.rules import (
find_applicable_rules,
match_file_with_glob,
)
from codemcp.testing import MCPEndToEndTestCase
class TestCursorRulesE2E(MCPEndToEndTestCase):
async def asyncSetUp(self):
# Call the parent setUp to initialize git repo and environment
await super().asyncSetUp()
self.repo_root = Path(self.temp_dir.name)
# Create .cursor/rules directory structure
self.rules_dir = self.repo_root / ".cursor" / "rules"
os.makedirs(self.rules_dir, exist_ok=True)
# Create a subdirectory structure for testing
self.src_dir = self.repo_root / "src"
self.components_dir = self.src_dir / "components"
self.utils_dir = self.src_dir / "utils"
self.tests_dir = self.repo_root / "tests"
os.makedirs(self.components_dir, exist_ok=True)
os.makedirs(self.utils_dir, exist_ok=True)
os.makedirs(self.tests_dir, exist_ok=True)
# Create some test files
with open(self.components_dir / "Button.jsx", "w") as f:
f.write("// Button component")
with open(self.components_dir / "Input.tsx", "w") as f:
f.write("// Input component")
with open(self.utils_dir / "helpers.js", "w") as f:
f.write("// Helper functions")
with open(self.utils_dir / "date.js", "w") as f:
f.write("// Date utilities")
with open(self.tests_dir / "test_button.js", "w") as f:
f.write("// Button tests")
with open(self.repo_root / "config.json", "w") as f:
f.write('{"name": "test-repo"}')
# Create some test rule files
self._create_rule(
"jsx_components.mdc",
"JSX Component Rule",
["**/*.jsx", "src/components/*.jsx"],
False,
"## JSX Component Guidelines\n\nUse functional components with hooks.",
)
self._create_rule(
"js_files.mdc",
"JavaScript Files Rule",
["*.js", "**/*.js"],
False,
"## JavaScript Guidelines\n\nUse ES6 features.",
)
self._create_rule(
"config_rule.mdc",
"Config Files Rule",
["*.json", "*.yaml", "*.yml"],
False,
"## Config Files Guidelines\n\nUse consistent formatting.",
)
self._create_rule(
"all_files.mdc",
"Global Rule",
[],
True,
"## Global Guidelines\n\nFollow project conventions.",
)
# Create nested rules directory
nested_dir = self.rules_dir / "typescript"
os.makedirs(nested_dir, exist_ok=True)
with open(nested_dir / "tsx_rule.mdc", "w") as f:
f.write("""---
description: TypeScript JSX Rule
globs: *.tsx, **/*.tsx
alwaysApply: false
---
## TypeScript JSX Guidelines
Use interfaces for props.""")
# Add all files to git so they're tracked
await self.git_run(["add", "."])
await self.git_run(["commit", "-m", "Add test files and rules"])
def _create_rule(self, file_name, description, globs, always_apply, content):
"""Helper to create a rule file"""
globs_str = ", ".join(globs)
always_apply_str = "true" if always_apply else "false"
with open(self.rules_dir / file_name, "w") as f:
f.write(f"""---
description: {description}
globs: {globs_str}
alwaysApply: {always_apply_str}
---
{content}""")
async def test_match_file_with_glob_patterns(self):
"""Test various glob patterns with the match_file_with_glob function"""
# Test basic extension matching
self.assertTrue(match_file_with_glob("test.js", "*.js"))
self.assertTrue(match_file_with_glob("helpers.js", "*.js"))
# Get relative paths for testing to ensure more consistent matching
button_jsx_path = os.path.relpath(
str(self.components_dir / "Button.jsx"), self.repo_root
)
helpers_js_path = os.path.relpath(
str(self.utils_dir / "helpers.js"), self.repo_root
)
config_json_path = os.path.relpath(
str(self.repo_root / "config.json"), self.repo_root
)
# Test patterns with /**/
self.assertTrue(match_file_with_glob(button_jsx_path, "**/*.jsx"))
self.assertTrue(match_file_with_glob(helpers_js_path, "**/*.js"))
# Make sure the relative paths work as expected
self.assertTrue(button_jsx_path.startswith("src/components/"))
self.assertTrue(helpers_js_path.startswith("src/utils/"))
# Test directory-specific patterns
self.assertTrue(match_file_with_glob(button_jsx_path, "src/components/*.jsx"))
self.assertFalse(match_file_with_glob(helpers_js_path, "src/components/*.js"))
# Test patterns with /**/
self.assertTrue(match_file_with_glob(button_jsx_path, "src/**/*.jsx"))
self.assertTrue(match_file_with_glob(helpers_js_path, "src/**/*.js"))
# Test non-matching patterns
self.assertFalse(match_file_with_glob(button_jsx_path, "*.js"))
self.assertFalse(match_file_with_glob(helpers_js_path, "*.jsx"))
# Test patterns with trailing /**
self.assertTrue(match_file_with_glob(helpers_js_path, "src/**"))
self.assertTrue(match_file_with_glob(button_jsx_path, "src/**"))
self.assertFalse(match_file_with_glob(config_json_path, "src/**"))
async def test_find_applicable_rules(self):
"""Test finding applicable rules based on file path"""
# Test finding rules for a JSX file
jsx_file_path = str(self.components_dir / "Button.jsx")
applicable_rules, suggested_rules = find_applicable_rules(
str(self.repo_root), jsx_file_path
)
# Should find the JSX component rule and the global rule
self.assertEqual(len(applicable_rules), 2)
rule_descriptions = [rule.description for rule in applicable_rules]
self.assertIn("JSX Component Rule", rule_descriptions)
self.assertIn("Global Rule", rule_descriptions)
# Test finding rules for a JavaScript file
js_file_path = str(self.utils_dir / "helpers.js")
applicable_rules, suggested_rules = find_applicable_rules(
str(self.repo_root), js_file_path
)
# Should find the JavaScript files rule and the global rule
self.assertEqual(len(applicable_rules), 2)
rule_descriptions = [rule.description for rule in applicable_rules]
self.assertIn("JavaScript Files Rule", rule_descriptions)
self.assertIn("Global Rule", rule_descriptions)
# Test finding rules for a TSX file
tsx_file_path = str(self.components_dir / "Input.tsx")
applicable_rules, suggested_rules = find_applicable_rules(
str(self.repo_root), tsx_file_path
)
# Should find the TypeScript JSX rule and the global rule
self.assertEqual(len(applicable_rules), 2)
rule_descriptions = [rule.description for rule in applicable_rules]
self.assertIn("TypeScript JSX Rule", rule_descriptions)
self.assertIn("Global Rule", rule_descriptions)
# Test finding rules for a config file
config_file_path = str(self.repo_root / "config.json")
applicable_rules, suggested_rules = find_applicable_rules(
str(self.repo_root), config_file_path
)
# Should find the config files rule and the global rule
self.assertEqual(len(applicable_rules), 2)
rule_descriptions = [rule.description for rule in applicable_rules]
self.assertIn("Config Files Rule", rule_descriptions)
self.assertIn("Global Rule", rule_descriptions)
# Test finding rules with no file path (only always_apply rules)
applicable_rules, suggested_rules = find_applicable_rules(str(self.repo_root))
# Should only find the global rule
self.assertEqual(len(applicable_rules), 1)
self.assertEqual(applicable_rules[0].description, "Global Rule")
# Should have suggested rules for all other rule files
self.assertEqual(len(suggested_rules), 4)
if __name__ == "__main__":
unittest.main()