-
Notifications
You must be signed in to change notification settings - Fork 137
Add tests for file uploads #192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
LinaSSavova
wants to merge
6
commits into
HackSoftware:master
Choose a base branch
from
LinaSSavova:files-tests
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
1233f3e
Add a test for the standard file upload
LinaSSavova bf67c57
Add a test for the Django admin file upload
LinaSSavova 39df660
Code improvements
LinaSSavova b7d0312
Initial direct upload test
LinaSSavova d77c134
Add a test for the direct file upload
LinaSSavova 8257343
Change the client when working with the admin
LinaSSavova File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
96 changes: 91 additions & 5 deletions
96
styleguide_example/files/tests/flows/test_direct_upload.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,95 @@ | ||
from django.test import TestCase | ||
from django.test import TestCase, override_settings | ||
from django.urls import reverse | ||
from django.conf import settings | ||
from django.core.files.uploadedfile import SimpleUploadedFile | ||
|
||
from rest_framework.test import APIClient | ||
|
||
from unittest import mock | ||
|
||
from styleguide_example.files.models import File | ||
|
||
from styleguide_example.users.services import user_create | ||
|
||
from styleguide_example.files.enums import FileUploadStorage | ||
|
||
|
||
class DirectUploadApiTests(TestCase): | ||
""" | ||
We want to test the following: | ||
def setUp(self): | ||
self.client = APIClient() | ||
|
||
self.jwt_login_url = reverse("api:authentication:jwt:login") | ||
self.direct_upload_start_url = reverse("api:files:upload:direct:start") | ||
self.direct_upload_finish_url = reverse("api:files:upload:direct:finish") | ||
self.direct_upload_local_url = lambda file: reverse( | ||
"api:files:upload:direct:local", | ||
kwargs={"file_id": str(file.id)} | ||
) | ||
|
||
@override_settings(FILE_UPLOAD_STORAGE=FileUploadStorage.S3, FILE_MAX_SIZE=10) | ||
def test_direct_upload(self): | ||
""" | ||
1. Get presigned_post_url from the direct_upload_start endpoint | ||
1.1. to mock generate the presigned post | ||
|
||
Assert the presigned data | ||
Assert that the file object is created | ||
|
||
2. Call the finish endpoint and assert that the file is marked as uploaded | ||
|
||
""" | ||
credentials = { | ||
"email": "[email protected]", | ||
"password": "123456" | ||
} | ||
user_create(**credentials) | ||
|
||
response = self.client.post(self.jwt_login_url, credentials) | ||
|
||
self.assertEqual(200, response.status_code) | ||
|
||
token = response.data["token"] | ||
auth_headers = { | ||
"HTTP_AUTHORIZATION": f"{settings.JWT_AUTH['JWT_AUTH_HEADER_PREFIX']} {token}" | ||
} | ||
|
||
file_1 = SimpleUploadedFile( | ||
name="file_small.txt", | ||
content=(settings.FILE_MAX_SIZE - 5) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
file_data = { | ||
"file_name": file_1.name, | ||
"file_type": file_1.content_type | ||
} | ||
|
||
presigned_url = "test_presigned_url" | ||
|
||
presigned_data = { | ||
"url": presigned_url, | ||
} | ||
|
||
with self.subTest("1. Get presigned_post_url from the direct_upload_start endpoint"): | ||
|
||
self.assertEqual(0, File.objects.count()) | ||
|
||
with mock.patch( | ||
"styleguide_example.files.services.s3_generate_presigned_post" | ||
) as s3_generate_presigned_post_mock: | ||
s3_generate_presigned_post_mock.return_value = {**presigned_data} | ||
|
||
response = self.client.post(self.direct_upload_start_url, file_data, **auth_headers) | ||
file_id = response.data["id"] | ||
|
||
self.assertEqual(200, response.status_code) | ||
self.assertEqual(presigned_url, response.data["url"]) | ||
self.assertTrue(s3_generate_presigned_post_mock.called) | ||
self.assertEqual(1, File.objects.count()) | ||
self.assertIsNone(File.objects.last().upload_finished_at) | ||
|
||
with self.subTest("2. Call the finish endpoint and assert that the file is marked as uploaded"): | ||
response = self.client.post(self.direct_upload_finish_url, {"file_id": file_id}) | ||
|
||
1. A start-upload-finish cycle, where we patch the presign generation with local upload storage. | ||
""" | ||
self.assertEqual(200, response.status_code) | ||
self.assertIsNotNone(File.objects.last().upload_finished_at) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,97 @@ | ||
from django.test import TestCase | ||
import shutil | ||
from django.conf import settings | ||
from django.test import TestCase, Client, override_settings | ||
from django.urls import reverse | ||
from django.core.files.uploadedfile import SimpleUploadedFile | ||
|
||
from rest_framework.test import APIClient | ||
|
||
from styleguide_example.files.models import File | ||
|
||
from styleguide_example.users.models import BaseUser | ||
from styleguide_example.users.services import user_create | ||
|
||
|
||
class StandardUploadApiTests(TestCase): | ||
""" | ||
We want to test the following general cases: | ||
|
||
1. Upload a file, below the size limit, assert models gets created accordingly. | ||
1. Upload a file, above the size limit (patch settings), assert API error, nothing gets created. | ||
2. Upload a file, above the size limit (patch settings), assert API error, nothing gets created. | ||
3. Upload a file, equal to the size limit, assert models gets created accordingly. | ||
""" | ||
def setUp(self): | ||
self.client = APIClient() | ||
|
||
self.jwt_login_url = reverse("api:authentication:jwt:login") | ||
self.standard_upload_url = reverse("api:files:upload:standard") | ||
|
||
@override_settings(FILE_MAX_SIZE=10) | ||
def test_standard_upload(self): | ||
|
||
self.assertEqual(0, File.objects.count()) | ||
self.assertEqual(0, BaseUser.objects.count()) | ||
|
||
# Create a user | ||
credentials = { | ||
"email": "[email protected]", | ||
"password": "123456" | ||
} | ||
user_create(**credentials) | ||
|
||
self.assertEqual(1, BaseUser.objects.count()) | ||
LinaSSavova marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# Log in and get the authorization data needed | ||
response = self.client.post(self.jwt_login_url, credentials) | ||
|
||
self.assertEqual(200, response.status_code) | ||
slavov-v marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
token = response.data["token"] | ||
auth_headers = { | ||
"HTTP_AUTHORIZATION": f"{settings.JWT_AUTH['JWT_AUTH_HEADER_PREFIX']} {token}" | ||
} | ||
|
||
# Create a small sized file | ||
file_1 = SimpleUploadedFile( | ||
name="file_small.txt", | ||
content=(settings.FILE_MAX_SIZE - 5) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
with self.subTest("1. Upload a file, below the size limit, assert models gets created accordingly"): | ||
response = self.client.post(self.standard_upload_url, {"file": file_1}, **auth_headers) | ||
|
||
self.assertEqual(201, response.status_code) | ||
self.assertEqual(1, File.objects.count()) | ||
|
||
# Create a file above the size limit | ||
file_2 = SimpleUploadedFile( | ||
name="file_big.txt", | ||
content=(settings.FILE_MAX_SIZE + 1) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
with self.subTest("2. Upload a file, above the size limit, assert API error, nothing gets created"): | ||
response = self.client.post(self.standard_upload_url, {"file": file_2}, **auth_headers) | ||
|
||
self.assertEqual(400, response.status_code) | ||
self.assertEqual(1, File.objects.count()) | ||
|
||
# Create a file equal to the size limit | ||
file_3 = SimpleUploadedFile( | ||
name="file_equal.txt", | ||
content=settings.FILE_MAX_SIZE * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
with self.subTest("3. Upload a file, equal to the size limit, assert models gets created accordingly"): | ||
response = self.client.post(self.standard_upload_url, {"file": file_3}, **auth_headers) | ||
|
||
self.assertEqual(201, response.status_code) | ||
self.assertEqual(2, File.objects.count()) | ||
|
||
def tearDown(self): | ||
shutil.rmtree(settings.MEDIA_ROOT, ignore_errors=True) | ||
|
||
|
||
class StandardUploadAdminTests(TestCase): | ||
|
@@ -24,3 +108,111 @@ class StandardUploadAdminTests(TestCase): | |
1. Create a new file via the Django admin, assert error, nothing gets created. | ||
2. Update an existing fila via the Django admin, assert error, nothing gets created. | ||
""" | ||
def setUp(self): | ||
self.client = Client() | ||
|
||
self.admin_upload_file_url = reverse("admin:files_file_add") | ||
self.admin_files_list_url = reverse("admin:files_file_changelist") | ||
self.admin_update_file_url = lambda file: reverse( | ||
"admin:files_file_change", | ||
kwargs={"object_id": str(file.id)} | ||
) | ||
|
||
@override_settings(FILE_MAX_SIZE=10) | ||
def test_standard_admin_upload_and_update(self): | ||
|
||
self.assertEqual(0, File.objects.count()) | ||
|
||
# Create a superuser | ||
credentials = { | ||
"email": "[email protected]", | ||
"password": "123456", | ||
"is_admin": True, | ||
"is_superuser": True | ||
} | ||
user = BaseUser.objects.create(**credentials) | ||
|
||
self.assertEqual(1, BaseUser.objects.count()) | ||
|
||
file_1 = SimpleUploadedFile( | ||
name="first_file.txt", | ||
content=(settings.FILE_MAX_SIZE - 5) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
data_file_1 = { | ||
"file": file_1, | ||
"uploaded_by": user.id | ||
} | ||
|
||
# Log in with the superuser account | ||
self.client.force_login(user) | ||
|
||
with self.subTest("1. Create a new file via the Django admin, assert everything gets created"): | ||
response = self.client.post(self.admin_upload_file_url, data_file_1) | ||
successfully_uploaded_file = File.objects.last() | ||
|
||
self.assertEqual(302, response.status_code) | ||
self.assertEqual(1, File.objects.count()) | ||
self.assertEqual(file_1.name, successfully_uploaded_file.original_file_name) | ||
|
||
file_2 = SimpleUploadedFile( | ||
name="second_file.txt", | ||
content=(settings.FILE_MAX_SIZE - 1) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
data_file_2 = { | ||
"file": file_2, | ||
"uploaded_by": user.id | ||
} | ||
|
||
with self.subTest("2. Update an existing file via the Django admin, assert everything gets updated"): | ||
response = self.client.post(self.admin_update_file_url(successfully_uploaded_file), data_file_2) | ||
|
||
self.assertEqual(302, response.status_code) | ||
self.assertEqual(1, File.objects.count()) | ||
self.assertEqual(file_2.name, File.objects.last().original_file_name) | ||
|
||
file_3 = SimpleUploadedFile( | ||
name="oversized_file.txt", | ||
content=(settings.FILE_MAX_SIZE + 1) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
data_oversized_file = { | ||
"file": file_3, | ||
"uploaded_by": user.id | ||
} | ||
|
||
with self.subTest("3. Create a new oversized file via the Django admin, assert error, nothing gets created"): | ||
response = self.client.post(self.admin_upload_file_url, data_oversized_file, follow=True) | ||
|
||
self.assertContains(response, "File is too large") | ||
self.assertEqual(1, File.objects.count()) | ||
self.assertEqual(file_2.name, File.objects.last().original_file_name) | ||
|
||
file_4 = SimpleUploadedFile( | ||
name="new_oversized_file.txt", | ||
content=(settings.FILE_MAX_SIZE + 1) * "a".encode(), | ||
content_type="text/plain" | ||
) | ||
|
||
data_new_oversized_file = { | ||
"file": file_4, | ||
"uploaded_by": user.id | ||
} | ||
|
||
with self.subTest( | ||
"4. Update an existing file with an oversized one via the Django admin, assert error, nothing gets created" | ||
): | ||
response = self.client.post( | ||
self.admin_update_file_url(File.objects.last()), data_new_oversized_file, follow=True | ||
) | ||
|
||
self.assertContains(response, "File is too large") | ||
self.assertEqual(1, File.objects.count()) | ||
self.assertEqual(file_2.name, File.objects.last().original_file_name) | ||
|
||
def tearDown(self): | ||
shutil.rmtree(settings.MEDIA_ROOT, ignore_errors=True) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.