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
1=== modified file 'lib/lp/registry/browser/__init__.py'
2--- lib/lp/registry/browser/__init__.py 2010-06-12 03:37:58 +0000
3+++ lib/lp/registry/browser/__init__.py 2010-08-10 02:14:45 +0000
4@@ -115,10 +115,11 @@
5 var create_milestone_link = Y.get(
6 '.menu-link-create_milestone');
7 create_milestone_link.addClass('js-action');
8+ var milestone_table = Y.lp.registry.milestonetable;
9 var config = {
10 milestone_form_uri: milestone_form_uri,
11 series_uri: series_uri,
12- next_step: Y.lp.registry.milestonetable.get_milestone_row,
13+ next_step: milestone_table.get_milestone_row,
14 activate_node: create_milestone_link
15 };
16 Y.lp.registry.milestoneoverlay.attach_widget(config);
17@@ -216,7 +217,10 @@
18 """Delete a milestone and unlink related objects."""
19 self._unsubscribe_structure(milestone)
20 for bugtask in self._getBugtasks(milestone):
21- bugtask.milestone = None
22+ if bugtask.conjoined_master is not None:
23+ Store.of(bugtask).remove(bugtask)
24+ else:
25+ bugtask.milestone = None
26 for spec in self._getSpecifications(milestone):
27 spec.milestone = None
28 self._deleteRelease(milestone.product_release)
29
30=== modified file 'lib/lp/registry/browser/tests/test_milestone.py'
31--- lib/lp/registry/browser/tests/test_milestone.py 2010-08-06 20:15:31 +0000
32+++ lib/lp/registry/browser/tests/test_milestone.py 2010-08-10 02:14:45 +0000
33@@ -7,7 +7,11 @@
34
35 import unittest
36
37-from lp.testing import ANONYMOUS, login_person, login
38+from zope.component import getUtility
39+
40+from canonical.testing.layers import DatabaseFunctionalLayer
41+from lp.bugs.interfaces.bugtask import IBugTaskSet
42+from lp.testing import ANONYMOUS, login_person, login, TestCaseWithFactory
43 from lp.testing.views import create_initialized_view
44 from lp.testing.memcache import MemcacheTestCase
45
46@@ -78,5 +82,27 @@
47 self.assertEqual(360, view.expire_cache_minutes)
48
49
50+class TestMilestoneDeleteView(TestCaseWithFactory):
51+ """Test the delete rules applied by the Milestone Delete view."""
52+
53+ layer = DatabaseFunctionalLayer
54+
55+ def test_delete_conjoined_bugtask(self):
56+ product = self.factory.makeProduct()
57+ bug = self.factory.makeBug(product=product)
58+ master_bugtask = getUtility(IBugTaskSet).createTask(
59+ bug, productseries=product.development_focus, owner=product.owner)
60+ milestone = self.factory.makeMilestone(
61+ productseries=product.development_focus)
62+ login_person(product.owner)
63+ master_bugtask.transitionToMilestone(milestone, product.owner)
64+ form = {
65+ 'field.actions.delete': 'Delete Milestone',
66+ }
67+ view = create_initialized_view(milestone, '+delete', form=form)
68+ self.assertEqual([], view.errors)
69+ self.assertEqual(0, len(product.all_milestones))
70+
71+
72 def test_suite():
73 return unittest.TestLoader().loadTestsFromName(__name__)