From fe0bf47c5a99f3d67e1ef9eb092008eb43afdf5b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Oct 2025 16:10:48 +0000 Subject: [PATCH 1/3] Add comprehensive test coverage to reach 70%+ threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhanced test suite across multiple modules: - Added 15 new CLI tests covering all commands (explain, examples, test, diagram) - Added 11 new tester tests for edge cases and additional patterns - Created new test_api.py with 11 tests for public API wrapper functions - Added 6 new explainer tests for various regex features - Added to_dict() method to MatchResult class for CLI integration - Updated pytest configuration to exclude src directory from test collection Test coverage improved from 68% to 70.19%, now passing the required threshold. All 92 tests passing successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pyproject.toml | 3 +- src/rexplain/core/tester.py | 9 +++ tests/test_api.py | 105 +++++++++++++++++++++++++++++++++++ tests/test_cli.py | 99 ++++++++++++++++++++++++++++++++- tests/test_explainer.py | 54 ++++++++++++++++++ tests/test_tester.py | 106 ++++++++++++++++++++++++++++++++++++ 6 files changed, 374 insertions(+), 2 deletions(-) create mode 100644 tests/test_api.py diff --git a/pyproject.toml b/pyproject.toml index 3a7cca2..b080e68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,4 +46,5 @@ path = "src/rexplain/__init__.py" [tool.pytest.ini_options] addopts = "--cov=src/rexplain --cov-report=term-missing --cov-fail-under=70" -testpaths = ["tests"] \ No newline at end of file +testpaths = ["tests"] +norecursedirs = ["src", ".git", "dist", "build"] \ No newline at end of file diff --git a/src/rexplain/core/tester.py b/src/rexplain/core/tester.py index 3f7dec1..d3edd2d 100644 --- a/src/rexplain/core/tester.py +++ b/src/rexplain/core/tester.py @@ -24,6 +24,15 @@ def __str__(self): f"failed_at={self.failed_at}, partial_matches={self.partial_matches})" ) + def to_dict(self): + """Convert the result to a dictionary format""" + return { + 'matches': self.matches, + 'reason': self.reason, + 'failed_at': self.failed_at, + 'partial_matches': self.partial_matches + } + class RegexTester: """ Tests if a string matches a regex pattern and provides detailed feedback. diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..79a1ced --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,105 @@ +""" +Tests for the public API wrapper functions in __init__.py +""" +import sys +import os +import re +import tempfile +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../src'))) + +import rexplain +from rexplain import explain, examples, diagram + +def test_explain_api(): + """Test the explain() API function""" + result = explain(r'\d+') + assert isinstance(result, str) + assert 'digit' in result.lower() or r'\d' in result + +def test_explain_api_with_flags(): + """Test explain() with regex flags""" + result = explain(r'abc', flags=re.IGNORECASE) + assert isinstance(result, str) + assert 'a' in result.lower() + +def test_examples_api(): + """Test the examples() API function""" + result = examples(r'[a-z]{3}', count=5) + assert isinstance(result, list) + assert len(result) == 5 + # Verify all examples match the pattern + pattern = re.compile(r'[a-z]{3}') + for ex in result: + assert pattern.fullmatch(ex) + +def test_examples_api_default_count(): + """Test examples() with default count""" + result = examples(r'\d+') + assert isinstance(result, list) + assert len(result) == 3 # Default count + +def test_examples_api_with_flags(): + """Test examples() with regex flags""" + result = examples(r'[a-z]+', count=2, flags=re.IGNORECASE) + assert isinstance(result, list) + assert len(result) == 2 + +def test_test_api_match(): + """Test the test() API function with matching string""" + result = rexplain.test(r'foo.*', 'foobar') + assert hasattr(result, 'matches') + assert result.matches is True + assert result.reason + +def test_test_api_no_match(): + """Test the test() API function with non-matching string""" + result = rexplain.test(r'abc', 'xyz') + assert hasattr(result, 'matches') + assert result.matches is False + assert result.reason + +def test_test_api_with_flags(): + """Test test() with regex flags""" + result = rexplain.test(r'abc', 'ABC', flags=re.IGNORECASE) + assert result.matches is True + +def test_diagram_api_basic(): + """Test the diagram() API function without output file""" + result = diagram(r'\w+') + assert isinstance(result, str) + assert '= 2 # Should have at least 2 examples + +def test_cli_test_match(): + """Test test command with matching string""" + result = subprocess.run([sys.executable, '-m', 'rexplain.cli.main', 'test', 'abc', 'abc'], + capture_output=True, text=True, cwd='/home/user/rexplain/src') + assert result.returncode == 0 + +def test_cli_test_no_match(): + """Test test command with non-matching string""" + result = subprocess.run([sys.executable, '-m', 'rexplain.cli.main', 'test', 'abc', 'xyz'], + capture_output=True, text=True, cwd='/home/user/rexplain/src') + assert result.returncode == 1 # Should exit with error code + +def test_cli_diagram_stdout(): + """Test diagram command without output file""" + result = subprocess.run([sys.executable, '-m', 'rexplain.cli.main', 'diagram', r'\w+'], + capture_output=True, text=True, cwd='/home/user/rexplain/src') + assert result.returncode == 0 + assert ' Date: Thu, 30 Oct 2025 17:58:56 +0000 Subject: [PATCH 2/3] Fix CI workflow to use 'python -m pytest' for proper plugin loading The CI was failing because running 'pytest' directly doesn't load the pytest-cov plugin properly in some environments. Using 'python -m pytest' ensures pytest-cov is available and the coverage options from pyproject.toml are recognized. This fixes the test workflow to properly run tests with coverage checking. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e9f2fb9..e68191c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,4 +23,4 @@ jobs: python -m pip install --upgrade pip pip install .[test] - name: Run tests with coverage - run: pytest \ No newline at end of file + run: python -m pytest \ No newline at end of file From fad3a33065c06c9940b7cbf2fc1496baff63e828 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Oct 2025 18:11:05 +0000 Subject: [PATCH 3/3] Update minimum Python version to 3.10 to match pyrailroad dependency The pyrailroad package (used for diagram generation) requires Python 3.10+. All available versions of pyrailroad (0.3.0 through 0.4.0) require Python >=3.10. Changes: - Updated requires-python from >=3.8 to >=3.10 in pyproject.toml - Removed Python 3.8 and 3.9 from classifiers - Updated CI test matrix to test on Python 3.10, 3.12, and 3.13 This fixes the pip installation error on Python 3.8: "ERROR: Could not find a version that satisfies the requirement pyrailroad>=0.4.0" --- .github/workflows/test.yml | 2 +- pyproject.toml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e68191c..79ab392 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.12, 3.13] + python-version: ["3.10", "3.12", "3.13"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} diff --git a/pyproject.toml b/pyproject.toml index b080e68..5c3502b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ description = "Explain, test, and generate examples for regular expressions" authors = [{name = "Dev B. Makwana"}] license = {text = "MIT"} readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.10" dependencies = [ "pyrailroad>=0.4.0" ] @@ -18,8 +18,6 @@ classifiers = [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12",