""" Tests for signals.py """ from unittest.mock import patch from django.test.utils import override_settings from django.conf import settings from edx_toggles.toggles.testutils import override_waffle_flag from xmodule.modulestore.django import SignalHandler from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory from common.djangoapps.util.storage import resolve_storage_backend from storages.backends.s3boto3 import S3Boto3Storage from django.core.files.storage import storages from .signals import export_course_metadata from .toggles import EXPORT_COURSE_METADATA_FLAG @override_waffle_flag(EXPORT_COURSE_METADATA_FLAG, True) class TestExportCourseMetadata(SharedModuleStoreTestCase): """ Tests for the export_course_metadata function """ ENABLED_SIGNALS = ['course_published'] def setUp(self): super().setUp() SignalHandler.course_published.disconnect(export_course_metadata) self.course = CourseFactory.create(highlights_enabled_for_messaging=True) self.course_key = self.course.id def tearDown(self): super().tearDown() SignalHandler.course_published.disconnect(export_course_metadata) def _create_chapter(self, **kwargs): BlockFactory.create( parent=self.course, category='chapter', **kwargs ) @patch('cms.djangoapps.export_course_metadata.tasks.course_metadata_export_storage') @patch('cms.djangoapps.export_course_metadata.tasks.ContentFile') def test_happy_path(self, patched_content, patched_storage): """ Ensure we call the storage class with the correct parameters and course metadata """ all_highlights = [["week1highlight1", "week1highlight2"], ["week1highlight1", "week1highlight2"], [], []] with self.store.bulk_operations(self.course_key): for week_highlights in all_highlights: self._create_chapter(highlights=week_highlights) SignalHandler.course_published.connect(export_course_metadata) SignalHandler.course_published.send(sender=None, course_key=self.course_key) patched_content.assert_called_once_with( b'{"highlights": [["week1highlight1", "week1highlight2"], ["week1highlight1", "week1highlight2"], [], []]}' ) patched_storage.save.assert_called_once_with( f'course_metadata_export/{self.course_key}.json', patched_content.return_value ) @override_settings( COURSE_METADATA_EXPORT_STORAGE="cms.djangoapps.export_course_metadata.storage.CourseMetadataExportS3Storage", STORAGES={ 'default': { 'BACKEND': "django.core.files.storage.FileSystemStorage" } } ) def test_resolve_default_storage(self): """ Ensure the default storage is invoked, even if course export storage is configured """ storage = storages["default"] self.assertEqual(storage.__class__.__name__, "FileSystemStorage") @override_settings( COURSE_METADATA_EXPORT_STORAGE="cms.djangoapps.export_course_metadata.storage.CourseMetadataExportS3Storage", STORAGES={ "default": { "BACKEND": "django.core.files.storage.FileSystemStorage" } }, COURSE_METADATA_EXPORT_BUCKET="bucket_name_test" ) def test_resolve_happy_path_storage(self): """ Make sure that the correct course export storage is being used """ storage = resolve_storage_backend( storage_key="course_metadata_export_storage", legacy_setting_key="COURSE_METADATA_EXPORT_STORAGE" ) self.assertEqual(storage.__class__.__name__, "CourseMetadataExportS3Storage") self.assertEqual(storage.bucket_name, "bucket_name_test") @override_settings() def test_resolve_storage_with_no_config(self): """ If no storage setup is defined, we get FileSystemStorage by default """ del settings.COURSE_METADATA_EXPORT_STORAGE del settings.COURSE_METADATA_EXPORT_BUCKET storage = resolve_storage_backend( storage_key="course_metadata_export_storage", legacy_setting_key="COURSE_METADATA_EXPORT_STORAGE" ) self.assertEqual(storage.__class__.__name__, "FileSystemStorage") @override_settings( COURSE_METADATA_EXPORT_STORAGE=None, COURSE_METADATA_EXPORT_BUCKET="bucket_name_test", STORAGES={ 'course_metadata_export_storage': { 'BACKEND': 'cms.djangoapps.export_course_metadata.storage.CourseMetadataExportS3Storage', 'OPTIONS': {} } } ) def test_resolve_storage_using_django5_settings(self): """ Simulating a Django 4 environment using Django 5 Storages configuration """ storage = resolve_storage_backend( storage_key="course_metadata_export_storage", legacy_setting_key="COURSE_METADATA_EXPORT_STORAGE" ) self.assertEqual(storage.__class__.__name__, "CourseMetadataExportS3Storage") self.assertEqual(storage.bucket_name, "bucket_name_test") @override_settings( STORAGES={ 'course_metadata_export_storage': { 'BACKEND': 'storages.backends.s3boto3.S3Boto3Storage', 'OPTIONS': { 'bucket_name': 'bucket_name_test' } } } ) def test_resolve_storage_using_django5_settings_with_options(self): """ Ensure we call the storage class with the correct parameters and Django 5 setup """ del settings.COURSE_METADATA_EXPORT_STORAGE del settings.COURSE_METADATA_EXPORT_BUCKET storage = resolve_storage_backend( storage_key="course_metadata_export_storage", legacy_setting_key="COURSE_METADATA_EXPORT_STORAGE" ) self.assertEqual(storage.__class__.__name__, S3Boto3Storage.__name__) self.assertEqual(storage.bucket_name, "bucket_name_test")