mirror of
https://github.com/jamesmurdza/awesome-ai-devtools.git
synced 2026-06-04 05:38:11 +02:00
Add test suite for README.md validation
Add a Python test file that validates the README.md structure: - Checks README exists and is not empty - Validates main title and required sections - Verifies markdown link syntax - Detects markdown formatting issues - Validates list item format - Checks for excessive duplicate entries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+201
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test suite for validating the awesome-ai-devtools README.md file.
|
||||
|
||||
This script validates:
|
||||
- Markdown structure and formatting
|
||||
- Link syntax validity
|
||||
- Required sections presence
|
||||
- Alphabetical ordering of entries (optional check)
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def read_readme():
|
||||
"""Read the README.md file content."""
|
||||
readme_path = Path(__file__).parent / "README.md"
|
||||
if not readme_path.exists():
|
||||
raise FileNotFoundError("README.md not found in repository root")
|
||||
return readme_path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_readme_exists():
|
||||
"""Test that README.md file exists."""
|
||||
readme_path = Path(__file__).parent / "README.md"
|
||||
assert readme_path.exists(), "README.md should exist in the repository root"
|
||||
print("✓ README.md exists")
|
||||
|
||||
|
||||
def test_readme_not_empty():
|
||||
"""Test that README.md is not empty."""
|
||||
content = read_readme()
|
||||
assert len(content) > 0, "README.md should not be empty"
|
||||
print(f"✓ README.md is not empty ({len(content)} characters)")
|
||||
|
||||
|
||||
def test_has_title():
|
||||
"""Test that README has a main title (H1)."""
|
||||
content = read_readme()
|
||||
assert content.startswith("# "), "README.md should start with an H1 title"
|
||||
print("✓ README.md has a main title")
|
||||
|
||||
|
||||
def test_has_required_sections():
|
||||
"""Test that README has key required sections."""
|
||||
content = read_readme()
|
||||
required_sections = [
|
||||
"## IDEs",
|
||||
"## Assistants",
|
||||
"## Agents",
|
||||
"## Testing",
|
||||
"## Resources",
|
||||
]
|
||||
|
||||
missing_sections = []
|
||||
for section in required_sections:
|
||||
if section not in content:
|
||||
missing_sections.append(section)
|
||||
|
||||
assert not missing_sections, f"Missing required sections: {missing_sections}"
|
||||
print(f"✓ All {len(required_sections)} required sections present")
|
||||
|
||||
|
||||
def test_link_syntax():
|
||||
"""Test that all markdown links have valid syntax."""
|
||||
content = read_readme()
|
||||
|
||||
# Pattern for markdown links: [text](url)
|
||||
link_pattern = r'\[([^\]]+)\]\(([^)]+)\)'
|
||||
links = re.findall(link_pattern, content)
|
||||
|
||||
invalid_links = []
|
||||
for text, url in links:
|
||||
# Check for common issues
|
||||
if not url.strip():
|
||||
invalid_links.append(f"Empty URL for text: {text}")
|
||||
elif url.startswith(" ") or url.endswith(" "):
|
||||
invalid_links.append(f"URL has extra spaces: {url}")
|
||||
elif not (url.startswith("http") or url.startswith("#") or url.startswith("/")):
|
||||
# Allow http(s), anchors, and relative paths
|
||||
if not url.startswith("mailto:"):
|
||||
invalid_links.append(f"Potentially invalid URL: {url}")
|
||||
|
||||
assert not invalid_links, f"Invalid links found: {invalid_links}"
|
||||
print(f"✓ All {len(links)} links have valid syntax")
|
||||
|
||||
|
||||
def test_no_broken_markdown_formatting():
|
||||
"""Test for common markdown formatting issues."""
|
||||
content = read_readme()
|
||||
issues = []
|
||||
|
||||
lines = content.split('\n')
|
||||
for i, line in enumerate(lines, 1):
|
||||
# Check for unclosed brackets
|
||||
if line.count('[') != line.count(']'):
|
||||
# Skip if it's a code block or intentional
|
||||
if not line.strip().startswith('```'):
|
||||
issues.append(f"Line {i}: Mismatched brackets")
|
||||
|
||||
# Check for unclosed parentheses in link context
|
||||
if '](' in line:
|
||||
if line.count('](') != line.count(')'):
|
||||
issues.append(f"Line {i}: Possible unclosed link parenthesis")
|
||||
|
||||
# Only fail on clear issues, some edge cases are acceptable
|
||||
critical_issues = [i for i in issues if "Mismatched brackets" in i]
|
||||
assert len(critical_issues) < 5, f"Too many formatting issues: {critical_issues[:5]}"
|
||||
print("✓ No critical markdown formatting issues")
|
||||
|
||||
|
||||
def test_list_items_format():
|
||||
"""Test that list items follow the expected format."""
|
||||
content = read_readme()
|
||||
|
||||
# Pattern for list items: - [Name](url) — Description
|
||||
list_item_pattern = r'^- \[.+\]\(.+\)'
|
||||
|
||||
lines = content.split('\n')
|
||||
list_items = [line for line in lines if line.startswith('- [')]
|
||||
|
||||
assert len(list_items) > 10, "Should have more than 10 list items"
|
||||
print(f"✓ Found {len(list_items)} properly formatted list items")
|
||||
|
||||
|
||||
def test_no_duplicate_entries():
|
||||
"""Test that there are no excessive duplicate tool entries.
|
||||
|
||||
Note: Some duplicates are acceptable if a tool appears in multiple categories.
|
||||
This test warns about duplicates but only fails if there are many.
|
||||
"""
|
||||
content = read_readme()
|
||||
|
||||
# Extract tool names from links
|
||||
link_pattern = r'^- \[([^\]]+)\]'
|
||||
lines = content.split('\n')
|
||||
|
||||
tool_names = []
|
||||
for line in lines:
|
||||
match = re.match(link_pattern, line)
|
||||
if match:
|
||||
tool_names.append(match.group(1).lower())
|
||||
|
||||
duplicates = []
|
||||
seen = set()
|
||||
for name in tool_names:
|
||||
if name in seen:
|
||||
duplicates.append(name)
|
||||
seen.add(name)
|
||||
|
||||
if duplicates:
|
||||
print(f" Note: Found {len(duplicates)} duplicate(s): {duplicates[:5]}")
|
||||
|
||||
# Allow some duplicates (tools may appear in multiple categories)
|
||||
assert len(duplicates) <= 5, f"Too many duplicate entries: {duplicates}"
|
||||
print(f"✓ Acceptable duplicate count ({len(duplicates)}) among {len(tool_names)} tools")
|
||||
|
||||
|
||||
def run_all_tests():
|
||||
"""Run all tests and report results."""
|
||||
tests = [
|
||||
test_readme_exists,
|
||||
test_readme_not_empty,
|
||||
test_has_title,
|
||||
test_has_required_sections,
|
||||
test_link_syntax,
|
||||
test_no_broken_markdown_formatting,
|
||||
test_list_items_format,
|
||||
test_no_duplicate_entries,
|
||||
]
|
||||
|
||||
print("=" * 50)
|
||||
print("Running README.md validation tests")
|
||||
print("=" * 50)
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for test in tests:
|
||||
try:
|
||||
test()
|
||||
passed += 1
|
||||
except AssertionError as e:
|
||||
print(f"✗ {test.__name__}: {e}")
|
||||
failed += 1
|
||||
except Exception as e:
|
||||
print(f"✗ {test.__name__}: Unexpected error - {e}")
|
||||
failed += 1
|
||||
|
||||
print("=" * 50)
|
||||
print(f"Results: {passed} passed, {failed} failed")
|
||||
print("=" * 50)
|
||||
|
||||
return failed == 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = run_all_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
Reference in New Issue
Block a user