| | 139 | |
| | 140 | class TestEditingInlineViews(TestCase): |
| | 141 | fixtures = ['admin-views-users.xml'] |
| | 142 | |
| | 143 | def setUp(self): |
| | 144 | result = self.client.login(username='super', password='secret') |
| | 145 | self.failUnlessEqual(result, True) |
| | 146 | # Set up a thing to be modified in the test |
| | 147 | self.thing = Thing.objects.create(description="Parent object") |
| | 148 | self.thing_item_1 = ThingItem.objects.create(description="Item #1", thing=self.thing) |
| | 149 | self.thing_item_2 = ThingItem.objects.create(description="Item #2", thing=self.thing) |
| | 150 | |
| | 151 | def tearDown(self): |
| | 152 | self.client.logout() |
| | 153 | self.thing_item_1.delete() |
| | 154 | self.thing_item_2.delete() |
| | 155 | self.thing.delete() |
| | 156 | |
| | 157 | def test_concurrent_editing_views(self): |
| | 158 | """ |
| | 159 | A ``POST`` to the edit view by two clients simultaneously is properly handled |
| | 160 | |
| | 161 | We are going to simulate two test clients by submitting slightly different data twice. |
| | 162 | This would be equivalent to working with two tabs open in a browser. Individually each of |
| | 163 | these forms is valid but the second one is invalid when submitted one after the other. The |
| | 164 | server needs to handle this case. |
| | 165 | """ |
| | 166 | |
| | 167 | data = [ |
| | 168 | # An existing item has been deleted. |
| | 169 | { |
| | 170 | u'description': [u'A new description'], |
| | 171 | u'thingitem_set-MAX_NUM_FORMS': [u'0'], |
| | 172 | u'thingitem_set-TOTAL_FORMS': [u'2'], |
| | 173 | u'thingitem_set-INITIAL_FORMS': [u'2'], |
| | 174 | u'thingitem_set-0-DELETE': [u''], |
| | 175 | u'thingitem_set-0-id': [u'%s' % (self.thing_item_1.pk,)], |
| | 176 | u'thingitem_set-0-thing': [u'%s' % (self.thing.pk,)], |
| | 177 | u'thingitem_set-0-description': [u'New item #1 description'], |
| | 178 | u'thingitem_set-1-DELETE': [u'on'], |
| | 179 | u'thingitem_set-1-id': [u'%s' % (self.thing_item_2.pk,)], |
| | 180 | u'thingitem_set-1-thing': [u'%s' % (self.thing.pk,)], |
| | 181 | u'thingitem_set-1-description': [u'Deleted item #2 description'], |
| | 182 | }, |
| | 183 | |
| | 184 | # In this second form, we are now attempting to edit the deleted item. This form is |
| | 185 | # invalid but the user submitting it doesn't know that yet. |
| | 186 | { |
| | 187 | u'description': [u'A new description #2'], |
| | 188 | u'thingitem_set-MAX_NUM_FORMS': [u'0'], |
| | 189 | u'thingitem_set-TOTAL_FORMS': [u'2'], |
| | 190 | u'thingitem_set-INITIAL_FORMS': [u'2'], |
| | 191 | u'thingitem_set-0-DELETE': [u''], |
| | 192 | u'thingitem_set-0-id': [u'%s' % (self.thing_item_1.pk,)], |
| | 193 | u'thingitem_set-0-thing': [u'%s' % (self.thing.pk,)], |
| | 194 | u'thingitem_set-0-description': [u'New item #1 description'], |
| | 195 | u'thingitem_set-1-DELETE': [u''], |
| | 196 | u'thingitem_set-1-id': [u'%s' % (self.thing_item_2.pk,)], |
| | 197 | u'thingitem_set-1-thing': [u'%s' % (self.thing.pk,)], |
| | 198 | u'thingitem_set-1-description': [u'Deleted item #2 description'], |
| | 199 | }, |
| | 200 | ] |
| | 201 | edit_url = 'admin:%s_%s_change' %(self.thing._meta.app_label, self.thing._meta.module_name) |
| | 202 | view_url = 'admin:%s_%s_changelist' %(self.thing._meta.app_label, self.thing._meta.module_name) |
| | 203 | |
| | 204 | count = Thing.objects.count() |
| | 205 | response = self.client.post(reverse(edit_url, args=[self.thing.pk]), data[0]) |
| | 206 | |
| | 207 | # Check our first edit was accepted |
| | 208 | self.assertRedirects(response, reverse(view_url), 302, 200) |
| | 209 | |
| | 210 | # Submit the second form. (this might be a second tab in the browser, or a second user) |
| | 211 | response = self.client.post(reverse(edit_url, args=[self.thing.pk]), data[1]) |
| | 212 | |
| | 213 | # Check our second edit was handled |
| | 214 | self.assertRedirects(response, reverse(view_url), 302, 200) |
| | 215 | |
| | 216 | |