Files
pkgmgr/tests/unit/pkgmgr/actions/release/test_changelog_lint.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 32e896fd09 fix(release): lint the changelog entry inside the file it lands in
The entry was linted alone, so rules that compare it with the rest of
CHANGELOG.md never fired. infinito-nexus 16.0.0 passed this check with
"-" bullets on top of older "*" entries, and the repository's own
markdown lint then reported 400 MD004 findings and refused the push.

update_changelog now also lints the merged file and reports only the
findings the existing file does not already produce
(lint_changelog_context), so pre-existing issues are not blamed on the
entry. markdownlint-cli2 runs with --no-globs, so a target config with
its own globs no longer lints the whole repository and blocks a release
on unrelated files. Without markdownlint-cli2 the built-in check now
requires "-" list markers (MD004 dash), the strictest style.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 23:31:22 +02:00

87 lines
3.8 KiB
Python

from __future__ import annotations
import shutil
import unittest
from unittest.mock import patch
from pkgmgr.actions.release.files import changelog_lint
from pkgmgr.actions.release.files.changelog_lint import (
lint_changelog_context,
lint_changelog_entry,
transform_changelog_message,
)
from pkgmgr.actions.release.files.changelog_md import _insert_after_h1
_OLD_ASTERISK_FILE = "# Changelog\n\n## [1.0.0] - 2026-01-01\n\n* old bullet\n"
class TestTransformChangelogMessage(unittest.TestCase):
def test_heading_is_pushed_below_the_release_heading(self) -> None:
out = transform_changelog_message("# Title\n\nbody")
self.assertIn("### Title", out)
self.assertNotIn("**Title**", out)
def test_a_heading_already_deep_enough_keeps_its_level(self) -> None:
self.assertEqual(transform_changelog_message("### Sub heading"), "### Sub heading")
def test_a_deeper_heading_is_left_alone(self) -> None:
self.assertEqual(transform_changelog_message("#### Deeper"), "#### Deeper")
def test_inline_code_becomes_italic(self) -> None:
out = transform_changelog_message("use `pkgmgr release` now")
self.assertEqual(out, "use *pkgmgr release* now")
self.assertNotIn("`", out)
def test_plain_message_is_unchanged(self) -> None:
self.assertEqual(transform_changelog_message("just text"), "just text")
def test_no_backtick_survives_and_no_heading_competes(self) -> None:
out = transform_changelog_message("# Heading\n\n* item with `code`")
self.assertNotIn("`", out)
for line in out.split("\n"):
if line.lstrip().startswith("#"):
self.assertTrue(line.startswith("###"), line)
class TestLintChangelogEntry(unittest.TestCase):
def test_clean_entry_has_no_findings(self) -> None:
entry = "## [1.0.0] - 2026-01-01\n\n- a clean bullet\n\n"
self.assertEqual(lint_changelog_entry("CHANGELOG.md", entry), [])
def test_a_structured_message_survives_its_own_transform(self) -> None:
body = transform_changelog_message(
"# Security\n\n- a bullet\n\n## Fixed\n\n- another bullet"
)
entry = f"## [1.0.0] - 2026-01-01\n\n{body}\n\n"
self.assertEqual(lint_changelog_entry("CHANGELOG.md", entry), [])
def test_the_builtin_check_refuses_a_non_dash_list_marker(self) -> None:
entry = "## [1.0.0] - 2026-01-01\n\n* a bullet\n\n"
with patch.object(changelog_lint.shutil, "which", return_value=None):
findings = lint_changelog_entry("CHANGELOG.md", entry)
self.assertTrue(any("MD004" in finding for finding in findings), findings)
@unittest.skipUnless(shutil.which("markdownlint-cli2"), "markdownlint-cli2 is not installed")
class TestLintChangelogContext(unittest.TestCase):
def test_a_marker_that_breaks_the_files_consistency_is_reported(self) -> None:
entry = "## [1.1.0] - 2026-02-01\n\n- new bullet\n\n"
merged = _insert_after_h1(_OLD_ASTERISK_FILE, entry)
findings = lint_changelog_context("CHANGELOG.md", _OLD_ASTERISK_FILE, merged)
self.assertTrue(any("MD004" in finding for finding in findings), findings)
def test_a_finding_the_file_already_has_is_not_blamed_on_the_entry(self) -> None:
existing = "# Changelog\n\n## [1.0.0] - 2026-01-01\n\n* old bullet \n"
entry = "## [1.1.0] - 2026-02-01\n\n* new bullet\n\n"
merged = _insert_after_h1(existing, entry)
self.assertEqual(lint_changelog_context("CHANGELOG.md", existing, merged), [])
def test_a_consistent_entry_has_no_findings(self) -> None:
entry = "## [1.1.0] - 2026-02-01\n\n* new bullet\n\n"
merged = _insert_after_h1(_OLD_ASTERISK_FILE, entry)
self.assertEqual(lint_changelog_context("CHANGELOG.md", _OLD_ASTERISK_FILE, merged), [])
if __name__ == "__main__": # pragma: no cover
unittest.main()