Merge lp:~sinzui/launchpad/delete-conjoined-bugtask-0 into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11341
Proposed branch: lp:~sinzui/launchpad/delete-conjoined-bugtask-0
Merge into: lp:launchpad
Diff against target: 73 lines (+33/-3)
2 files modified
lib/lp/registry/browser/__init__.py (+6/-2)
lib/lp/registry/browser/tests/test_milestone.py (+27/-1)
To merge this branch: bzr merge lp:~sinzui/launchpad/delete-conjoined-bugtask-0
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+32163@code.launchpad.net

Description of the change

This is my branch to delete milestone conjoined bugtasks instead of oops.

    lp:~sinzui/launchpad/delete-conjoined-bugtask-0
    Diff size: 74
    Launchpad bug:
          https://bugs.launchpad.net/bugs/522599
    Test command: ./bin/test -vv \
          -t TestMilestoneDeleteView -t registry.*test_yuitests
    Pre-implementation: deryck, jcsackett
    Target release: 10.09

Delete milestone conjoined bugtasks instead of oops
---------------------------------------------------

OOPS-1507A1604, shows that the act of untargeting a bugtask fails when it is
conjoined. This is contrary to the intended rules where the bugtask should be
deleted...removing the insane conjoined situation.

Rules
-----

In discussion with Deryck and jcsackett regarding when a bug is conjoined
and where the oops really takes place. We discovered there are two, not
one, condition where a bugtask must be deleted. All bugtasks targeted to
product series are deleted since the project already has a bugtask, but
in the case of milestones, some of them may also be conjoined.

    * When iterating over the milestone's bug tasks, check if it has a
      conjoined_master and call ``Store.of(bugtask).remove(bugtask)``
      as the productseries delete milestone code does.

QA
--

    * Create a series for a project you control and make it the development
      focus.
    * Create a milestone for the release.
    * Target a bug to the series.
    * Choose the delete the milestone.
    * Verify the milestone is deleted.

Lint
----

Linting changed files:
  lib/lp/registry/browser/__init__.py
  lib/lp/registry/browser/tests/test_milestone.py

Test
----

    * lib/lp/registry/browser/tests/test_milestone.py
      * Added a test that reproduced error seen in the oops and verification
        that the milestone was deleted.

Implementation
--------------

    * lib/lp/registry/browser/__init__.py
      * Added a condition to check for a conjoined_master and delete it when
        there is a master.
      * Wrapped long line per lint.

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

The name in your test data probably isn't needed as the factory has sane defaults.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/browser/__init__.py'
--- lib/lp/registry/browser/__init__.py 2010-06-12 03:37:58 +0000
+++ lib/lp/registry/browser/__init__.py 2010-08-10 02:14:45 +0000
@@ -115,10 +115,11 @@
115 var create_milestone_link = Y.get(115 var create_milestone_link = Y.get(
116 '.menu-link-create_milestone');116 '.menu-link-create_milestone');
117 create_milestone_link.addClass('js-action');117 create_milestone_link.addClass('js-action');
118 var milestone_table = Y.lp.registry.milestonetable;
118 var config = {119 var config = {
119 milestone_form_uri: milestone_form_uri,120 milestone_form_uri: milestone_form_uri,
120 series_uri: series_uri,121 series_uri: series_uri,
121 next_step: Y.lp.registry.milestonetable.get_milestone_row,122 next_step: milestone_table.get_milestone_row,
122 activate_node: create_milestone_link123 activate_node: create_milestone_link
123 };124 };
124 Y.lp.registry.milestoneoverlay.attach_widget(config);125 Y.lp.registry.milestoneoverlay.attach_widget(config);
@@ -216,7 +217,10 @@
216 """Delete a milestone and unlink related objects."""217 """Delete a milestone and unlink related objects."""
217 self._unsubscribe_structure(milestone)218 self._unsubscribe_structure(milestone)
218 for bugtask in self._getBugtasks(milestone):219 for bugtask in self._getBugtasks(milestone):
219 bugtask.milestone = None220 if bugtask.conjoined_master is not None:
221 Store.of(bugtask).remove(bugtask)
222 else:
223 bugtask.milestone = None
220 for spec in self._getSpecifications(milestone):224 for spec in self._getSpecifications(milestone):
221 spec.milestone = None225 spec.milestone = None
222 self._deleteRelease(milestone.product_release)226 self._deleteRelease(milestone.product_release)
223227
=== modified file 'lib/lp/registry/browser/tests/test_milestone.py'
--- lib/lp/registry/browser/tests/test_milestone.py 2010-08-06 20:15:31 +0000
+++ lib/lp/registry/browser/tests/test_milestone.py 2010-08-10 02:14:45 +0000
@@ -7,7 +7,11 @@
77
8import unittest8import unittest
99
10from lp.testing import ANONYMOUS, login_person, login10from zope.component import getUtility
11
12from canonical.testing.layers import DatabaseFunctionalLayer
13from lp.bugs.interfaces.bugtask import IBugTaskSet
14from lp.testing import ANONYMOUS, login_person, login, TestCaseWithFactory
11from lp.testing.views import create_initialized_view15from lp.testing.views import create_initialized_view
12from lp.testing.memcache import MemcacheTestCase16from lp.testing.memcache import MemcacheTestCase
1317
@@ -78,5 +82,27 @@
78 self.assertEqual(360, view.expire_cache_minutes)82 self.assertEqual(360, view.expire_cache_minutes)
7983
8084
85class TestMilestoneDeleteView(TestCaseWithFactory):
86 """Test the delete rules applied by the Milestone Delete view."""
87
88 layer = DatabaseFunctionalLayer
89
90 def test_delete_conjoined_bugtask(self):
91 product = self.factory.makeProduct()
92 bug = self.factory.makeBug(product=product)
93 master_bugtask = getUtility(IBugTaskSet).createTask(
94 bug, productseries=product.development_focus, owner=product.owner)
95 milestone = self.factory.makeMilestone(
96 productseries=product.development_focus)
97 login_person(product.owner)
98 master_bugtask.transitionToMilestone(milestone, product.owner)
99 form = {
100 'field.actions.delete': 'Delete Milestone',
101 }
102 view = create_initialized_view(milestone, '+delete', form=form)
103 self.assertEqual([], view.errors)
104 self.assertEqual(0, len(product.all_milestones))
105
106
81def test_suite():107def test_suite():
82 return unittest.TestLoader().loadTestsFromName(__name__)108 return unittest.TestLoader().loadTestsFromName(__name__)