Drafts system implemented
This commit is contained in:
+147
-25
@@ -79,18 +79,21 @@ class UGCProjectTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
self.bob = User.objects.create_user(username='Bob', password='pw')
|
||||
UserProfile.objects.get_or_create(user=self.bob)
|
||||
|
||||
def _upload_temp(self, kind, name, content, content_type):
|
||||
def _upload_temp(self, kind, name, content, content_type, draft_uuid=None):
|
||||
data = {'kind': kind, 'file': SimpleUploadedFile(name, content, content_type=content_type)}
|
||||
if draft_uuid is not None:
|
||||
data['draft_uuid'] = str(draft_uuid)
|
||||
return self.client.post(
|
||||
reverse('library:api_upload_temp'),
|
||||
{'kind': kind, 'file': SimpleUploadedFile(name, content, content_type=content_type)},
|
||||
reverse('library:api_upload_temp'), data,
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
)
|
||||
|
||||
def _create_project(self, user, title='Test Pack', tags='general:furry content:threesome'):
|
||||
self.gate()
|
||||
self.client.login(username=user.username, password='pw')
|
||||
self._upload_temp('thumbnail', 'thumb.png', make_png_bytes(), 'image/png')
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip')
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
self._upload_temp('thumbnail', 'thumb.png', make_png_bytes(), 'image/png', draft_uuid=uuid)
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip', draft_uuid=uuid)
|
||||
data = {
|
||||
'title': title,
|
||||
'summary': 'A test pack.',
|
||||
@@ -100,11 +103,11 @@ class UGCProjectTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
'version_name': '1.0.0',
|
||||
'changelog': 'First release.',
|
||||
}
|
||||
return self.client.post(reverse('library:project_create'), data)
|
||||
return self.client.post(reverse('library:project_create', args=[uuid]), data)
|
||||
|
||||
def test_create_requires_login(self):
|
||||
self.gate()
|
||||
resp = self.client.get(reverse('library:project_create'))
|
||||
resp = self.client.get(reverse('library:project_create_new'))
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
|
||||
def test_create_project(self):
|
||||
@@ -135,7 +138,8 @@ class UGCProjectTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
def test_create_requires_version_file(self):
|
||||
self.gate()
|
||||
self.client.login(username='Alice', password='pw')
|
||||
resp = self.client.post(reverse('library:project_create'), {
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
resp = self.client.post(reverse('library:project_create', args=[uuid]), {
|
||||
'title': 'No File',
|
||||
'category': 'mod',
|
||||
'version_name': '1.0.0',
|
||||
@@ -146,16 +150,17 @@ class UGCProjectTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
def test_create_adopts_media_and_captions(self):
|
||||
self.gate()
|
||||
self.client.login(username='Alice', password='pw')
|
||||
self._upload_temp('thumbnail', 'thumb.png', make_png_bytes(), 'image/png')
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip')
|
||||
self._upload_temp('media', 'pic1.png', make_png_bytes(), 'image/png')
|
||||
self._upload_temp('media', 'pic2.png', make_png_bytes(), 'image/png')
|
||||
media = TempUpload.objects.filter(kind='media').order_by('created_at')
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
self._upload_temp('thumbnail', 'thumb.png', make_png_bytes(), 'image/png', draft_uuid=uuid)
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip', draft_uuid=uuid)
|
||||
self._upload_temp('media', 'pic1.png', make_png_bytes(), 'image/png', draft_uuid=uuid)
|
||||
self._upload_temp('media', 'pic2.png', make_png_bytes(), 'image/png', draft_uuid=uuid)
|
||||
media = TempUpload.objects.filter(kind='media', draft_uuid=uuid).order_by('created_at')
|
||||
caption_payload = {
|
||||
f'caption_{media[0].uuid}': 'Showcase',
|
||||
f'caption_{media[1].uuid}': 'Second angle',
|
||||
}
|
||||
resp = self.client.post(reverse('library:project_create'), {
|
||||
resp = self.client.post(reverse('library:project_create', args=[uuid]), {
|
||||
'title': 'Media Pack',
|
||||
'category': 'skin',
|
||||
'version_name': '1.0.0',
|
||||
@@ -192,6 +197,119 @@ class UGCProjectTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
|
||||
class UGCProjectDraftTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
"""Multiple per-user drafts, keyed by UUID, with upload scoping."""
|
||||
|
||||
def setUp(self):
|
||||
User = get_user_model()
|
||||
self.alice = User.objects.create_user(username='Alice', password='pw')
|
||||
UserProfile.objects.get_or_create(user=self.alice)
|
||||
self.bob = User.objects.create_user(username='Bob', password='pw')
|
||||
UserProfile.objects.get_or_create(user=self.bob)
|
||||
|
||||
def _auth(self, user):
|
||||
self.client.force_login(user)
|
||||
self.gate()
|
||||
|
||||
def _upload_temp(self, kind, name, content, content_type, draft_uuid=None):
|
||||
data = {'kind': kind, 'file': SimpleUploadedFile(name, content, content_type=content_type)}
|
||||
if draft_uuid is not None:
|
||||
data['draft_uuid'] = str(draft_uuid)
|
||||
return self.client.post(
|
||||
reverse('library:api_upload_temp'), data,
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
)
|
||||
|
||||
def _new_draft(self, user=None):
|
||||
self._auth(user or self.alice)
|
||||
resp = self.client.get(reverse('library:project_create_new'))
|
||||
uuid = resp.url.rstrip('/').split('/')[-1]
|
||||
return uuid, resp
|
||||
|
||||
def test_new_project_creates_draft_and_redirects(self):
|
||||
uuid, resp = self._new_draft()
|
||||
self.assertRedirects(
|
||||
resp, reverse('library:project_create', args=[uuid]),
|
||||
fetch_redirect_response=False,
|
||||
)
|
||||
self.assertTrue(ProjectDraft.objects.filter(uuid=uuid, user=self.alice).exists())
|
||||
|
||||
def test_my_projects_lists_projects_and_drafts(self):
|
||||
project = Project.objects.create(
|
||||
slug='mine', title='Mine', category='mod', owner=self.alice,
|
||||
)
|
||||
uuid, _ = self._new_draft()
|
||||
self._auth(self.alice)
|
||||
resp = self.client.get(reverse('library:my_projects'))
|
||||
self.assertContains(resp, 'Mine')
|
||||
self.assertContains(resp, 'Untitled draft')
|
||||
self.assertEqual(Project.objects.get(pk=project.pk).title, 'Mine')
|
||||
|
||||
def test_wrong_user_cannot_access_draft(self):
|
||||
uuid, _ = self._new_draft()
|
||||
self._auth(self.bob)
|
||||
resp = self.client.get(reverse('library:project_create', args=[uuid]))
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
self.assertEqual(resp.url, reverse('library:my_projects'))
|
||||
# API save also refused.
|
||||
resp = self.client.post(
|
||||
reverse('library:api_draft'),
|
||||
data=json.dumps({'draft': uuid, 'data': {'title': 'hack'}}),
|
||||
content_type='application/json',
|
||||
)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
draft = ProjectDraft.objects.get(uuid=uuid)
|
||||
self.assertNotEqual(draft.data.get('title'), 'hack')
|
||||
|
||||
def test_autosave_scopes_to_its_draft(self):
|
||||
uuid_a, _ = self._new_draft()
|
||||
uuid_b, _ = self._new_draft()
|
||||
self._auth(self.alice)
|
||||
self.client.post(
|
||||
reverse('library:api_draft'),
|
||||
data=json.dumps({'draft': uuid_a, 'data': {'title': 'Draft A'}}),
|
||||
content_type='application/json',
|
||||
)
|
||||
self.assertEqual(ProjectDraft.objects.get(uuid=uuid_a).data.get('title'), 'Draft A')
|
||||
self.assertEqual(ProjectDraft.objects.get(uuid=uuid_b).data.get('title'), None)
|
||||
|
||||
def test_uploads_scoped_per_draft(self):
|
||||
uuid_a, _ = self._new_draft()
|
||||
uuid_b, _ = self._new_draft()
|
||||
self._auth(self.alice)
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip', draft_uuid=uuid_a)
|
||||
self.assertEqual(TempUpload.objects.filter(draft_uuid=uuid_a).count(), 1)
|
||||
self.assertEqual(TempUpload.objects.filter(draft_uuid=uuid_b).count(), 0)
|
||||
self.assertEqual(TempUpload.objects.filter(draft_uuid=uuid_a, status='pending').count(), 1)
|
||||
self.assertEqual(TempUpload.objects.filter(draft_uuid=uuid_b, status='pending').count(), 0)
|
||||
|
||||
def test_submit_consumes_draft_uploads_and_deletes_draft(self):
|
||||
uuid, _ = self._new_draft()
|
||||
self._auth(self.alice)
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip', draft_uuid=uuid)
|
||||
resp = self.client.post(
|
||||
reverse('library:project_create', args=[uuid]),
|
||||
{
|
||||
'title': 'From Draft', 'summary': 's', 'description': 'd',
|
||||
'category': 'mod', 'tags': 'general:test',
|
||||
'version_name': '1.0.0', 'changelog': 'c',
|
||||
},
|
||||
)
|
||||
self.assertRedirects(resp, reverse('library:project_detail', args=['from-draft']))
|
||||
self.assertTrue(Project.objects.filter(title='From Draft', owner=self.alice).exists())
|
||||
self.assertFalse(ProjectDraft.objects.filter(uuid=uuid).exists())
|
||||
self.assertEqual(TempUpload.objects.filter(draft_uuid=uuid, status='used').count(), 1)
|
||||
|
||||
def test_draft_delete_purges_uploads(self):
|
||||
uuid, _ = self._new_draft()
|
||||
self._auth(self.alice)
|
||||
self._upload_temp('version', 'pack.zip', make_zip_bytes(), 'application/zip', draft_uuid=uuid)
|
||||
resp = self.client.post(reverse('library:project_draft_delete', args=[uuid]))
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
self.assertFalse(ProjectDraft.objects.filter(uuid=uuid).exists())
|
||||
self.assertFalse(TempUpload.objects.filter(draft_uuid=uuid).exists())
|
||||
|
||||
|
||||
class UGCVersionTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
def setUp(self):
|
||||
User = get_user_model()
|
||||
@@ -585,7 +703,8 @@ class UGCDraftTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
def test_draft_save_and_restore(self):
|
||||
self.gate()
|
||||
self.client.login(username='Alice', password='pw')
|
||||
self._post_json(reverse('library:api_draft'), {'data': {
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
self._post_json(reverse('library:api_draft'), {'draft': uuid, 'data': {
|
||||
'title': 'Draft Title',
|
||||
'summary': 'Draft summary',
|
||||
'category': 'mod',
|
||||
@@ -593,34 +712,36 @@ class UGCDraftTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
'tags': 'species:dragon',
|
||||
'version_name': '2.0.0',
|
||||
}})
|
||||
draft = ProjectDraft.objects.get(user=self.alice)
|
||||
draft = ProjectDraft.objects.get(uuid=uuid, user=self.alice)
|
||||
self.assertEqual(draft.data['title'], 'Draft Title')
|
||||
|
||||
resp = self.client.get(reverse('library:api_draft'))
|
||||
resp = self.client.get(reverse('library:api_draft'), {'draft': uuid})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.json()['data']['title'], 'Draft Title')
|
||||
|
||||
def test_create_form_prefills_draft(self):
|
||||
self.gate()
|
||||
self.client.login(username='Alice', password='pw')
|
||||
self._post_json(reverse('library:api_draft'), {'data': {'title': 'My Draft Pack'}})
|
||||
resp = self.client.get(reverse('library:project_create'))
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
self._post_json(reverse('library:api_draft'), {'draft': uuid, 'data': {'title': 'My Draft Pack'}})
|
||||
resp = self.client.get(reverse('library:project_create', args=[uuid]))
|
||||
self.assertContains(resp, 'value="My Draft Pack"')
|
||||
|
||||
def test_create_clears_draft(self):
|
||||
self.gate()
|
||||
self.client.login(username='Alice', password='pw')
|
||||
self._post_json(reverse('library:api_draft'), {'data': {'title': 'My Draft Pack'}})
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
self._post_json(reverse('library:api_draft'), {'draft': uuid, 'data': {'title': 'My Draft Pack'}})
|
||||
self.client.post(
|
||||
reverse('library:api_upload_temp'),
|
||||
{'kind': 'version', 'file': SimpleUploadedFile('pack.zip', make_zip_bytes(), content_type='application/zip')},
|
||||
{'kind': 'version', 'file': SimpleUploadedFile('pack.zip', make_zip_bytes(), content_type='application/zip'), 'draft_uuid': uuid},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
|
||||
)
|
||||
resp = self.client.post(reverse('library:project_create'), {
|
||||
resp = self.client.post(reverse('library:project_create', args=[uuid]), {
|
||||
'title': 'My Draft Pack', 'category': 'mod', 'version_name': '1.0.0',
|
||||
})
|
||||
self.assertRedirects(resp, reverse('library:project_detail', args=['my-draft-pack']))
|
||||
self.assertFalse(ProjectDraft.objects.filter(user=self.alice).exists())
|
||||
self.assertFalse(ProjectDraft.objects.filter(uuid=uuid).exists())
|
||||
|
||||
|
||||
class UGCPermissionTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
@@ -1825,10 +1946,11 @@ class UGCAnimationIdTests(UGCMediaTestCase, UGCGatedTestCase):
|
||||
def test_create_injects_defaults(self):
|
||||
self.gate()
|
||||
self.client.login(username='Alice', password='pw')
|
||||
uuid = self.client.get(reverse('library:project_create_new')).url.rstrip('/').split('/')[-1]
|
||||
self.client.post(reverse('library:api_upload_temp'),
|
||||
{'kind': 'version', 'file': SimpleUploadedFile('pack.zip', _logical_zip(), content_type='application/zip')},
|
||||
{'kind': 'version', 'file': SimpleUploadedFile('pack.zip', _logical_zip(), content_type='application/zip'), 'draft_uuid': uuid},
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
resp = self.client.post(reverse('library:project_create'), {
|
||||
resp = self.client.post(reverse('library:project_create', args=[uuid]), {
|
||||
'title': 'New Pack', 'category': 'non_pack', 'version_name': '1.0.0',
|
||||
})
|
||||
self.assertRedirects(resp, reverse('library:project_detail', args=['new-pack']))
|
||||
|
||||
Reference in New Issue
Block a user