fix: make README audit deterministic

This commit is contained in:
Wilson Freitas
2026-09-02 09:07:53 -03:00
parent 40faf37a33
commit 644832fd35
2 changed files with 71 additions and 1 deletions
+10 -1
View File
@@ -72,6 +72,8 @@ def collect_targets(readme_path: str | Path) -> list[AuditTarget]:
def _parse_github_repository_url(url: str) -> str | None:
if "?" in url or "#" in url:
return None
try:
parsed = urlsplit(url)
port = parsed.port
@@ -247,7 +249,14 @@ def _candidate_repositories(entry: ReadmeEntry, github_client: Any) -> tuple[Can
)
)
matches.sort(key=lambda candidate: (-candidate.stars, candidate.full_name.casefold()))
matches.sort(
key=lambda candidate: (
-candidate.stars,
candidate.full_name.casefold(),
candidate.full_name,
candidate.url,
)
)
unique = []
seen = set()
for candidate in matches:
+61
View File
@@ -199,6 +199,35 @@ class AuditReadmeTests(unittest.TestCase):
self.assertEqual(findings, [])
def test_empty_query_and_fragment_delimiters_are_not_github_repository_urls(self):
urls = [
"https://github.com/owner/query?",
"https://github.com/owner/fragment#",
"https://github.com/owner/slash-query/?",
"https://github.com/owner/slash-fragment/#",
]
readme = "## Trading & Backtesting\n" + "".join(
f"- [Project {index}]({url}) - `Python` - Entry.\n"
for index, url in enumerate(urls)
)
client = FakeGithubClient(
{
"owner/query": FakeRepository("owner/query"),
"owner/fragment": FakeRepository("owner/fragment"),
"owner/slash-query": FakeRepository("owner/slash-query"),
"owner/slash-fragment": FakeRepository("owner/slash-fragment"),
}
)
findings = self.audit(
readme,
client,
{url: observation(url) for url in urls},
)
self.assertEqual(findings, [])
self.assertEqual(client.repo_calls, [])
def test_github_404_adds_no_metadata_finding_and_other_api_errors_escape(self):
missing_url = "https://github.com/owner/missing"
broken_url = "https://github.com/owner/broken"
@@ -289,6 +318,38 @@ class AuditReadmeTests(unittest.TestCase):
[{"query": "Dead Tool in:name", "sort": "stars", "order": "desc"}],
)
def test_equal_star_case_variant_candidates_are_independent_of_api_order(self):
url = "https://dead.example/tool"
readme = (
"## Trading & Backtesting\n"
f"- [Dead Tool]({url}) - `Python` - Entry.\n"
)
upper = FakeRepository("OWNER/Dead-Tool", name="dead-tool", stars=40)
lower = FakeRepository("owner/dead-tool", name="dead-tool", stars=40)
selections = []
for repositories in ((lower, upper), (upper, lower)):
client = FakeGithubClient(
searches={"Dead Tool in:name": repositories}
)
findings = self.audit(
readme,
client,
{url: observation(url, Outcome.DEAD, status=404)},
)
selections.append(
next(
finding.candidates
for finding in findings
if finding.kind == FindingKind.CANDIDATES
)
)
expected = (
Candidate("OWNER/Dead-Tool", "https://github.com/OWNER/Dead-Tool", 40),
)
self.assertEqual(selections, [expected, expected])
def test_all_finding_kinds_are_mapped_and_sorted_deterministically(self):
urls = {
"dead": "https://dead.example",