Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bugbot/rules/assignee_no_login.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ def get_priority_change_date(self, bug):

for change in reversed(bug["history"]):
if (
change["field_name"] == "priority"
and change["added"] == current_priority
change.get("field_name") == "priority"
and change.get("added") == current_priority
):
return datetime.strptime(change["when"], "%Y-%m-%dT%H:%M:%SZ")
return None
Expand Down
108 changes: 108 additions & 0 deletions tests/rules/test_assignee_no_login_priority.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

"""Regression tests for the AssigneeNoLogin rule's priority-change lookup.

The production fix changed ``change["field_name"]`` and ``change["added"]`` to
``change.get("field_name")`` and ``change.get("added")`` in
``bugbot/rules/assignee_no_login.py:get_priority_change_date``. These tests
exercise both the pre-fix failure mode (a KeyError when history entries are
missing one of the expected keys) and the fixed behaviour.
"""

from datetime import datetime, timezone

from bugbot.rules.assignee_no_login import AssigneeNoLogin


def _make_rule():
return AssigneeNoLogin(
username="test@example.com",
user_contact={"cf_id": 0, "url": "https://example.com/"},
)


def test_get_priority_change_date_returns_date_when_history_entry_matches():
rule = _make_rule()
when = "2024-01-15T12:00:00Z"
bug = {
"priority": "P1",
"history": [
{"field_name": "priority", "added": "P3", "when": "2023-01-01T00:00:00Z"},
{"field_name": "priority", "added": "P1", "when": when},
],
}

result = rule.get_priority_change_date(bug)

assert result == datetime(2024, 1, 15, 12, 0, 0, tzinfo=timezone.utc)


def test_get_priority_change_date_handles_missing_field_name_without_keyerror():
"""A history entry without ``field_name`` must not crash the lookup."""
rule = _make_rule()
when = "2024-05-20T08:30:00Z"
bug = {
"priority": "P2",
"history": [
# Missing both "field_name" and "added" - this is what crashed
# before the fix.
{"when": "2024-04-01T00:00:00Z"},
{"field_name": "status", "added": "NEW", "when": "2024-04-15T00:00:00Z"},
{"field_name": "priority", "added": "P2", "when": when},
],
}

result = rule.get_priority_change_date(bug)

assert result == datetime(2024, 5, 20, 8, 30, 0, tzinfo=timezone.utc)


def test_get_priority_change_date_returns_none_when_no_match():
rule = _make_rule()
bug = {
"priority": "P1",
"history": [
{"field_name": "priority", "added": "P2", "when": "2024-01-01T00:00:00Z"},
{
"field_name": "status",
"added": "RESOLVED",
"when": "2024-02-01T00:00:00Z",
},
],
}

assert rule.get_priority_change_date(bug) is None


def test_get_priority_change_date_skips_entries_missing_field_name():
"""Entries that lack ``field_name`` must be skipped, not raise KeyError."""
rule = _make_rule()
bug = {
"priority": "P1",
"history": [
# No "field_name" key at all.
{"added": "P1", "when": "2024-03-10T00:00:00Z"},
],
}

# Should not raise KeyError. The fixed implementation falls back to None
# when the field_name check fails (None != "priority").
assert rule.get_priority_change_date(bug) is None


def test_get_priority_change_date_picks_most_recent_match():
rule = _make_rule()
bug = {
"priority": "P3",
"history": [
{"field_name": "priority", "added": "P1", "when": "2023-06-01T00:00:00Z"},
{"field_name": "priority", "added": "P2", "when": "2024-02-01T00:00:00Z"},
{"field_name": "priority", "added": "P3", "when": "2024-08-15T00:00:00Z"},
],
}

result = rule.get_priority_change_date(bug)

assert result == datetime(2024, 8, 15, 0, 0, 0, tzinfo=timezone.utc)