launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #20319
[Merge] lp:~wgrant/launchpad/complete-incomplete into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/complete-incomplete into lp:launchpad.
Commit message:
Fix BugTaskSet.createManyTasks to map Incomplete to its storage values.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1576857 in Launchpad itself: "Bug tasks can be created with bare unsearchable Incomplete status"
https://bugs.launchpad.net/launchpad/+bug/1576857
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/complete-incomplete/+merge/293859
Fix BugTaskSet.createManyTasks to map Incomplete to its storage values.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/complete-incomplete into lp:launchpad.
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2015-09-28 12:25:52 +0000
+++ lib/lp/bugs/model/bugtask.py 2016-05-05 08:25:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2013 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Classes that implement IBugTask and its related interfaces."""
@@ -302,6 +302,19 @@
return value
+def map_status_for_storage(bug, status, when=None):
+ if status == BugTaskStatus.INCOMPLETE:
+ # We store INCOMPLETE as INCOMPLETE_WITHOUT_RESPONSE so that it
+ # can be queried on efficiently.
+ if (when is None or bug.date_last_message is None or
+ when > bug.date_last_message):
+ return BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE
+ else:
+ return BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE
+ else:
+ return status
+
+
def validate_assignee(self, attr, value):
value = validate_conjoined_attribute(self, attr, value)
# Check if this person is valid and not None.
@@ -868,15 +881,7 @@
"Only Bug Supervisors may change status to %s." % (
new_status.title,))
- if new_status == BugTaskStatus.INCOMPLETE:
- # We store INCOMPLETE as INCOMPLETE_WITHOUT_RESPONSE so that it
- # can be queried on efficiently.
- if (when is None or self.bug.date_last_message is None or
- when > self.bug.date_last_message):
- new_status = BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE
- else:
- new_status = BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE
-
+ new_status = map_status_for_storage(self.bug, new_status, when=when)
self._setStatusDateProperties(self.status, new_status, when=when)
def _setStatusDateProperties(self, old_status, new_status, when=None):
@@ -1573,6 +1578,7 @@
"""See `IBugTaskSet`."""
if status is None:
status = IBugTask['status'].default
+ status = map_status_for_storage(bug, status)
if importance is None:
importance = IBugTask['importance'].default
target_keys = []
=== modified file 'lib/lp/bugs/model/tests/test_bugtask.py'
--- lib/lp/bugs/model/tests/test_bugtask.py 2016-01-26 15:47:37 +0000
+++ lib/lp/bugs/model/tests/test_bugtask.py 2016-05-05 08:25:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -224,6 +224,21 @@
self.assertContentEqual(
expected_policies, get_policies_for_artifact(bug))
+ def test_incomplete_mapped_for_storage(self):
+ """A task is never actually "Incomplete" in the DB.
+
+ Incomplete is automatically mapped to (with response) or
+ (without response) as appropriate.
+ """
+ task = getUtility(IBugTaskSet).createTask(
+ self.factory.makeBug(), self.factory.makePerson(),
+ self.factory.makeProduct(),
+ status=BugTaskStatus.INCOMPLETE)
+ self.assertEqual(
+ BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE, task._status)
+ self.assertEqual(
+ BugTaskStatus.INCOMPLETE, task.status)
+
class TestBugTaskCreationPackageComponent(TestCaseWithFactory):
"""IBugTask contains a convenience method to look up archive component
=== modified file 'lib/lp/bugs/tests/test_bug_messages.py'
--- lib/lp/bugs/tests/test_bug_messages.py 2012-09-18 18:36:09 +0000
+++ lib/lp/bugs/tests/test_bug_messages.py 2016-05-05 08:25:25 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Webservice unit tests related to Launchpad Bugs."""
@@ -8,10 +8,15 @@
from zope.component import getUtility
from lp.app.enums import InformationType
+from lp.bugs.interfaces.bugtask import (
+ BugTaskStatus,
+ BugTaskStatusSearch,
+ )
from lp.registry.interfaces.accesspolicy import IAccessPolicySource
from lp.testing import (
login,
login_celebrity,
+ person_logged_in,
TestCaseWithFactory,
)
from lp.testing.layers import DatabaseFunctionalLayer
@@ -77,3 +82,48 @@
self.factory.makeAccessPolicyGrant(
policy=policy, grantor=product.owner, grantee=person)
self.assertTrue(bug.userCanSetCommentVisibility(person))
+
+
+class TestBugLinkMessageSetsIncompleteStatus(TestCaseWithFactory):
+
+ """Test that Bug.linkMessage updates "Incomplete (without response)" bugs.
+
+ They should transition from "Incomplete (without response)" to
+ "Incomplete (with response)".
+ """
+
+ layer = DatabaseFunctionalLayer
+
+ def test_new_untouched(self):
+ bugtask = self.factory.makeBugTask(status=BugTaskStatus.NEW)
+ with person_logged_in(bugtask.owner):
+ bugtask.bug.linkMessage(self.factory.makeMessage())
+ self.assertEqual(BugTaskStatus.NEW, bugtask.status)
+
+ def test_incomplete_with_response_untouched(self):
+ bugtask = self.factory.makeBugTask(
+ status=BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE)
+ self.assertEqual(
+ BugTaskStatus.INCOMPLETE, bugtask.status)
+ self.assertEqual(
+ BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE, bugtask._status)
+ with person_logged_in(bugtask.owner):
+ bugtask.bug.linkMessage(self.factory.makeMessage())
+ self.assertEqual(
+ BugTaskStatus.INCOMPLETE, bugtask.status)
+ self.assertEqual(
+ BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE, bugtask._status)
+
+ def test_incomplete_without_response_updated(self):
+ bugtask = self.factory.makeBugTask(
+ status=BugTaskStatus.INCOMPLETE)
+ self.assertEqual(
+ BugTaskStatus.INCOMPLETE, bugtask.status)
+ self.assertEqual(
+ BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE, bugtask._status)
+ with person_logged_in(bugtask.owner):
+ bugtask.bug.linkMessage(self.factory.makeMessage())
+ self.assertEqual(
+ BugTaskStatus.INCOMPLETE, bugtask.status)
+ self.assertEqual(
+ BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE, bugtask._status)
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2016-04-28 02:25:46 +0000
+++ lib/lp/testing/factory.py 2016-05-05 08:25:25 +0000
@@ -83,7 +83,10 @@
CreateBugParams,
IBugSet,
)
-from lp.bugs.interfaces.bugtask import BugTaskStatus
+from lp.bugs.interfaces.bugtask import (
+ BugTaskStatus,
+ IBugTaskSet,
+ )
from lp.bugs.interfaces.bugtracker import (
BugTrackerType,
IBugTrackerSet,
@@ -1875,7 +1878,8 @@
removeSecurityProxy(bug).clearBugNotificationRecipientsCache()
return bug
- def makeBugTask(self, bug=None, target=None, owner=None, publish=True):
+ def makeBugTask(self, bug=None, target=None, owner=None, publish=True,
+ status=None):
"""Create and return a bug task.
If the bug is already targeted to the given target, the existing
@@ -1944,8 +1948,8 @@
if owner is None:
owner = self.makePerson()
- task = removeSecurityProxy(bug).addTask(
- owner, removeSecurityProxy(target))
+ task = getUtility(IBugTaskSet).createTask(
+ bug, owner, target, status=status)
removeSecurityProxy(bug).clearBugNotificationRecipientsCache()
return task
Follow ups