tests.test_lint_changelog module¶
Tests for repomatic.lint_changelog: a changelog against its releases.
- tests.test_lint_changelog.changelog_file(tmp_path)[source]¶
A changelog file holding
MULTI_RELEASE_CHANGELOG.A test needing other content writes over the returned path.
- tests.test_lint_changelog.test_lint_changelog_dates_pypi_all_match(changelog_file, monkeypatch)[source]¶
Test that lint returns 0 when all PyPI dates match.
- tests.test_lint_changelog.test_lint_changelog_dates_pypi_mismatch(changelog_file, monkeypatch)[source]¶
Test that lint returns 1 when a PyPI date differs.
- tests.test_lint_changelog.test_lint_changelog_dates_fallback_to_tags(changelog_file, monkeypatch)[source]¶
Test that lint falls back to git tags when not on PyPI.
- tests.test_lint_changelog.test_lint_changelog_dates_fallback_no_package(changelog_file, monkeypatch)[source]¶
Test that lint falls back to git tags when no package name is detected.
- tests.test_lint_changelog.test_lint_changelog_dates_warns_missing_pypi(changelog_file, monkeypatch, caplog)[source]¶
A version missing from PyPI warns only when it postdates the first release.
One predating the first PyPI release gets an info log instead.
- tests.test_lint_changelog.test_lint_changelog_dates_skips_pre_pypi(changelog_file, monkeypatch, caplog)[source]¶
Test that versions older than the first PyPI release are skipped.
- tests.test_lint_changelog.test_lint_changelog_dates_skips_abandoned(changelog_file, monkeypatch, caplog)[source]¶
Test that explicitly-abandoned versions are skipped without warning.
- tests.test_lint_changelog.test_lint_changelog_dates_archive_suppresses_orphans(tmp_path, monkeypatch)[source]¶
An archived version is neither flagged nor re-inserted as an orphan.
- tests.test_lint_changelog.test_lint_changelog_dates_ignores_prerelease_orphans(tmp_path, monkeypatch, prerelease)[source]¶
A published pre-release is never treated as a missing changelog entry.
The changelog documents only final releases. A pre-release (dev/alpha/beta/rc) present as a git tag, PyPI upload, or GitHub release must not be flagged as an orphan. The former bug inserted a spurious
## X.Y.Z.dev0section (placed below its final release, since a pre-release sorts lower) and rewrote the final release’s comparison-URL base to point at it (v1.0.0...v2.0.0becamev2.0.0.dev0...v2.0.0).
- tests.test_lint_changelog.test_count_bullet_words(bullet, expected_words)[source]¶
List markers are stripped before counting; everything else counts.
- tests.test_lint_changelog.test_warn_on_long_bullets_flags_unreleased(caplog)[source]¶
An over-long unreleased bullet emits a non-fatal warning.
- tests.test_lint_changelog.test_warn_on_long_bullets_short_entry_is_silent(caplog)[source]¶
A short unreleased bullet stays under the ceiling and warns nothing.
- tests.test_lint_changelog.test_warn_on_long_bullets_ignores_released_sections(caplog)[source]¶
Immutable released sections are never flagged, only the unreleased one.
- tests.test_lint_changelog.test_warn_on_long_bullets_threshold_zero_disables(caplog)[source]¶
A threshold of 0 disables the check, even for a very long bullet.
- tests.test_lint_changelog.test_warn_on_empty_sections_flags_released(caplog)[source]¶
A released section with no entry emits a non-fatal warning.
- tests.test_lint_changelog.test_warn_on_empty_sections_ignores_the_unreleased_one(caplog)[source]¶
The empty unreleased section the post-release bump creates is expected.
- tests.test_lint_changelog.test_warn_on_empty_sections_counts_an_admonition_as_empty(caplog)[source]¶
An availability caveat is not a statement of what changed.
- tests.test_lint_changelog.test_annotation_reads_true_downstream(warn, content, capsys)[source]¶
A lint annotation cites a section the published documentation declares.
The annotation shows in the checks of a downstream repository, which holds no copy of this project’s
claude.md.
- tests.test_lint_changelog.test_lint_changelog_dates_warns_long_unreleased_bullet(tmp_path, caplog)[source]¶
lint-changelog surfaces the bullet-length warning without failing.
An unreleased-only changelog has no released versions, so the date check returns 0 early; the length warning still fires beforehand.
- tests.test_lint_changelog.test_lint_fix_corrects_date(changelog_file, monkeypatch)[source]¶
Test that –fix corrects mismatched dates in the changelog.
- tests.test_lint_changelog.test_lint_fix_adds_release_admonition(changelog_file, monkeypatch)[source]¶
Test that –fix adds conditional release admonitions.
- tests.test_lint_changelog.test_lint_fix_github_only(changelog_file, monkeypatch)[source]¶
Test that –fix adds GitHub-only admonition when not on PyPI.
- tests.test_lint_changelog.THREE_RELEASE_CHANGELOG = '# Changelog\n\n## [3.0.0 (unreleased)](https://github.com/user/repo/compare/v2.0.0...main)\n\n> [!WARNING]\n> This version is **not released yet** and is under active development.\n\n## [2.0.0 (2026-02-10)](https://github.com/user/repo/compare/v1.0.0...v2.0.0)\n\n- Second release.\n\n## [1.0.0 (2025-12-01)](https://github.com/user/repo/compare/v0.9.0...v1.0.0)\n\n- First release.\n\n## [0.5.0 (2025-06-01)](https://github.com/user/repo/compare/v0.4.0...v0.5.0)\n\n- Pre-PyPI release.\n'¶
Changelog with three released versions for first-version testing.
- tests.test_lint_changelog.test_lint_fix_first_version_admonition(tmp_path, monkeypatch)[source]¶
Test that –fix uses ‘first version’ wording for inaugural releases.
- tests.test_lint_changelog.test_lint_fix_pypi_only(changelog_file, monkeypatch)[source]¶
Test that –fix adds PyPI NOTE and GitHub WARNING when only on PyPI.
- tests.test_lint_changelog.test_lint_fix_no_warning_predates_github(changelog_file, monkeypatch)[source]¶
–fix adds no GitHub WARNING to a version older than the first GitHub release.
- tests.test_lint_changelog.test_lint_fix_adds_yanked_admonition(changelog_file, monkeypatch)[source]¶
Test that –fix adds a CAUTION admonition for yanked releases.
- tests.test_lint_changelog.test_lint_fix_yanked_admonition_carries_reason(changelog_file, monkeypatch)[source]¶
PyPI’s yank reason lands in the CAUTION admonition.
- tests.test_lint_changelog.test_lint_fix_no_admonition_when_nowhere(changelog_file, monkeypatch)[source]¶
–fix adds both WARNINGs to a version on neither PyPI nor GitHub.
- tests.test_lint_changelog.test_lint_fix_idempotent(changelog_file, monkeypatch)[source]¶
Test that running –fix twice produces the same result.
–fix removes a stale ‘is not available on’ warning once published.
- tests.test_lint_changelog.test_lint_keeps_availability_when_only_the_cache_is_stale(tmp_path, monkeypatch, caplog, fix)[source]¶
A cached lookup predating a release must not retract its availability.
Both lookups are TTL-cached for a day, so between publishing
1.1.0and the cache expiring they still answer “1.0.0 only”. Acting on that would stamp a false “not available” warning onto a correct section under--fix, and report the published version as missing in either mode.
- tests.test_lint_changelog.test_lint_finds_a_release_newer_than_the_cache(tmp_path, monkeypatch, caplog, fix)[source]¶
A release the cache predates is re-confirmed live, not reported missing.
Its section records no availability yet, as
--fixhas not run since the release. The cached lookups then have no claim to retract, and their answer is still stale.
- tests.test_lint_changelog.test_lint_leaves_a_recorded_gap_to_the_cache(tmp_path, monkeypatch)[source]¶
A gap the section already records costs no live request.
A version that never reached a platform keeps its “not available” warning from one run to the next: re-fetching it on every run would defeat the cache.
- tests.test_lint_changelog.test_lint_fix_retracts_availability_confirmed_missing(tmp_path, monkeypatch)[source]¶
A live-confirmed absence still retracts, so the gate is not a veto.
The counterpart to
test_lint_keeps_availability_when_only_the_cache_is_stale(): when the forced refresh agrees the release is gone (a deleted GitHub release, a removed PyPI file), rewriting the section is the correct repair.
- tests.test_lint_changelog.test_lint_fix_refuses_when_confirming_github_lookup_fails(tmp_path, monkeypatch)[source]¶
An unreachable API during confirmation must not license a retraction.
- tests.test_lint_changelog.test_lint_reports_an_unconfirmed_gap_when_github_lookup_fails(tmp_path, monkeypatch, caplog)[source]¶
A failed confirming lookup is reported as such, never as a confirmation.
- tests.test_lint_changelog.test_lint_confirms_a_pypi_only_gap_when_github_lookup_fails(tmp_path, monkeypatch, caplog)[source]¶
A gap on PyPI alone is settled by the fresh PyPI answer.
The GitHub half stays cached, but no GitHub answer is needed to tell whether PyPI carries the release.
- tests.test_lint_changelog.test_lint_orphan_detection_returns_1(changelog_file, monkeypatch, caplog)[source]¶
Test that an orphaned version causes lint to return 1.
- tests.test_lint_changelog.test_lint_orphan_fix_inserts_placeholder(changelog_file, monkeypatch)[source]¶
Test that –fix inserts placeholder sections for orphaned versions.
- tests.test_lint_changelog.test_lint_orphan_fix_idempotent(changelog_file, monkeypatch)[source]¶
Test that running –fix with orphans twice produces the same result.
- tests.test_lint_changelog.test_lint_orphan_tag_only(changelog_file, monkeypatch, caplog)[source]¶
Test orphan detected from git tag only (not on PyPI or GitHub).
- tests.test_lint_changelog.test_lint_orphan_uses_pypi_date(changelog_file, monkeypatch)[source]¶
Test that orphan fix prefers PyPI date over GitHub and tag dates.
- tests.test_lint_changelog.test_lint_unreleased_not_flagged_as_orphan(changelog_file, monkeypatch)[source]¶
Test that the unreleased dev version is not flagged as orphan.
- tests.test_lint_changelog.RENAME_CHANGELOG = '# Changelog\n\n## [2.0.0 (unreleased)](https://github.com/user/repo/compare/v1.1.0...main)\n\n> [!WARNING]\n> This version is **not released yet** and is under active development.\n\n## [1.1.0 (2026-02-10)](https://github.com/user/repo/compare/v1.0.0...v1.1.0)\n\n- New release under current name.\n\n## [1.0.0 (2025-12-01)](https://github.com/user/repo/compare/v0.9.0...v1.0.0)\n\n- Release under old name.\n'¶
Changelog fixture for package rename tests.
- tests.test_lint_changelog.test_lint_fix_pypi_package_history(tmp_path, monkeypatch)[source]¶
Versions from former package names get correct PyPI URLs.
- tests.test_lint_changelog.test_lint_pypi_history_current_wins(tmp_path, monkeypatch)[source]¶
Current package name wins when a version exists under both names.
- tests.test_lint_changelog.CHANGELOG_WITH_ADMONITIONS = '# Changelog\n\n## [`1.3.0` (2026-03-01)](https://github.com/user/repo/compare/v1.2.0...v1.3.0)\n\n> [!NOTE]\n> `1.3.0` is available on [🐍 PyPI](https://pypi.org/project/my-package/1.3.0/) and [🐙 GitHub](https://github.com/user/repo/releases/tag/v1.3.0).\n\n- Third release.\n\n## [`1.2.0` (2026-02-01)](https://github.com/user/repo/compare/v1.1.0...v1.2.0)\n\n> [!NOTE]\n> `1.2.0` is available on [🐍 PyPI](https://pypi.org/project/my-package/1.2.0/) and [🐙 GitHub](https://github.com/user/repo/releases/tag/v1.2.0).\n\n- Second release.\n\n## [`1.1.0` (2026-01-01)](https://github.com/user/repo/compare/v1.0.0...v1.1.0)\n\n> [!NOTE]\n> `1.1.0` is available on [🐍 PyPI](https://pypi.org/project/my-package/1.1.0/) and [🐙 GitHub](https://github.com/user/repo/releases/tag/v1.1.0).\n\n- Minor release.\n\n## [`1.0.0` (2025-12-01)](https://github.com/user/repo/compare/v0.9.0...v1.0.0)\n\n> [!NOTE]\n> `1.0.0` is the *first version* available on [🐍 PyPI](https://pypi.org/project/my-package/1.0.0/) and [🐙 GitHub](https://github.com/user/repo/releases/tag/v1.0.0).\n\n- Initial release.\n'¶
Changelog with four releases, each carrying both PyPI and GitHub links.
Used by sanity-gate tests to assert that a destructive rewrite is refused when an upstream lookup fails or returns no data.
- tests.test_lint_changelog.test_lint_fix_refuses_rewrite_when_github_lookup_raises(tmp_path, monkeypatch)[source]¶
The sanity gate refuses to strip every GitHub link when the API errors.
This replays the kdeldycke/click-extra#1702 scenario: GitHub returns a transient error, the legacy code treated the empty dict as “no GitHub releases,” and the rewrite stripped every
🐙 GitHublink from the file. The gate exits non-zero before any write.
- tests.test_lint_changelog.test_lint_fix_refuses_rewrite_when_pypi_empty_with_coverage(tmp_path, monkeypatch)[source]¶
The sanity gate refuses when PyPI returns nothing but coverage exists.
PyPI’s client conflates
404with all transient failures, so an empty result above the coverage threshold is treated as a failure fingerprint rather than a genuine “package no longer published” transition.
- tests.test_lint_changelog.test_lint_fix_proceeds_when_github_fails_but_no_existing_links(changelog_file, monkeypatch)[source]¶
No existing GitHub links → GitHub failure is not destructive, so proceed.
This is the legitimate “new repo, GitHub API flaky” case: there’s nothing in the changelog to lose, so the rewrite is a no-op on the GitHub side and can safely run.
- tests.test_lint_changelog.test_lint_fix_proceeds_when_pypi_empty_below_threshold(tmp_path, monkeypatch)[source]¶
Empty PyPI fetch with < threshold existing PyPI links → proceed.
A project that’s never been on PyPI (or just migrated off) has at most a couple of PyPI links in its history. The threshold lets that legitimate case through while still catching the transient-failure fingerprint.
- tests.test_lint_changelog.test_lint_fix_fails_when_orphans_cannot_be_inserted(tmp_path, monkeypatch)[source]¶
A date fix must not mask an orphan the run was unable to insert.
Orphan insertion needs a repository URL to build comparison links from. Without one the orphan stays missing, so reporting success on the strength of an unrelated date correction would publish the gap.
- tests.test_lint_changelog.test_lint_fix_skips_orphan_without_a_release_date(changelog_file, monkeypatch, caplog)[source]¶
An orphan no source can date is reported, not stamped with a placeholder.
A placeholder date satisfies the released-heading pattern, so it would be re-checked against the reference source on every later run and mismatch forever.
- tests.test_lint_changelog.test_lint_skips_a_release_whose_version_does_not_parse(changelog_file, monkeypatch, source)[source]¶
A release named outside PEP 440 is dropped, never compared.
A GitHub release can carry any tag, and PyPI keeps releases from before PEP 440. Each of them is a version the lint has to read past.
- tests.test_lint_changelog.test_lint_reads_the_tags_once(changelog_file, monkeypatch)[source]¶
The date check and the orphan search share one tag listing.
- tests.test_lint_changelog.test_lint_tolerates_an_unreadable_tag_list(changelog_file, monkeypatch, caplog)[source]¶
A checkout the tags cannot be read from degrades to “no tag known”.
- tests.test_lint_changelog.test_warn_on_entry_order_flags_a_late_breaking_entry(caplog)[source]¶
A Breaking entry filed after an ordinary one is reported with its index.
- tests.test_lint_changelog.test_warn_on_entry_order_flags_a_deprecation_after_a_plain_entry(caplog)[source]¶
A Deprecated entry sits after the Breaking ones and before the rest.
- tests.test_lint_changelog.test_warn_on_entry_order_accepts_the_canonical_order(caplog)[source]¶
Breaking, then Deprecated, then the rest is the order the check wants.
- tests.test_lint_changelog.test_warn_on_entry_order_ignores_released_sections(caplog)[source]¶
A released section is immutable, so its order is never re-flagged.
- tests.test_lint_changelog.test_positional_reference_warns_when_no_thread_carries_the_number(monkeypatch, caplog)[source]¶
A
#Nnaming no issue or pull request is reported with its entry.
- tests.test_lint_changelog.test_positional_reference_check_skips_when_gh_cannot_read_the_repo(monkeypatch, caplog)[source]¶
An unreachable repository leaves every reference unjudged, with a note.
- tests.test_lint_changelog.test_positional_reference_check_reads_prose_only(monkeypatch)[source]¶
A
#Nin a code span, a link target or a slug is no cross-reference.