diff --git a/action.yml b/action.yml index dbf935ee..936c16a2 100644 --- a/action.yml +++ b/action.yml @@ -45,6 +45,10 @@ inputs: description: 'Icon to be used for duplicity warning. Icon is placed before the record line.' required: false default: '🔔' + open-hierarchy-sub-issue-icon: + description: 'Icon prepended to open children under a closed hierarchy parent.' + required: false + default: '🟡' published-at: description: 'Use `published-at` timestamp instead of `created-at` as the reference point of previous Release.' required: false @@ -181,6 +185,7 @@ runs: INPUT_HIERARCHY: ${{ inputs.hierarchy }} INPUT_DUPLICITY_SCOPE: ${{ inputs.duplicity-scope }} INPUT_DUPLICITY_ICON: ${{ inputs.duplicity-icon }} + INPUT_OPEN_HIERARCHY_SUB_ISSUE_ICON: ${{ inputs.open-hierarchy-sub-issue-icon }} INPUT_WARNINGS: ${{ inputs.warnings }} INPUT_HIDDEN_SERVICE_CHAPTERS: ${{ inputs.hidden-service-chapters }} INPUT_SERVICE_CHAPTER_ORDER: ${{ inputs.service-chapter-order }} diff --git a/docs/features/issue_hierarchy_support.md b/docs/features/issue_hierarchy_support.md index 75475bcd..87dceac0 100644 --- a/docs/features/issue_hierarchy_support.md +++ b/docs/features/issue_hierarchy_support.md @@ -6,7 +6,10 @@ Represent issue → sub-issue relationships directly in release notes, aggregati ## How It Works - Enabled via input `hierarchy: true` (default: `false`). When disabled, all issues render flat. - Parent issues are detected; sub-issues (and nested hierarchy issues) are fetched and ordered by level. Levels indent with two spaces per depth; nested items use list markers (`-`). -- Only closed sub-issues that contain a change increment (merged PR to default branch) are rendered; open ones, and PR to non default branch are ignored. +- When the parent hierarchy issue is **open**: + - Leaf sub-issues: only closed ones with a change increment are rendered; open ones and those closed or delivered in a previous release are ignored. + - Nested sub-hierarchy children: filtered by change increment only — an open child that aggregates PRs from deeper levels is still rendered. +- When the parent hierarchy issue is **closed**: all children (sub-issues and nested sub-hierarchy children) are rendered. Open children are prefixed with the `open-hierarchy-sub-issue-icon` (default `🟡`) to signal incomplete work. - Each hierarchy issue line can expand with its own extracted release notes block if present (prefixed with `_Release Notes_:` heading within the item block). ## Configuration @@ -23,6 +26,12 @@ Represent issue → sub-issue relationships directly in release notes, aggregati - {"title": "New Features 🎉", "label": "feature"} ``` +Optional inputs related to hierarchy: + +| Input | Default | Description | +|---|---|---| +| `open-hierarchy-sub-issue-icon` | `🟡` | Icon prepended to open sub-issues and open sub-hierarchy issues rendered under a closed hierarchy parent. Set to an empty string to disable highlighting. | + ## Example Result ```markdown ### New Features 🎉 @@ -33,6 +42,7 @@ Represent issue → sub-issue relationships directly in release notes, aggregati - Updated `scala213 = "2.13.13"` - Feature: _Add user MFA enrollment flow_ #123 developed by @alice in #124 - Add user MFA enrollment flow + - 🟡 Feature: _Add OAuth2 login_ #125 ← open sub-issue under closed Epic ``` (1st four indented bullets under Epic line represent the extracted release notes from the parent hierarchy issue's body.) diff --git a/release_notes_generator/action_inputs.py b/release_notes_generator/action_inputs.py index ec6555c7..fe55ec64 100644 --- a/release_notes_generator/action_inputs.py +++ b/release_notes_generator/action_inputs.py @@ -39,6 +39,7 @@ PRINT_EMPTY_CHAPTERS, DUPLICITY_SCOPE, DUPLICITY_ICON, + OPEN_HIERARCHY_SUB_ISSUE_ICON, ROW_FORMAT_LINK_PR, ROW_FORMAT_ISSUE, ROW_FORMAT_PR, @@ -200,6 +201,13 @@ def get_duplicity_icon() -> str: """ return get_action_input(DUPLICITY_ICON, "🔔") # type: ignore[return-value] # string is returned as default + @staticmethod + def get_open_hierarchy_sub_issue_icon() -> str: + """ + Get the icon prepended to open sub-issues rendered under a closed hierarchy parent. + """ + return get_action_input(OPEN_HIERARCHY_SUB_ISSUE_ICON, "🟡") # type: ignore[return-value] # string is returned as default + @staticmethod def get_published_at() -> bool: """ diff --git a/release_notes_generator/model/record/hierarchy_issue_record.py b/release_notes_generator/model/record/hierarchy_issue_record.py index 05a5aa4c..5696d790 100644 --- a/release_notes_generator/model/record/hierarchy_issue_record.py +++ b/release_notes_generator/model/record/hierarchy_issue_record.py @@ -216,24 +216,40 @@ def to_chapter_row(self, add_into_chapters: bool = True) -> str: # add sub-hierarchy issues for sub_hierarchy_issue in self._sub_hierarchy_issues.values(): - logger.debug("Rendering hierarchy issue row for sub-issue #%s", sub_hierarchy_issue.issue.number) - if sub_hierarchy_issue.contains_change_increment(): - logger.debug("Sub-hierarchy issue #%s contains change increment", sub_hierarchy_issue.issue.number) - row = f"{row}\n{sub_hierarchy_issue.to_chapter_row()}" + logger.debug("Rendering sub-hierarchy issue row for #%s", sub_hierarchy_issue.issue.number) + if self.is_open: + if not sub_hierarchy_issue.contains_change_increment(): + continue + # Closed parent: render all sub-hierarchy issues regardless of state or change increment + logger.debug("Rendering sub-hierarchy issue #%s", sub_hierarchy_issue.issue.number) + sub_row = sub_hierarchy_issue.to_chapter_row() + if self.is_closed and sub_hierarchy_issue.is_open: + # Highlight open children under a closed parent to signal incomplete work + icon = ActionInputs.get_open_hierarchy_sub_issue_icon() + header_line, newline, remaining_lines = sub_row.partition("\n") + header_text = header_line.lstrip() + indent = header_line[: len(header_line) - len(header_text)] + sub_row = f"{indent}{icon} {header_text}{newline}{remaining_lines}" + row = f"{row}\n{sub_row}" # add sub-issues if len(self._sub_issues) > 0: sub_indent = " " * (self._level + 1) for sub_issue in self._sub_issues.values(): - logger.debug("Rendering sub-issue row for issue #%d", sub_issue.issue.number) - if sub_issue.is_open: - continue # only closed issues are reported in release notes - - if not sub_issue.contains_change_increment(): - continue # skip sub-issues without change increment - - logger.debug("Sub-issue #%s contains change increment", sub_issue.issue.number) - sub_issue_block = "- " + sub_issue.to_chapter_row() + logger.debug("Rendering sub-issue row for issue #%s", sub_issue.issue.number) + if self.is_open: + if sub_issue.is_open: + continue # only closed issues are reported in release notes + if not sub_issue.contains_change_increment(): + continue # skip sub-issues without change increment + # Closed parent: render all sub-issues regardless of state or change increment + + logger.debug("Rendering sub-issue #%s", sub_issue.issue.number) + open_icon_prefix = "" + if self.is_closed and sub_issue.is_open: + # Highlight open children under a closed parent to signal incomplete work + open_icon_prefix = f"{ActionInputs.get_open_hierarchy_sub_issue_icon()} " + sub_issue_block = "- " + open_icon_prefix + sub_issue.to_chapter_row() ind_child_block = "\n".join( f"{sub_indent}{line}" if line else "" for line in sub_issue_block.splitlines() ) diff --git a/release_notes_generator/utils/constants.py b/release_notes_generator/utils/constants.py index 89693c24..38a2c35b 100644 --- a/release_notes_generator/utils/constants.py +++ b/release_notes_generator/utils/constants.py @@ -26,6 +26,7 @@ CHAPTERS = "chapters" DUPLICITY_SCOPE = "duplicity-scope" DUPLICITY_ICON = "duplicity-icon" +OPEN_HIERARCHY_SUB_ISSUE_ICON = "open-hierarchy-sub-issue-icon" PUBLISHED_AT = "published-at" SKIP_RELEASE_NOTES_LABELS = "skip-release-notes-labels" VERBOSE = "verbose" diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index c924e4dd..ea8b2817 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -17,6 +17,7 @@ from datetime import datetime, timedelta import pytest +from pytest_mock import MockerFixture from github import Github, IssueType, NamedUser from github.Commit import Commit @@ -1226,3 +1227,108 @@ def mock_logging_setup(mocker): """Fixture to mock the basic logging setup using pytest-mock.""" mock_log_config = mocker.patch("logging.basicConfig") yield mock_log_config + + +# Helpers for hierarchy issue record tests +_HIERARCHY_ROW_FMT = "_{title}_ {number}" +_ISSUE_ROW_FMT_MINIMAL = "{number} _{title}_" + + +def make_minimal_issue(mocker: MockerFixture, state: str, number: int) -> Issue: + """Return a minimal Issue mock.""" + issue = mocker.Mock(spec=Issue) + issue.state = state + issue.number = number + issue.title = "Minimal test issue" + issue.body = None + issue.type = None + issue.user = None + issue.assignees = [] + issue.get_labels.return_value = [] + return issue + + +def make_minimal_pr(mocker: MockerFixture, number: int) -> PullRequest: + """Return a minimal PullRequest mock.""" + pr = mocker.Mock(spec=PullRequest) + pr.number = number + pr.body = None + pr.html_url = f"https://github.com/org/repo/pull/{number}" + pr.user = None + pr.assignees = [] + pr.get_labels.return_value = [] + return pr + + +def make_closed_sub_issue_record_with_pr(mocker: MockerFixture, number: int) -> SubIssueRecord: + """Return a closed SubIssueRecord with one PR (no commits).""" + sub_record = SubIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number)) + sub_record.register_pull_request(make_minimal_pr(mocker, number=number + 1000)) + return sub_record + + +def make_open_sub_issue_record_no_pr(mocker: MockerFixture, number: int) -> SubIssueRecord: + """Return an open SubIssueRecord with no PRs.""" + return SubIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number)) + + +def make_closed_sub_issue_record_no_pr(mocker: MockerFixture, number: int) -> SubIssueRecord: + """Return a closed SubIssueRecord with no PRs (no change increment).""" + return SubIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number)) + + +def make_open_sub_hierarchy_record_no_pr(mocker: MockerFixture, number: int) -> HierarchyIssueRecord: + """Return an open HierarchyIssueRecord with no PRs.""" + return HierarchyIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number)) + + +def make_open_sub_hierarchy_record_with_pr(mocker: MockerFixture, number: int) -> HierarchyIssueRecord: + """Return an open HierarchyIssueRecord with one PR (no commits).""" + rec = HierarchyIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number)) + rec.register_pull_request(make_minimal_pr(mocker, number=number + 1000)) + return rec + + +def make_closed_sub_hierarchy_record_with_pr(mocker: MockerFixture, number: int) -> HierarchyIssueRecord: + """Return a closed HierarchyIssueRecord with one PR (no commits).""" + rec = HierarchyIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number)) + rec.register_pull_request(make_minimal_pr(mocker, number=number + 1000)) + return rec + + +def make_closed_sub_hierarchy_record_no_pr(mocker: MockerFixture, number: int) -> HierarchyIssueRecord: + """Return a closed HierarchyIssueRecord with no PRs (no change increment).""" + return HierarchyIssueRecord(make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number)) + + +@pytest.fixture +def patch_hierarchy_action_inputs(mocker): + """Patch ActionInputs with minimal row-format values for HierarchyIssueRecord tests.""" + mocker.patch( + "release_notes_generator.model.record.hierarchy_issue_record.ActionInputs.get_row_format_hierarchy_issue", + return_value=_HIERARCHY_ROW_FMT, + ) + mocker.patch( + "release_notes_generator.model.record.hierarchy_issue_record.ActionInputs.get_duplicity_icon", + return_value="🔔", + ) + mocker.patch( + "release_notes_generator.model.record.issue_record.ActionInputs.get_row_format_issue", + return_value=_ISSUE_ROW_FMT_MINIMAL, + ) + mocker.patch( + "release_notes_generator.model.record.issue_record.ActionInputs.get_duplicity_icon", + return_value="🔔", + ) + mocker.patch( + "release_notes_generator.model.record.record.ActionInputs.get_release_notes_title", + return_value="Release Notes:", + ) + mocker.patch( + "release_notes_generator.model.record.record.ActionInputs.is_coderabbit_support_active", + return_value=False, + ) + mocker.patch( + "release_notes_generator.model.record.hierarchy_issue_record.ActionInputs.get_open_hierarchy_sub_issue_icon", + return_value="🟡", + ) diff --git a/tests/unit/release_notes_generator/model/test_hierarchy_issue_record.py b/tests/unit/release_notes_generator/model/test_hierarchy_issue_record.py index d9f6ba6e..a8fb515f 100644 --- a/tests/unit/release_notes_generator/model/test_hierarchy_issue_record.py +++ b/tests/unit/release_notes_generator/model/test_hierarchy_issue_record.py @@ -19,6 +19,172 @@ from release_notes_generator.model.record.hierarchy_issue_record import HierarchyIssueRecord from release_notes_generator.model.record.issue_record import IssueRecord +from tests.unit.conftest import ( + make_closed_sub_hierarchy_record_no_pr, + make_closed_sub_hierarchy_record_with_pr, + make_closed_sub_issue_record_no_pr, + make_closed_sub_issue_record_with_pr, + make_minimal_issue, + make_minimal_pr, + make_open_sub_hierarchy_record_no_pr, + make_open_sub_hierarchy_record_with_pr, + make_open_sub_issue_record_no_pr, +) + + +def test_to_chapter_row_closed_parent_renders_closed_and_open_sub_issues(mocker, patch_hierarchy_action_inputs): + """ + Closed parent with one closed sub-issue (has PR) and one open sub-issue (no change + increment) → both appear in to_chapter_row() output. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_issues["450"] = make_closed_sub_issue_record_with_pr(mocker, number=450) + record.sub_issues["400"] = make_open_sub_issue_record_no_pr(mocker, number=400) + + row = record.to_chapter_row() + + assert "#450" in row, f"Closed sub-issue #450 must appear; got:\n{row}" + assert "#400" in row, f"Open sub-issue #400 must appear when parent is closed; got:\n{row}" + + +def test_to_chapter_row_closed_parent_highlights_open_sub_issue(mocker, patch_hierarchy_action_inputs): + """Open sub-issue under a closed parent is prefixed with the open-hierarchy-sub-issue-icon.""" + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_issues["400"] = make_open_sub_issue_record_no_pr(mocker, number=400) + record.sub_issues["450"] = make_closed_sub_issue_record_with_pr(mocker, number=450) + + row = record.to_chapter_row() + + lines = row.splitlines() + open_line = next((l for l in lines if "#400" in l), None) + closed_line = next((l for l in lines if "#450" in l), None) + assert open_line is not None, f"Open sub-issue #400 must be present; got:\n{row}" + assert "🟡" in open_line, f"Open sub-issue line must contain icon; got: {open_line!r}" + assert closed_line is not None, f"Closed sub-issue #450 must be present; got:\n{row}" + assert "🟡" not in closed_line, f"Closed sub-issue line must NOT contain icon; got: {closed_line!r}" + + +def test_to_chapter_row_closed_parent_highlights_open_sub_hierarchy_issue(mocker, patch_hierarchy_action_inputs): + """Open sub-hierarchy-issue under a closed parent is prefixed with the open-hierarchy-sub-issue-icon.""" + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_hierarchy_issues["360"] = make_open_sub_hierarchy_record_with_pr(mocker, number=360) + record.sub_hierarchy_issues["350"] = make_closed_sub_hierarchy_record_with_pr(mocker, number=350) + + row = record.to_chapter_row() + + lines = row.splitlines() + open_line = next((l for l in lines if "#360" in l), None) + closed_line = next((l for l in lines if "#350" in l), None) + assert open_line is not None, f"Open sub-hierarchy #360 must be present; got:\n{row}" + assert "🟡" in open_line, f"Open sub-hierarchy line must contain icon; got: {open_line!r}" + assert closed_line is not None, f"Closed sub-hierarchy #350 must be present; got:\n{row}" + assert "🟡" not in closed_line, f"Closed sub-hierarchy line must NOT contain icon; got: {closed_line!r}" + + +def test_to_chapter_row_open_parent_only_renders_closed_sub_issues_with_change_increment(mocker, patch_hierarchy_action_inputs): + """ + Open parent with one closed sub-issue (has PR) and one open sub-issue (no PR) → + only the closed sub-issue appears; open sub-issue is suppressed. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_issues["450"] = make_closed_sub_issue_record_with_pr(mocker, number=450) + record.sub_issues["400"] = make_open_sub_issue_record_no_pr(mocker, number=400) + + row = record.to_chapter_row() + + assert "#450" in row, f"Closed sub-issue #450 must appear; got:\n{row}" + assert "#400" not in row, f"Open sub-issue #400 must NOT appear when parent is open; got:\n{row}" + + +def test_to_chapter_row_open_parent_suppresses_closed_sub_issue_from_previous_release(mocker, patch_hierarchy_action_inputs): + """ + Open parent with one closed sub-issue that has no change increment (closed or delivered + in a previous release) → the sub-issue does not appear in the current release notes. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_issues["450"] = make_closed_sub_issue_record_no_pr(mocker, number=450) + + row = record.to_chapter_row() + + assert "#450" not in row, f"Sub-issue from previous release must NOT appear; got:\n{row}" + + +def test_to_chapter_row_open_parent_suppresses_closed_sub_hierarchy_issue_from_previous_release(mocker, patch_hierarchy_action_inputs): + """ + Open parent with one closed sub-hierarchy issue that has no change increment (closed or + delivered in a previous release) → the sub-hierarchy issue does not appear. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_hierarchy_issues["350"] = make_closed_sub_hierarchy_record_no_pr(mocker, number=350) + + row = record.to_chapter_row() + + assert "#350" not in row, f"Sub-hierarchy issue from previous release must NOT appear; got:\n{row}" + + +def test_to_chapter_row_closed_parent_renders_closed_and_open_sub_hierarchy_issues(mocker, patch_hierarchy_action_inputs): + """ + Closed parent with one closed sub-hierarchy issue (has PR) and one open sub-hierarchy + issue (no change increment) → both appear in to_chapter_row() output. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_CLOSED, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_hierarchy_issues["350"] = make_closed_sub_hierarchy_record_with_pr(mocker, number=350) + record.sub_hierarchy_issues["360"] = make_open_sub_hierarchy_record_no_pr(mocker, number=360) + + row = record.to_chapter_row() + + assert "#350" in row, f"Closed sub-hierarchy issue #350 must appear; got:\n{row}" + assert "#360" in row, f"Open sub-hierarchy issue #360 must appear when parent is closed; got:\n{row}" + + +def test_to_chapter_row_open_parent_only_renders_sub_hierarchy_issues_with_change_increment(mocker, patch_hierarchy_action_inputs): + """ + Open parent with one closed sub-hierarchy issue (has PR) and one open sub-hierarchy + issue (no PR, no change increment) → only the closed sub-hierarchy issue appears. + Unlike leaf sub-issues, sub-hierarchy issues are filtered by change increment only; + an open sub-hierarchy issue that aggregates PRs from its own sub-issues would still appear. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_hierarchy_issues["350"] = make_closed_sub_hierarchy_record_with_pr(mocker, number=350) + record.sub_hierarchy_issues["360"] = make_open_sub_hierarchy_record_no_pr(mocker, number=360) + + row = record.to_chapter_row() + + assert "#350" in row, f"Closed sub-hierarchy issue #350 must appear; got:\n{row}" + assert "#360" not in row, f"Sub-hierarchy issue #360 with no change increment must NOT appear; got:\n{row}" + + +def test_to_chapter_row_open_parent_renders_open_sub_hierarchy_issue_with_change_increment(mocker, patch_hierarchy_action_inputs): + """ + Open parent with one open sub-hierarchy issue that has a PR (change increment present) + → the sub-hierarchy issue appears. Sub-hierarchy children are filtered by change + increment only; open state alone is not a reason to suppress them. + """ + parent = make_minimal_issue(mocker, IssueRecord.ISSUE_STATE_OPEN, number=300) + record = HierarchyIssueRecord(parent) + + record.sub_hierarchy_issues["360"] = make_open_sub_hierarchy_record_with_pr(mocker, number=360) + + row = record.to_chapter_row() + + assert "#360" in row, f"Open sub-hierarchy issue #360 with a PR must appear under open parent; got:\n{row}" + def test_progress_leaf_node_returns_empty_string(make_hierarchy_issue): @@ -205,24 +371,14 @@ def test_progress_per_level_independence(make_hierarchy_issue, make_sub_issue): assert child_c.progress == "", f"child_c: {child_c.progress!r}" -def _make_mock_pull(mocker, number: int): - """Create a minimal mock PullRequest with the given number.""" - from github.PullRequest import PullRequest as GHPullRequest - - pull = mocker.Mock(spec=GHPullRequest) - pull.number = number - pull.get_labels.return_value = [] - return pull - - def test_contains_change_increment_false_when_all_sub_issues_open(mocker, make_hierarchy_issue, make_sub_issue): """Bug regression: open hierarchy with only open sub-issues (with PRs) must not appear in release notes.""" parent = HierarchyIssueRecord(make_hierarchy_issue(10, IssueRecord.ISSUE_STATE_OPEN)) sub1 = make_sub_issue(11, IssueRecord.ISSUE_STATE_OPEN) - sub1.register_pull_request(_make_mock_pull(mocker, 111)) + sub1.register_pull_request(make_minimal_pr(mocker, 111)) sub2 = make_sub_issue(12, IssueRecord.ISSUE_STATE_OPEN) - sub2.register_pull_request(_make_mock_pull(mocker, 112)) + sub2.register_pull_request(make_minimal_pr(mocker, 112)) parent.sub_issues.update({"org/repo#11": sub1, "org/repo#12": sub2}) assert parent.contains_change_increment() is False @@ -233,9 +389,9 @@ def test_contains_change_increment_true_when_one_closed_sub_issue_has_pr(mocker, parent = HierarchyIssueRecord(make_hierarchy_issue(20, IssueRecord.ISSUE_STATE_OPEN)) open_sub = make_sub_issue(21, IssueRecord.ISSUE_STATE_OPEN) - open_sub.register_pull_request(_make_mock_pull(mocker, 211)) + open_sub.register_pull_request(make_minimal_pr(mocker, 211)) closed_sub = make_sub_issue(22, IssueRecord.ISSUE_STATE_CLOSED) - closed_sub.register_pull_request(_make_mock_pull(mocker, 212)) + closed_sub.register_pull_request(make_minimal_pr(mocker, 212)) parent.sub_issues.update({"org/repo#21": open_sub, "org/repo#22": closed_sub}) assert parent.contains_change_increment() is True @@ -251,7 +407,7 @@ def test_contains_change_increment_false_leaf_no_prs(make_hierarchy_issue): def test_contains_change_increment_true_leaf_with_direct_pr(mocker, make_hierarchy_issue): """A hierarchy issue with a direct PR on itself (not from sub-issues) returns True.""" record = HierarchyIssueRecord(make_hierarchy_issue(40, IssueRecord.ISSUE_STATE_OPEN)) - record.register_pull_request(_make_mock_pull(mocker, 401)) + record.register_pull_request(make_minimal_pr(mocker, 401)) assert record.contains_change_increment() is True @@ -274,7 +430,7 @@ def test_contains_change_increment_false_nested_open_only(mocker, make_hierarchy child = HierarchyIssueRecord(make_hierarchy_issue(61, IssueRecord.ISSUE_STATE_OPEN)) open_leaf = make_sub_issue(62, IssueRecord.ISSUE_STATE_OPEN) - open_leaf.register_pull_request(_make_mock_pull(mocker, 621)) + open_leaf.register_pull_request(make_minimal_pr(mocker, 621)) child.sub_issues.update({"org/repo#62": open_leaf}) root.sub_hierarchy_issues.update({"org/repo#61": child}) @@ -291,7 +447,7 @@ def test_contains_change_increment_true_nested_with_closed_leaf(mocker, make_hie child = HierarchyIssueRecord(make_hierarchy_issue(71, IssueRecord.ISSUE_STATE_OPEN)) closed_leaf = make_sub_issue(72, IssueRecord.ISSUE_STATE_CLOSED) - closed_leaf.register_pull_request(_make_mock_pull(mocker, 721)) + closed_leaf.register_pull_request(make_minimal_pr(mocker, 721)) child.sub_issues.update({"org/repo#72": closed_leaf}) root.sub_hierarchy_issues.update({"org/repo#71": child})