diff --git a/tests/settings.py b/tests/settings.py index adeb99786..833e5e670 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -10,3 +10,8 @@ SECRET_KEY = 'hailthesunshine' USE_TZ = True + + +AWS_STORAGE_BUCKET_NAME = "test_bucket" +AWS_ACCESS_KEY_ID = "testing_key_id" +AWS_SECRET_ACCESS_KEY = "testing_access_key" diff --git a/tests/test_s3boto3.py b/tests/test_s3boto3.py index f4415c94e..c500c0bef 100644 --- a/tests/test_s3boto3.py +++ b/tests/test_s3boto3.py @@ -2,16 +2,19 @@ import pickle import threading from datetime import datetime +from io import BytesIO, StringIO from textwrap import dedent from unittest import mock, skipIf from urllib.parse import urlparse +from boto3 import resource from botocore.exceptions import ClientError from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.core.files.base import ContentFile +from django.core.files.base import ContentFile, File from django.test import TestCase, override_settings from django.utils.timezone import is_aware, utc +from moto import mock_s3 from storages.backends import s3boto3 @@ -670,3 +673,59 @@ def test_override_init_argument(self): self.assertEqual(storage.location, 'foo1') storage = s3boto3.S3Boto3Storage(location='foo2') self.assertEqual(storage.location, 'foo2') + + +@mock_s3 +class S3Boto3StorageTestsWithMoto(TestCase): + """ + These tests use the moto library to mock S3, rather than unittest.mock. + This is better because more of boto3's internal code will be run in tests. + + For example this issue + https://github.com/jschneier/django-storages/issues/708 + wouldn't be caught using unittest.mock, since the error occurs in boto3's internals. + + Using mock_s3 as a class decorator automatically decorates methods, + but NOT classmethods or staticmethods. + """ + @classmethod + @mock_s3 + def setUpClass(cls): + super().setUpClass() + # create a bucket specified in settings. + cls.bucket = resource("s3").Bucket(settings.AWS_STORAGE_BUCKET_NAME) + cls.bucket.create() + # create a S3Boto3Storage backend instance. + cls.s3boto3_storage = s3boto3.S3Boto3Storage() + + def test_save_bytes_file(self): + self.s3boto3_storage.save("bytes_file.txt", File(BytesIO(b"foo1"))) + + self.assertEqual( + b"foo1", + self.bucket.Object("bytes_file.txt").get()['Body'].read(), + ) + + def test_save_string_file(self): + self.s3boto3_storage.save("string_file.txt", File(StringIO("foo2"))) + + self.assertEqual( + b"foo2", + self.bucket.Object("string_file.txt").get()['Body'].read(), + ) + + def test_save_bytes_content(self): + self.s3boto3_storage.save("bytes_content.txt", ContentFile(b"foo3")) + + self.assertEqual( + b"foo3", + self.bucket.Object("bytes_content.txt").get()['Body'].read(), + ) + + def test_save_string_content(self): + self.s3boto3_storage.save("string_content.txt", ContentFile("foo4")) + + self.assertEqual( + b"foo4", + self.bucket.Object("string_content.txt").get()['Body'].read(), + )