from pathlib import Path from clint.config import Config from clint.index import SymbolIndex from clint.linter import lint_file from clint.rules.redundant_test_docstring import RedundantTestDocstring def test_redundant_docstrings_are_flagged(index: SymbolIndex) -> None: code = ''' def test_feature_a(): """ This test verifies that feature A works correctly. It has multiple lines of documentation. """ assert True def test_feature_behavior(): """Test feature.""" assert True def test_c(): """Test the complex interaction between modules.""" assert True def test_validation_logic(): """Test validation.""" assert True def test_feature_d(): assert True ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_something.py"), code, config, index) # All single-line docstrings should be flagged # (test_feature_behavior, test_c, and test_validation_logic) assert len(violations) == 3 assert all(isinstance(v.rule, RedundantTestDocstring) for v in violations) def test_docstring_word_overlap(index: SymbolIndex) -> None: code = ''' def test_very_long_function_name(): """Short.""" assert True def test_short(): """This is a much longer docstring than the function name.""" assert True def test_data_validation(): """Test data validation""" assert True def test_multi(): """Line 1 Line 2""" assert True def test_foo_bar_baz(): """Test qux.""" assert True ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_length.py"), code, config, index) # All single-line docstrings should be flagged # (test_very_long_function_name, test_short, test_data_validation, test_foo_bar_baz) assert len(violations) == 4 def test_class_docstrings_follow_same_rules(index: SymbolIndex) -> None: code = ''' class TestFeature: """ Tests for the Feature module. Includes comprehensive test coverage. """ def test_method(self): assert True class TestFeatureImplementation: """Test feature.""" pass class TestShort: """This is a longer docstring than the class name TestShort.""" pass ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_classes.py"), code, config, index) # Both classes with single-line docstrings should be flagged assert len(violations) == 2 def test_non_test_files_are_ignored(index: SymbolIndex) -> None: code = ''' def test_something(): """Short.""" assert True class TestFeature: """Test.""" pass ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("regular_module.py"), code, config, index) assert len(violations) == 0 def test_supports_test_suffix_files(index: SymbolIndex) -> None: code = ''' def test_feature_implementation(): """Test feature.""" assert True class TestClassImplementation: """Test class.""" pass ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("module_test.py"), code, config, index) assert len(violations) == 2 def test_multiline_docstrings_are_always_allowed(index: SymbolIndex) -> None: code = '''def test_with_multiline(): """ Multi-line. """ assert True def test_with_multiline_compact(): """Line 1 Line 2""" assert True class TestWithMultilineDoc: """ Multi Line """ pass class TestCompactMultiline: """Line1 Line2""" pass ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_multiline.py"), code, config, index) assert len(violations) == 0 def test_error_message_content(index: SymbolIndex) -> None: code = '''def test_data_processing_validation(): """Test data processing.""" pass class TestDataProcessingValidation: """Test data processing.""" pass ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_messages.py"), code, config, index) assert len(violations) == 2 func_violation = violations[0] assert "rarely provide meaningful context" in func_violation.rule.message assert "Consider removing it" in func_violation.rule.message class_violation = violations[1] assert "rarely provide meaningful context" in class_violation.rule.message assert "Consider removing it" in class_violation.rule.message def test_module_single_line_docstrings_are_flagged(index: SymbolIndex) -> None: code = '''"""This is a test module.""" def test_something(): assert True ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_module.py"), code, config, index) assert len(violations) == 1 assert isinstance(violations[0].rule, RedundantTestDocstring) assert "rarely provide meaningful context" in violations[0].rule.message def test_module_multiline_docstrings_are_flagged(index: SymbolIndex) -> None: code = '''""" This is a test module. It has multiple lines. """ def test_something(): assert True ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_module.py"), code, config, index) assert len(violations) == 1 assert isinstance(violations[0].rule, RedundantTestDocstring) def test_module_without_docstring_is_not_flagged(index: SymbolIndex) -> None: code = """def test_something(): assert True """ config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("test_module.py"), code, config, index) assert len(violations) == 0 def test_non_test_module_docstrings_are_ignored(index: SymbolIndex) -> None: code = '''"""This is a regular module.""" def some_function(): pass ''' config = Config(select={RedundantTestDocstring.name}) violations = lint_file(Path("regular_module.py"), code, config, index) assert len(violations) == 0