def test_symlink(self): if symlinks_supported(): os.symlink(os.path.join(self.test_dir, 'templates'), self.symlinked_dir) else: self.skipTest("os.symlink() not available on this OS + Python version combination.") management.call_command('makemessages', locale=[LOCALE], verbosity=0, symlinks=True) self.assertTrue(os.path.exists(self.PO_FILE)) with open(self.PO_FILE) as fp: po_contents = fp.read() self.assertMsgId('This literal should be included.', po_contents) self.assertLocationCommentPresent(self.PO_FILE, None, 'templates_symlinked', 'test.html')
def test_symlink(self): if symlinks_supported(): os.symlink(os.path.join(self.test_dir, 'templates'), self.symlinked_dir) else: self.skipTest("os.symlink() not available on this OS + Python version combination.") management.call_command('makemessages', locale=[LOCALE], verbosity=0, symlinks=True) self.assertTrue(os.path.exists(self.PO_FILE)) with open(self.PO_FILE) as fp: po_contents = fp.read() self.assertMsgId('This literal should be included.', po_contents) self.assertLocationCommentPresent(self.PO_FILE, None, 'templates_symlinked', 'test.html')
@override_settings(STATICFILES_STORAGE='staticfiles_tests.storage.NeverCopyRemoteStorage') def test_skips_newer_files_in_remote_storage(self): """ collectstatic skips newer files in a remote storage. run_collectstatic() in setUp() copies the static files, then files are always skipped after NeverCopyRemoteStorage is activated since NeverCopyRemoteStorage.get_modified_time() returns a datetime in the future to simulate an unmodified file. """ stdout = StringIO() self.run_collectstatic(stdout=stdout, verbosity=2) output = stdout.getvalue() self.assertIn("Skipping 'test.txt' (not modified)", output) @unittest.skipUnless(symlinks_supported(), "Must be able to symlink to run this test.") class TestCollectionLinks(TestDefaults, CollectionTestCase): """ Test ``--link`` option for ``collectstatic`` management command. Note that by inheriting ``TestDefaults`` we repeat all the standard file resolving tests here, to make sure using ``--link`` does not change the file-selection semantics. """ def run_collectstatic(self, clear=False, link=True, **kwargs): super().run_collectstatic(link=link, clear=clear, **kwargs) def test_links_created(self): """ With ``--link``, symbolic links are created. """
"/static/cached/styles.deploy12345.css") self.assertStaticRenders("path/", "/static/path/") self.assertStaticRenders("path/?query", "/static/path/?query") def test_template_tag_simple_content(self): relpath = self.hashed_file_path("cached/styles.css") self.assertEqual(relpath, "cached/styles.deploy12345.css") with storage.staticfiles_storage.open(relpath) as relfile: content = relfile.read() self.assertNotIn(b"cached/other.css", content) self.assertIn(b"other.deploy12345.css", content) @unittest.skipUnless(symlinks_supported(), "Must be able to symlink to run this test.") class TestCollectionLinks(CollectionTestCase, TestDefaults): """ Test ``--link`` option for ``collectstatic`` management command. Note that by inheriting ``TestDefaults`` we repeat all the standard file resolving tests here, to make sure using ``--link`` does not change the file-selection semantics. """ def run_collectstatic(self): super(TestCollectionLinks, self).run_collectstatic(link=True) def test_links_created(self): """ With ``--link``, symbolic links are created.
# Make sure the warning went away again. with self.settings(STATICFILES_DIRS=[static_dir]): output = self._collectstatic_output(clear=True) self.assertNotIn(self.warning_string, output) @override_settings(STATICFILES_STORAGE='staticfiles_tests.storage.DummyStorage') class TestCollectionNonLocalStorage(CollectionTestCase, TestNoFilesCreated): """ Tests for #15035 """ pass @unittest.skipUnless(symlinks_supported(), "Must be able to symlink to run this test.") class TestCollectionLinks(CollectionTestCase, TestDefaults): """ Test ``--link`` option for ``collectstatic`` management command. Note that by inheriting ``TestDefaults`` we repeat all the standard file resolving tests here, to make sure using ``--link`` does not change the file-selection semantics. """ def run_collectstatic(self, clear=False): super(TestCollectionLinks, self).run_collectstatic(link=True, clear=clear) def test_links_created(self): """ With ``--link``, symbolic links are created. """
class FileStorageTests(SimpleTestCase): storage_class = FileSystemStorage def setUp(self): self.temp_dir = tempfile.mkdtemp() self.storage = self.storage_class(location=self.temp_dir, base_url='/test_media_url/') # Set up a second temporary directory which is ensured to have a mixed # case name. self.temp_dir2 = tempfile.mkdtemp(suffix='aBc') def tearDown(self): shutil.rmtree(self.temp_dir) shutil.rmtree(self.temp_dir2) def test_empty_location(self): """ Makes sure an exception is raised if the location is empty """ storage = self.storage_class(location='') self.assertEqual(storage.base_location, '') self.assertEqual(storage.location, os.getcwd()) def test_file_access_options(self): """ Standard file access options are available, and work as expected. """ self.assertFalse(self.storage.exists('storage_test')) f = self.storage.open('storage_test', 'w') f.write('storage contents') f.close() self.assertTrue(self.storage.exists('storage_test')) f = self.storage.open('storage_test', 'r') self.assertEqual(f.read(), 'storage contents') f.close() self.storage.delete('storage_test') self.assertFalse(self.storage.exists('storage_test')) def _test_file_time_getter(self, getter): # Check for correct behavior under both USE_TZ=True and USE_TZ=False. # The tests are similar since they both set up a situation where the # system time zone, Django's TIME_ZONE, and UTC are distinct. self._test_file_time_getter_tz_handling_on(getter) self._test_file_time_getter_tz_handling_off(getter) @override_settings(USE_TZ=True, TIME_ZONE='Africa/Algiers') def _test_file_time_getter_tz_handling_on(self, getter): # Django's TZ (and hence the system TZ) is set to Africa/Algiers which # is UTC+1 and has no DST change. We can set the Django TZ to something # else so that UTC, Django's TIME_ZONE, and the system timezone are all # different. now_in_algiers = timezone.make_aware(datetime.now()) with timezone.override(timezone.get_fixed_timezone(-300)): # At this point the system TZ is +1 and the Django TZ # is -5. The following will be aware in UTC. now = timezone.now() self.assertFalse(self.storage.exists('test.file.tz.on')) f = ContentFile('custom contents') f_name = self.storage.save('test.file.tz.on', f) self.addCleanup(self.storage.delete, f_name) dt = getter(f_name) # dt should be aware, in UTC self.assertTrue(timezone.is_aware(dt)) self.assertEqual(now.tzname(), dt.tzname()) # The three timezones are indeed distinct. naive_now = datetime.now() algiers_offset = now_in_algiers.tzinfo.utcoffset(naive_now) django_offset = timezone.get_current_timezone().utcoffset(naive_now) utc_offset = timezone.utc.utcoffset(naive_now) self.assertGreater(algiers_offset, utc_offset) self.assertLess(django_offset, utc_offset) # dt and now should be the same effective time. self.assertLess(abs(dt - now), timedelta(seconds=2)) @override_settings(USE_TZ=False, TIME_ZONE='Africa/Algiers') def _test_file_time_getter_tz_handling_off(self, getter): # Django's TZ (and hence the system TZ) is set to Africa/Algiers which # is UTC+1 and has no DST change. We can set the Django TZ to something # else so that UTC, Django's TIME_ZONE, and the system timezone are all # different. now_in_algiers = timezone.make_aware(datetime.now()) with timezone.override(timezone.get_fixed_timezone(-300)): # At this point the system TZ is +1 and the Django TZ # is -5. self.assertFalse(self.storage.exists('test.file.tz.off')) f = ContentFile('custom contents') f_name = self.storage.save('test.file.tz.off', f) self.addCleanup(self.storage.delete, f_name) dt = getter(f_name) # dt should be naive, in system (+1) TZ self.assertTrue(timezone.is_naive(dt)) # The three timezones are indeed distinct. naive_now = datetime.now() algiers_offset = now_in_algiers.tzinfo.utcoffset(naive_now) django_offset = timezone.get_current_timezone().utcoffset(naive_now) utc_offset = timezone.utc.utcoffset(naive_now) self.assertGreater(algiers_offset, utc_offset) self.assertLess(django_offset, utc_offset) # dt and naive_now should be the same effective time. self.assertLess(abs(dt - naive_now), timedelta(seconds=2)) # If we convert dt to an aware object using the Algiers # timezone then it should be the same effective time to # now_in_algiers. _dt = timezone.make_aware(dt, now_in_algiers.tzinfo) self.assertLess(abs(_dt - now_in_algiers), timedelta(seconds=2)) def test_file_get_accessed_time(self): """ File storage returns a Datetime object for the last accessed time of a file. """ self.assertFalse(self.storage.exists('test.file')) f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) self.addCleanup(self.storage.delete, f_name) atime = self.storage.get_accessed_time(f_name) self.assertEqual(atime, datetime.fromtimestamp(os.path.getatime(self.storage.path(f_name)))) self.assertLess(timezone.now() - self.storage.get_accessed_time(f_name), timedelta(seconds=2)) @requires_tz_support def test_file_get_accessed_time_timezone(self): self._test_file_time_getter(self.storage.get_accessed_time) def test_file_get_created_time(self): """ File storage returns a datetime for the creation time of a file. """ self.assertFalse(self.storage.exists('test.file')) f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) self.addCleanup(self.storage.delete, f_name) ctime = self.storage.get_created_time(f_name) self.assertEqual(ctime, datetime.fromtimestamp(os.path.getctime(self.storage.path(f_name)))) self.assertLess(timezone.now() - self.storage.get_created_time(f_name), timedelta(seconds=2)) @requires_tz_support def test_file_get_created_time_timezone(self): self._test_file_time_getter(self.storage.get_created_time) def test_file_get_modified_time(self): """ File storage returns a datetime for the last modified time of a file. """ self.assertFalse(self.storage.exists('test.file')) f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) self.addCleanup(self.storage.delete, f_name) mtime = self.storage.get_modified_time(f_name) self.assertEqual(mtime, datetime.fromtimestamp(os.path.getmtime(self.storage.path(f_name)))) self.assertLess(timezone.now() - self.storage.get_modified_time(f_name), timedelta(seconds=2)) @requires_tz_support def test_file_get_modified_time_timezone(self): self._test_file_time_getter(self.storage.get_modified_time) def test_file_save_without_name(self): """ File storage extracts the filename from the content object if no name is given explicitly. """ self.assertFalse(self.storage.exists('test.file')) f = ContentFile('custom contents') f.name = 'test.file' storage_f_name = self.storage.save(None, f) self.assertEqual(storage_f_name, f.name) self.assertTrue(os.path.exists(os.path.join(self.temp_dir, f.name))) self.storage.delete(storage_f_name) def test_file_save_with_path(self): """ Saving a pathname should create intermediate directories as necessary. """ self.assertFalse(self.storage.exists('path/to')) self.storage.save('path/to/test.file', ContentFile('file saved with path')) self.assertTrue(self.storage.exists('path/to')) with self.storage.open('path/to/test.file') as f: self.assertEqual(f.read(), b'file saved with path') self.assertTrue(os.path.exists( os.path.join(self.temp_dir, 'path', 'to', 'test.file'))) self.storage.delete('path/to/test.file') def test_file_save_abs_path(self): test_name = 'path/to/test.file' f = ContentFile('file saved with path') f_name = self.storage.save(os.path.join(self.temp_dir, test_name), f) self.assertEqual(f_name, test_name) @unittest.skipUnless(symlinks_supported(), 'Must be able to symlink to run this test.') def test_file_save_broken_symlink(self): """A new path is created on save when a broken symlink is supplied.""" nonexistent_file_path = os.path.join(self.temp_dir, 'nonexistent.txt') broken_symlink_path = os.path.join(self.temp_dir, 'symlink.txt') os.symlink(nonexistent_file_path, broken_symlink_path) f = ContentFile('some content') f_name = self.storage.save(broken_symlink_path, f) self.assertIs(os.path.exists(os.path.join(self.temp_dir, f_name)), True) def test_save_doesnt_close(self): with TemporaryUploadedFile('test', 'text/plain', 1, 'utf8') as file: file.write(b'1') file.seek(0) self.assertFalse(file.closed) self.storage.save('path/to/test.file', file) self.assertFalse(file.closed) self.assertFalse(file.file.closed) file = InMemoryUploadedFile(StringIO('1'), '', 'test', 'text/plain', 1, 'utf8') with file: self.assertFalse(file.closed) self.storage.save('path/to/test.file', file) self.assertFalse(file.closed) self.assertFalse(file.file.closed) def test_file_path(self): """ File storage returns the full path of a file """ self.assertFalse(self.storage.exists('test.file')) f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) self.assertEqual(self.storage.path(f_name), os.path.join(self.temp_dir, f_name)) self.storage.delete(f_name) def test_file_url(self): """ File storage returns a url to access a given file from the web. """ self.assertEqual(self.storage.url('test.file'), self.storage.base_url + 'test.file') # should encode special chars except ~!*()' # like encodeURIComponent() JavaScript function do self.assertEqual( self.storage.url(r"~!*()'@#$%^&*abc`+ =.file"), "/test_media_url/~!*()'%40%23%24%25%5E%26*abc%60%2B%20%3D.file" ) self.assertEqual(self.storage.url("ab\0c"), "/test_media_url/ab%00c") # should translate os path separator(s) to the url path separator self.assertEqual(self.storage.url("""a/b\\c.file"""), "/test_media_url/a/b/c.file") # #25905: remove leading slashes from file names to prevent unsafe url output self.assertEqual(self.storage.url("/evil.com"), "/test_media_url/evil.com") self.assertEqual(self.storage.url(r"\evil.com"), "/test_media_url/evil.com") self.assertEqual(self.storage.url("///evil.com"), "/test_media_url/evil.com") self.assertEqual(self.storage.url(r"\\\evil.com"), "/test_media_url/evil.com") self.assertEqual(self.storage.url(None), "/test_media_url/") def test_base_url(self): """ File storage returns a url even when its base_url is unset or modified. """ self.storage.base_url = None with self.assertRaises(ValueError): self.storage.url('test.file') # #22717: missing ending slash in base_url should be auto-corrected storage = self.storage_class(location=self.temp_dir, base_url='/no_ending_slash') self.assertEqual( storage.url('test.file'), '%s%s' % (storage.base_url, 'test.file') ) def test_listdir(self): """ File storage returns a tuple containing directories and files. """ self.assertFalse(self.storage.exists('storage_test_1')) self.assertFalse(self.storage.exists('storage_test_2')) self.assertFalse(self.storage.exists('storage_dir_1')) self.storage.save('storage_test_1', ContentFile('custom content')) self.storage.save('storage_test_2', ContentFile('custom content')) os.mkdir(os.path.join(self.temp_dir, 'storage_dir_1')) self.addCleanup(self.storage.delete, 'storage_test_1') self.addCleanup(self.storage.delete, 'storage_test_2') for directory in ('', Path('')): with self.subTest(directory=directory): dirs, files = self.storage.listdir(directory) self.assertEqual(set(dirs), {'storage_dir_1'}) self.assertEqual(set(files), {'storage_test_1', 'storage_test_2'}) def test_file_storage_prevents_directory_traversal(self): """ File storage prevents directory traversal (files can only be accessed if they're below the storage location). """ with self.assertRaises(SuspiciousFileOperation): self.storage.exists('..') with self.assertRaises(SuspiciousFileOperation): self.storage.exists('/etc/passwd') def test_file_storage_preserves_filename_case(self): """The storage backend should preserve case of filenames.""" # Create a storage backend associated with the mixed case name # directory. other_temp_storage = self.storage_class(location=self.temp_dir2) # Ask that storage backend to store a file with a mixed case filename. mixed_case = 'CaSe_SeNsItIvE' file = other_temp_storage.open(mixed_case, 'w') file.write('storage contents') file.close() self.assertEqual(os.path.join(self.temp_dir2, mixed_case), other_temp_storage.path(mixed_case)) other_temp_storage.delete(mixed_case) def test_makedirs_race_handling(self): """ File storage should be robust against directory creation race conditions. """ real_makedirs = os.makedirs # Monkey-patch os.makedirs, to simulate a normal call, a raced call, # and an error. def fake_makedirs(path, mode=0o777, exist_ok=False): if path == os.path.join(self.temp_dir, 'normal'): real_makedirs(path, mode, exist_ok) elif path == os.path.join(self.temp_dir, 'raced'): real_makedirs(path, mode, exist_ok) if not exist_ok: raise FileExistsError() elif path == os.path.join(self.temp_dir, 'error'): raise PermissionError() else: self.fail('unexpected argument %r' % path) try: os.makedirs = fake_makedirs self.storage.save('normal/test.file', ContentFile('saved normally')) with self.storage.open('normal/test.file') as f: self.assertEqual(f.read(), b'saved normally') self.storage.save('raced/test.file', ContentFile('saved with race')) with self.storage.open('raced/test.file') as f: self.assertEqual(f.read(), b'saved with race') # Exceptions aside from FileExistsError are raised. with self.assertRaises(PermissionError): self.storage.save('error/test.file', ContentFile('not saved')) finally: os.makedirs = real_makedirs def test_remove_race_handling(self): """ File storage should be robust against file removal race conditions. """ real_remove = os.remove # Monkey-patch os.remove, to simulate a normal call, a raced call, # and an error. def fake_remove(path): if path == os.path.join(self.temp_dir, 'normal.file'): real_remove(path) elif path == os.path.join(self.temp_dir, 'raced.file'): real_remove(path) raise FileNotFoundError() elif path == os.path.join(self.temp_dir, 'error.file'): raise PermissionError() else: self.fail('unexpected argument %r' % path) try: os.remove = fake_remove self.storage.save('normal.file', ContentFile('delete normally')) self.storage.delete('normal.file') self.assertFalse(self.storage.exists('normal.file')) self.storage.save('raced.file', ContentFile('delete with race')) self.storage.delete('raced.file') self.assertFalse(self.storage.exists('normal.file')) # Exceptions aside from FileNotFoundError are raised. self.storage.save('error.file', ContentFile('delete with error')) with self.assertRaises(PermissionError): self.storage.delete('error.file') finally: os.remove = real_remove def test_file_chunks_error(self): """ Test behavior when file.chunks() is raising an error """ f1 = ContentFile('chunks fails') def failing_chunks(): raise OSError f1.chunks = failing_chunks with self.assertRaises(OSError): self.storage.save('error.file', f1) def test_delete_no_name(self): """ Calling delete with an empty name should not try to remove the base storage directory, but fail loudly (#20660). """ msg = 'The name must be given to delete().' with self.assertRaisesMessage(ValueError, msg): self.storage.delete(None) with self.assertRaisesMessage(ValueError, msg): self.storage.delete('') def test_delete_deletes_directories(self): tmp_dir = tempfile.mkdtemp(dir=self.storage.location) self.storage.delete(tmp_dir) self.assertFalse(os.path.exists(tmp_dir)) @override_settings( MEDIA_ROOT='media_root', MEDIA_URL='media_url/', FILE_UPLOAD_PERMISSIONS=0o777, FILE_UPLOAD_DIRECTORY_PERMISSIONS=0o777, ) def test_setting_changed(self): """ Properties using settings values as defaults should be updated on referenced settings change while specified values should be unchanged. """ storage = self.storage_class( location='explicit_location', base_url='explicit_base_url/', file_permissions_mode=0o666, directory_permissions_mode=0o666, ) defaults_storage = self.storage_class() settings = { 'MEDIA_ROOT': 'overridden_media_root', 'MEDIA_URL': '/overridden_media_url/', 'FILE_UPLOAD_PERMISSIONS': 0o333, 'FILE_UPLOAD_DIRECTORY_PERMISSIONS': 0o333, } with self.settings(**settings): self.assertEqual(storage.base_location, 'explicit_location') self.assertIn('explicit_location', storage.location) self.assertEqual(storage.base_url, 'explicit_base_url/') self.assertEqual(storage.file_permissions_mode, 0o666) self.assertEqual(storage.directory_permissions_mode, 0o666) self.assertEqual(defaults_storage.base_location, settings['MEDIA_ROOT']) self.assertIn(settings['MEDIA_ROOT'], defaults_storage.location) self.assertEqual(defaults_storage.base_url, settings['MEDIA_URL']) self.assertEqual(defaults_storage.file_permissions_mode, settings['FILE_UPLOAD_PERMISSIONS']) self.assertEqual( defaults_storage.directory_permissions_mode, settings['FILE_UPLOAD_DIRECTORY_PERMISSIONS'] ) def test_file_methods_pathlib_path(self): p = Path('test.file') self.assertFalse(self.storage.exists(p)) f = ContentFile('custom contents') f_name = self.storage.save(p, f) # Storage basic methods. self.assertEqual(self.storage.path(p), os.path.join(self.temp_dir, p)) self.assertEqual(self.storage.size(p), 15) self.assertEqual(self.storage.url(p), self.storage.base_url + f_name) with self.storage.open(p) as f: self.assertEqual(f.read(), b'custom contents') self.addCleanup(self.storage.delete, p)