Improved prompt for immediate action Additional logging of tool names Updated documentation Context aware decomposition system prompt New script to run code quality checks
584 lines
25 KiB
Python
584 lines
25 KiB
Python
"""
|
|
Tests for the refactor tool functionality
|
|
"""
|
|
|
|
import json
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from tools.refactor import RefactorTool
|
|
from utils.file_utils import read_file_content
|
|
|
|
|
|
class TestRefactorTool:
|
|
"""Test suite for the refactor tool"""
|
|
|
|
@pytest.fixture
|
|
def refactor_tool(self):
|
|
"""Create a refactor tool instance for testing"""
|
|
return RefactorTool()
|
|
|
|
@pytest.fixture
|
|
def mock_model_response(self):
|
|
"""Create a mock model response with valid JSON"""
|
|
|
|
def _create_response(content=None):
|
|
if content is None:
|
|
content = json.dumps(
|
|
{
|
|
"refactor_opportunities": [
|
|
{
|
|
"id": "refactor-001",
|
|
"type": "codesmells",
|
|
"severity": "high",
|
|
"file": "/test/file.py",
|
|
"start_line": 10,
|
|
"end_line": 25,
|
|
"context_start_text": "def long_method():",
|
|
"context_end_text": " return result",
|
|
"issue": "Method too long with multiple responsibilities",
|
|
"suggestion": "Extract helper methods",
|
|
"rationale": "Improves readability and maintainability",
|
|
"code_to_replace": "# original code",
|
|
"replacement_code_snippet": "# refactored code",
|
|
"new_code_snippets": [],
|
|
}
|
|
],
|
|
"priority_sequence": ["refactor-001"],
|
|
"next_actions_for_claude": [],
|
|
}
|
|
)
|
|
|
|
from unittest.mock import Mock
|
|
|
|
return Mock(
|
|
content=content,
|
|
usage={"input_tokens": 100, "output_tokens": 200, "total_tokens": 300},
|
|
model_name="test-model",
|
|
metadata={"finish_reason": "STOP"},
|
|
)
|
|
|
|
return _create_response
|
|
|
|
def test_get_name(self, refactor_tool):
|
|
"""Test that the tool returns the correct name"""
|
|
assert refactor_tool.get_name() == "refactor"
|
|
|
|
def test_get_description(self, refactor_tool):
|
|
"""Test that the tool returns a comprehensive description"""
|
|
description = refactor_tool.get_description()
|
|
assert "INTELLIGENT CODE REFACTORING" in description
|
|
assert "codesmells" in description
|
|
assert "decompose" in description
|
|
assert "modernize" in description
|
|
assert "organization" in description
|
|
|
|
def test_get_input_schema(self, refactor_tool):
|
|
"""Test that the input schema includes all required fields"""
|
|
schema = refactor_tool.get_input_schema()
|
|
|
|
assert schema["type"] == "object"
|
|
assert "files" in schema["properties"]
|
|
assert "prompt" in schema["properties"]
|
|
assert "refactor_type" in schema["properties"]
|
|
|
|
# Check refactor_type enum values
|
|
refactor_enum = schema["properties"]["refactor_type"]["enum"]
|
|
expected_types = ["codesmells", "decompose", "modernize", "organization"]
|
|
assert all(rt in refactor_enum for rt in expected_types)
|
|
|
|
def test_language_detection_python(self, refactor_tool):
|
|
"""Test language detection for Python files"""
|
|
files = ["/test/file1.py", "/test/file2.py", "/test/utils.py"]
|
|
language = refactor_tool.detect_primary_language(files)
|
|
assert language == "python"
|
|
|
|
def test_language_detection_javascript(self, refactor_tool):
|
|
"""Test language detection for JavaScript files"""
|
|
files = ["/test/app.js", "/test/component.jsx", "/test/utils.js"]
|
|
language = refactor_tool.detect_primary_language(files)
|
|
assert language == "javascript"
|
|
|
|
def test_language_detection_mixed(self, refactor_tool):
|
|
"""Test language detection for mixed language files"""
|
|
files = ["/test/app.py", "/test/script.js", "/test/main.java"]
|
|
language = refactor_tool.detect_primary_language(files)
|
|
assert language == "mixed"
|
|
|
|
def test_language_detection_unknown(self, refactor_tool):
|
|
"""Test language detection for unknown file types"""
|
|
files = ["/test/data.txt", "/test/config.json"]
|
|
language = refactor_tool.detect_primary_language(files)
|
|
assert language == "unknown"
|
|
|
|
def test_language_specific_guidance_python(self, refactor_tool):
|
|
"""Test language-specific guidance for Python modernization"""
|
|
guidance = refactor_tool.get_language_specific_guidance("python", "modernize")
|
|
assert "f-strings" in guidance
|
|
assert "dataclasses" in guidance
|
|
assert "type hints" in guidance
|
|
|
|
def test_language_specific_guidance_javascript(self, refactor_tool):
|
|
"""Test language-specific guidance for JavaScript modernization"""
|
|
guidance = refactor_tool.get_language_specific_guidance("javascript", "modernize")
|
|
assert "async/await" in guidance
|
|
assert "destructuring" in guidance
|
|
assert "arrow functions" in guidance
|
|
|
|
def test_language_specific_guidance_unknown(self, refactor_tool):
|
|
"""Test language-specific guidance for unknown languages"""
|
|
guidance = refactor_tool.get_language_specific_guidance("unknown", "modernize")
|
|
assert guidance == ""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_basic_refactor(self, refactor_tool, mock_model_response):
|
|
"""Test basic refactor tool execution"""
|
|
with patch.object(refactor_tool, "get_model_provider") as mock_get_provider:
|
|
mock_provider = MagicMock()
|
|
mock_provider.get_provider_type.return_value = MagicMock(value="test")
|
|
mock_provider.supports_thinking_mode.return_value = False
|
|
mock_provider.generate_content.return_value = mock_model_response()
|
|
mock_get_provider.return_value = mock_provider
|
|
|
|
# Mock file processing
|
|
with patch.object(refactor_tool, "_prepare_file_content_for_prompt") as mock_prepare:
|
|
mock_prepare.return_value = "def test(): pass"
|
|
|
|
result = await refactor_tool.execute(
|
|
{
|
|
"files": ["/test/file.py"],
|
|
"prompt": "Find code smells in this Python code",
|
|
"refactor_type": "codesmells",
|
|
}
|
|
)
|
|
|
|
assert len(result) == 1
|
|
output = json.loads(result[0].text)
|
|
assert output["status"] == "success"
|
|
# The format_response method adds markdown instructions, so content_type should be "markdown"
|
|
# It could also be "json" or "text" depending on the response format
|
|
assert output["content_type"] in ["json", "text", "markdown"]
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_execute_with_style_guide(self, refactor_tool, mock_model_response):
|
|
"""Test refactor tool execution with style guide examples"""
|
|
with patch.object(refactor_tool, "get_model_provider") as mock_get_provider:
|
|
mock_provider = MagicMock()
|
|
mock_provider.get_provider_type.return_value = MagicMock(value="test")
|
|
mock_provider.supports_thinking_mode.return_value = False
|
|
mock_provider.generate_content.return_value = mock_model_response()
|
|
mock_get_provider.return_value = mock_provider
|
|
|
|
# Mock file processing
|
|
with patch.object(refactor_tool, "_prepare_file_content_for_prompt") as mock_prepare:
|
|
mock_prepare.return_value = "def example(): pass"
|
|
|
|
with patch.object(refactor_tool, "_process_style_guide_examples") as mock_style:
|
|
mock_style.return_value = ("# style guide content", "")
|
|
|
|
result = await refactor_tool.execute(
|
|
{
|
|
"files": ["/test/file.py"],
|
|
"prompt": "Modernize this code following our style guide",
|
|
"refactor_type": "modernize",
|
|
"style_guide_examples": ["/test/style_example.py"],
|
|
}
|
|
)
|
|
|
|
assert len(result) == 1
|
|
output = json.loads(result[0].text)
|
|
assert output["status"] == "success"
|
|
|
|
def test_format_response_valid_json(self, refactor_tool):
|
|
"""Test response formatting with valid structured JSON"""
|
|
valid_json_response = json.dumps(
|
|
{
|
|
"status": "refactor_analysis_complete",
|
|
"refactor_opportunities": [
|
|
{
|
|
"id": "test-001",
|
|
"type": "codesmells",
|
|
"severity": "medium",
|
|
"file": "/test.py",
|
|
"start_line": 1,
|
|
"end_line": 5,
|
|
"context_start_text": "def test():",
|
|
"context_end_text": " pass",
|
|
"issue": "Test issue",
|
|
"suggestion": "Test suggestion",
|
|
"rationale": "Test rationale",
|
|
"code_to_replace": "old code",
|
|
"replacement_code_snippet": "new code",
|
|
}
|
|
],
|
|
"priority_sequence": ["test-001"],
|
|
"next_actions_for_claude": [],
|
|
}
|
|
)
|
|
|
|
# Create a mock request
|
|
request = MagicMock()
|
|
request.refactor_type = "codesmells"
|
|
|
|
formatted = refactor_tool.format_response(valid_json_response, request)
|
|
|
|
# Should contain the original response plus implementation instructions
|
|
assert valid_json_response in formatted
|
|
assert "MANDATORY NEXT STEPS" in formatted
|
|
assert "Start executing the refactoring plan immediately" in formatted
|
|
assert "MANDATORY: MUST start executing the refactor plan" in formatted
|
|
|
|
def test_format_response_invalid_json(self, refactor_tool):
|
|
"""Test response formatting with invalid JSON - now handled by base tool"""
|
|
invalid_response = "This is not JSON content"
|
|
|
|
# Create a mock request
|
|
request = MagicMock()
|
|
request.refactor_type = "codesmells"
|
|
|
|
formatted = refactor_tool.format_response(invalid_response, request)
|
|
|
|
# Should contain the original response plus implementation instructions
|
|
assert invalid_response in formatted
|
|
assert "MANDATORY NEXT STEPS" in formatted
|
|
assert "Start executing the refactoring plan immediately" in formatted
|
|
|
|
def test_model_category(self, refactor_tool):
|
|
"""Test that the refactor tool uses EXTENDED_REASONING category"""
|
|
from tools.models import ToolModelCategory
|
|
|
|
category = refactor_tool.get_model_category()
|
|
assert category == ToolModelCategory.EXTENDED_REASONING
|
|
|
|
def test_default_temperature(self, refactor_tool):
|
|
"""Test that the refactor tool uses analytical temperature"""
|
|
from config import TEMPERATURE_ANALYTICAL
|
|
|
|
temp = refactor_tool.get_default_temperature()
|
|
assert temp == TEMPERATURE_ANALYTICAL
|
|
|
|
def test_format_response_more_refactor_required(self, refactor_tool):
|
|
"""Test that format_response handles more_refactor_required field"""
|
|
more_refactor_response = json.dumps(
|
|
{
|
|
"status": "refactor_analysis_complete",
|
|
"refactor_opportunities": [
|
|
{
|
|
"id": "refactor-001",
|
|
"type": "decompose",
|
|
"severity": "critical",
|
|
"file": "/test/file.py",
|
|
"start_line": 1,
|
|
"end_line": 10,
|
|
"context_start_text": "def test_function():",
|
|
"context_end_text": " return True",
|
|
"issue": "Function too large",
|
|
"suggestion": "Break into smaller functions",
|
|
"rationale": "Improves maintainability",
|
|
"code_to_replace": "original code",
|
|
"replacement_code_snippet": "refactored code",
|
|
"new_code_snippets": [],
|
|
}
|
|
],
|
|
"priority_sequence": ["refactor-001"],
|
|
"next_actions_for_claude": [
|
|
{
|
|
"action_type": "EXTRACT_METHOD",
|
|
"target_file": "/test/file.py",
|
|
"source_lines": "1-10",
|
|
"description": "Extract method from large function",
|
|
}
|
|
],
|
|
"more_refactor_required": True,
|
|
"continuation_message": "Large codebase requires extensive refactoring across multiple files",
|
|
}
|
|
)
|
|
|
|
# Create a mock request
|
|
request = MagicMock()
|
|
request.refactor_type = "decompose"
|
|
|
|
formatted = refactor_tool.format_response(more_refactor_response, request)
|
|
|
|
# Should contain the original response plus continuation instructions
|
|
assert more_refactor_response in formatted
|
|
assert "MANDATORY NEXT STEPS" in formatted
|
|
assert "Start executing the refactoring plan immediately" in formatted
|
|
assert "MANDATORY: MUST start executing the refactor plan" in formatted
|
|
assert "AFTER IMPLEMENTING ALL ABOVE" in formatted # Special instruction for more_refactor_required
|
|
assert "continuation_id" in formatted
|
|
|
|
|
|
class TestFileUtilsLineNumbers:
|
|
"""Test suite for line numbering functionality in file_utils"""
|
|
|
|
def test_read_file_content_with_line_numbers(self, project_path):
|
|
"""Test reading file content with line numbers enabled"""
|
|
|
|
# Create a test file within the workspace
|
|
temp_path = project_path / "test_file.py"
|
|
with open(temp_path, "w") as f:
|
|
f.write("def hello():\n print('Hello')\n return True")
|
|
|
|
# Read with line numbers explicitly enabled
|
|
content, tokens = read_file_content(str(temp_path), include_line_numbers=True)
|
|
|
|
# Check that line numbers are present
|
|
assert "1│ def hello():" in content
|
|
assert "2│ print('Hello')" in content
|
|
assert "3│ return True" in content
|
|
assert "--- BEGIN FILE:" in content
|
|
assert "--- END FILE:" in content
|
|
|
|
def test_read_file_content_without_line_numbers(self, project_path):
|
|
"""Test reading file content with line numbers disabled"""
|
|
|
|
# Create a test file within the workspace
|
|
temp_path = project_path / "test_file.txt"
|
|
with open(temp_path, "w") as f:
|
|
f.write("Line 1\nLine 2\nLine 3")
|
|
|
|
# Read with line numbers explicitly disabled
|
|
content, tokens = read_file_content(str(temp_path), include_line_numbers=False)
|
|
|
|
# Check that line numbers are NOT present
|
|
assert "1│" not in content
|
|
assert "Line 1" in content
|
|
assert "Line 2" in content
|
|
assert "--- BEGIN FILE:" in content
|
|
|
|
def test_read_file_content_auto_detect_programming(self, project_path):
|
|
"""Test that auto-detection is OFF by default (backwards compatibility)"""
|
|
|
|
# Create a test file within the workspace
|
|
temp_path = project_path / "test_auto.py"
|
|
with open(temp_path, "w") as f:
|
|
f.write("import os\nprint('test')")
|
|
|
|
# Read without specifying line numbers (should NOT auto-detect for backwards compatibility)
|
|
content, tokens = read_file_content(str(temp_path))
|
|
|
|
# Should NOT automatically add line numbers for .py files (default behavior)
|
|
assert "1│" not in content
|
|
assert "import os" in content
|
|
assert "print('test')" in content
|
|
|
|
def test_read_file_content_auto_detect_text(self, project_path):
|
|
"""Test auto-detection of line numbers for text files"""
|
|
|
|
# Create a test file within the workspace
|
|
temp_path = project_path / "test_auto.txt"
|
|
with open(temp_path, "w") as f:
|
|
f.write("This is a text file\nWith multiple lines")
|
|
|
|
# Read without specifying line numbers (should auto-detect)
|
|
content, tokens = read_file_content(str(temp_path))
|
|
|
|
# Should NOT automatically add line numbers for .txt files
|
|
assert "1│" not in content
|
|
assert "This is a text file" in content
|
|
|
|
def test_line_ending_normalization(self):
|
|
"""Test that different line endings are normalized consistently"""
|
|
from utils.file_utils import _add_line_numbers, _normalize_line_endings
|
|
|
|
# Test different line ending formats
|
|
content_crlf = "Line 1\r\nLine 2\r\nLine 3"
|
|
content_cr = "Line 1\rLine 2\rLine 3"
|
|
content_lf = "Line 1\nLine 2\nLine 3"
|
|
|
|
# All should normalize to the same result
|
|
normalized_crlf = _normalize_line_endings(content_crlf)
|
|
normalized_cr = _normalize_line_endings(content_cr)
|
|
normalized_lf = _normalize_line_endings(content_lf)
|
|
|
|
assert normalized_crlf == normalized_cr == normalized_lf
|
|
assert normalized_lf == "Line 1\nLine 2\nLine 3"
|
|
|
|
# Line numbering should work consistently
|
|
numbered = _add_line_numbers(content_crlf)
|
|
assert " 1│ Line 1" in numbered
|
|
assert " 2│ Line 2" in numbered
|
|
assert " 3│ Line 3" in numbered
|
|
|
|
def test_detect_file_type(self):
|
|
"""Test file type detection"""
|
|
from utils.file_utils import detect_file_type
|
|
|
|
# Test programming language files
|
|
assert detect_file_type("test.py") == "text"
|
|
assert detect_file_type("test.js") == "text"
|
|
assert detect_file_type("test.java") == "text"
|
|
|
|
# Test image files
|
|
assert detect_file_type("image.png") == "image"
|
|
assert detect_file_type("photo.jpg") == "image"
|
|
|
|
# Test binary files
|
|
assert detect_file_type("program.exe") == "binary"
|
|
assert detect_file_type("library.dll") == "binary"
|
|
|
|
def test_should_add_line_numbers(self):
|
|
"""Test line number detection logic"""
|
|
from utils.file_utils import should_add_line_numbers
|
|
|
|
# NO files should get line numbers by default (backwards compatibility)
|
|
assert not should_add_line_numbers("test.py")
|
|
assert not should_add_line_numbers("app.js")
|
|
assert not should_add_line_numbers("Main.java")
|
|
assert not should_add_line_numbers("readme.txt")
|
|
assert not should_add_line_numbers("data.csv")
|
|
|
|
# Explicit override should work
|
|
assert should_add_line_numbers("readme.txt", True)
|
|
assert not should_add_line_numbers("test.py", False)
|
|
|
|
def test_line_numbers_double_triple_digits(self, project_path):
|
|
"""Test line numbering with double and triple digit line numbers"""
|
|
from utils.file_utils import _add_line_numbers
|
|
|
|
# Create content with many lines to test double and triple digit formatting
|
|
lines = []
|
|
for i in range(1, 125): # Lines 1-124 for testing up to triple digits
|
|
if i < 10:
|
|
lines.append(f"# Single digit line {i}")
|
|
elif i < 100:
|
|
lines.append(f"# Double digit line {i}")
|
|
else:
|
|
lines.append(f"# Triple digit line {i}")
|
|
|
|
content = "\n".join(lines)
|
|
numbered_content = _add_line_numbers(content)
|
|
|
|
# Test single digit formatting (should be right-aligned with spaces)
|
|
assert " 1│ # Single digit line 1" in numbered_content
|
|
assert " 9│ # Single digit line 9" in numbered_content
|
|
|
|
# Test double digit formatting (should be right-aligned)
|
|
assert " 10│ # Double digit line 10" in numbered_content # Line 10 has "double digit" content
|
|
assert " 50│ # Double digit line 50" in numbered_content
|
|
assert " 99│ # Double digit line 99" in numbered_content
|
|
|
|
# Test triple digit formatting (should be right-aligned)
|
|
assert " 100│ # Triple digit line 100" in numbered_content
|
|
assert " 124│ # Triple digit line 124" in numbered_content
|
|
|
|
# Verify consistent alignment - all line numbers should end with "│ "
|
|
lines_with_numbers = numbered_content.split("\n")
|
|
for line in lines_with_numbers:
|
|
if "│" in line:
|
|
# Find the pipe character position
|
|
pipe_pos = line.find("│")
|
|
# Ensure the character before pipe is a digit
|
|
assert line[pipe_pos - 1].isdigit(), f"Line format issue: {line}"
|
|
# Ensure the character after pipe is a space
|
|
assert line[pipe_pos + 1] == " ", f"Line format issue: {line}"
|
|
|
|
def test_line_numbers_with_file_reading(self, project_path):
|
|
"""Test line numbering through file reading with large file"""
|
|
|
|
# Create a test file with 150 functions (600 total lines: 4 lines per function)
|
|
temp_path = project_path / "large_test_file.py"
|
|
with open(temp_path, "w") as f:
|
|
for i in range(1, 151): # Functions 1-150
|
|
f.write(f"def function_{i}():\n")
|
|
f.write(f" # This is function number {i}\n")
|
|
f.write(f" return {i}\n")
|
|
f.write("\n")
|
|
|
|
# Read with line numbers enabled
|
|
content, tokens = read_file_content(str(temp_path), include_line_numbers=True)
|
|
|
|
# Calculate actual line numbers based on file structure (4 lines per function)
|
|
# Function 1: lines 1-4, Function 2: lines 5-8, etc.
|
|
# Line 1: def function_1():
|
|
# Line 2: # This is function number 1
|
|
# Line 3: return 1
|
|
# Line 4: (empty)
|
|
|
|
# Test various line number formats in the actual file content
|
|
assert " 1│ def function_1():" in content
|
|
|
|
# Function 13 starts at line 49 (12*4 + 1), so line 50 is " # This is function number 13"
|
|
assert " 50│ # This is function number 13" in content
|
|
|
|
# Line 100 is actually an empty line after function 25 (line 99 was "return 25")
|
|
assert " 100│ " in content # Empty line
|
|
|
|
# Line 99 is "return 25" from function 25
|
|
assert " 99│ return 25" in content
|
|
|
|
# Test more line numbers - line 147 is "return 37" from function 37
|
|
assert " 147│ return 37" in content
|
|
|
|
# Test that we have the final lines (600 total lines)
|
|
assert " 599│ return 150" in content
|
|
assert " 600│ " in content # Final empty line
|
|
|
|
# Verify the file structure is preserved
|
|
assert "--- BEGIN FILE:" in content
|
|
assert "--- END FILE:" in content
|
|
assert str(temp_path) in content
|
|
|
|
def test_line_numbers_large_files_22k_lines(self, project_path):
|
|
"""Test line numbering for very large files (22,500+ lines)"""
|
|
from utils.file_utils import _add_line_numbers
|
|
|
|
# Create content simulating a very large file with 25,000 lines
|
|
lines = []
|
|
for i in range(1, 25001): # Lines 1-25000
|
|
lines.append(f"// Large file line {i}")
|
|
|
|
content = "\n".join(lines)
|
|
numbered_content = _add_line_numbers(content)
|
|
|
|
# Test that width dynamically adjusts to 5 digits for large files
|
|
# Small line numbers should now have 5-digit width
|
|
assert " 1│ // Large file line 1" in numbered_content
|
|
assert " 9│ // Large file line 9" in numbered_content
|
|
assert " 10│ // Large file line 10" in numbered_content
|
|
assert " 99│ // Large file line 99" in numbered_content
|
|
assert " 100│ // Large file line 100" in numbered_content
|
|
assert " 999│ // Large file line 999" in numbered_content
|
|
assert " 1000│ // Large file line 1000" in numbered_content
|
|
assert " 9999│ // Large file line 9999" in numbered_content
|
|
assert "10000│ // Large file line 10000" in numbered_content
|
|
assert "22500│ // Large file line 22500" in numbered_content
|
|
assert "25000│ // Large file line 25000" in numbered_content
|
|
|
|
# Verify consistent alignment - all line numbers should end with "│ "
|
|
lines_with_numbers = numbered_content.split("\n")
|
|
for i, line in enumerate(lines_with_numbers[:100]): # Check first 100 lines
|
|
if "│" in line:
|
|
pipe_pos = line.find("│")
|
|
# For large files, should be 5-character width plus pipe
|
|
assert line[pipe_pos - 1].isdigit(), f"Line {i+1} format issue: {line}"
|
|
assert line[pipe_pos + 1] == " ", f"Line {i+1} format issue: {line}"
|
|
|
|
def test_line_numbers_boundary_conditions(self):
|
|
"""Test line numbering at boundary conditions (9999 vs 10000 lines)"""
|
|
from utils.file_utils import _add_line_numbers
|
|
|
|
# Test exactly 9999 lines (should use 4-digit width)
|
|
lines_9999 = [f"Line {i}" for i in range(1, 10000)] # 9999 lines
|
|
content_9999 = "\n".join(lines_9999)
|
|
numbered_9999 = _add_line_numbers(content_9999)
|
|
|
|
# Should use 4-digit format
|
|
assert " 1│ Line 1" in numbered_9999
|
|
assert "9999│ Line 9999" in numbered_9999
|
|
|
|
# Test exactly 10000 lines (should use 5-digit width)
|
|
lines_10000 = [f"Line {i}" for i in range(1, 10001)] # 10000 lines
|
|
content_10000 = "\n".join(lines_10000)
|
|
numbered_10000 = _add_line_numbers(content_10000)
|
|
|
|
# Should use 5-digit format
|
|
assert " 1│ Line 1" in numbered_10000
|
|
assert "10000│ Line 10000" in numbered_10000
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|