diff --git a/tests/utils_tests/files/archive_unsupported.txt b/tests/utils_tests/files/archive_unsupported.txt new file mode 100644 index 0000000000..1183bbc9d3 --- /dev/null +++ b/tests/utils_tests/files/archive_unsupported.txt @@ -0,0 +1 @@ +I'm created to test unsupported format of `utils/archive.py`. diff --git a/tests/utils_tests/files/archive_weird_extension.zip.txt b/tests/utils_tests/files/archive_weird_extension.zip.txt new file mode 100644 index 0000000000..9956385ee2 Binary files /dev/null and b/tests/utils_tests/files/archive_weird_extension.zip.txt differ diff --git a/tests/utils_tests/test_archive.py b/tests/utils_tests/test_archive.py index 8cd107063f..61551cca06 100644 --- a/tests/utils_tests/test_archive.py +++ b/tests/utils_tests/test_archive.py @@ -3,10 +3,12 @@ import stat import sys import tempfile import unittest +from unittest.mock import Mock from django.core.exceptions import SuspiciousOperation from django.test import SimpleTestCase from django.utils import archive +from django.utils.archive import BaseArchive, TarArchive, UnrecognizedArchiveFormat try: import bz2 # NOQA @@ -78,6 +80,55 @@ class TestArchive(unittest.TestCase): filepath = os.path.join(tmpdir, "no_permissions") self.assertEqual(os.stat(filepath).st_mode & mask, 0o666 & ~umask) + def test_extract_not_supported_format(self): + test_file_path = os.path.join( + os.path.dirname(__file__), "files", "archive_unsupported.txt" + ) + with tempfile.TemporaryDirectory() as tmpdir: + with self.assertRaises(UnrecognizedArchiveFormat) as context: + archive.extract(test_file_path, tmpdir) + self.assertEqual( + "Path not a recognized archive format: %s" % test_file_path, + str(context.exception), + ) + + def test_extract_supported_extension_followed_by_unsupported_extension(self): + test_file_path = os.path.join( + os.path.dirname(__file__), "files", "archive_weird_extension.zip.txt" + ) + with tempfile.TemporaryDirectory() as tmpdir: + archive.extract(test_file_path, tmpdir) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "1"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "2"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "1"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "2"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "bar", "1"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "bar", "2"))) + + def test_extract_function_with_unsupported_instance(self): + with tempfile.TemporaryDirectory() as tmpdir: + with self.assertRaises(UnrecognizedArchiveFormat) as context: + # Accessing any attribute not in the spec will raise AttributeError. + file = Mock(spec=[]) + archive.extract(file, tmpdir) + self.assertEqual( + "File object not a recognized archive format.", + str(context.exception), + ) + + def test_extract_function_with_file_instance(self): + test_file_path = os.path.join( + os.path.dirname(__file__), "archives", "foobar.tar" + ) + with tempfile.TemporaryDirectory() as tmpdir: + archive.extract(test_file_path, tmpdir) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "1"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "2"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "1"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "2"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "bar", "1"))) + self.assertTrue(os.path.isfile(os.path.join(tmpdir, "foo", "bar", "2"))) + class TestArchiveInvalid(SimpleTestCase): def test_extract_function_traversal(self): @@ -96,3 +147,73 @@ class TestArchiveInvalid(SimpleTestCase): with self.subTest(entry), tempfile.TemporaryDirectory() as tmpdir: with self.assertRaisesMessage(SuspiciousOperation, msg % invalid_path): archive.extract(os.path.join(archives_dir, entry), tmpdir) + + +class TestBaseArchive(unittest.TestCase): + def test_split_leading_dir_windows_style(self): + self.assertEqual( + ["C:", "Documents\\Newsletters\\Summer2018.pdf"], + BaseArchive().split_leading_dir( + "C:\\Documents\\Newsletters\\Summer2018.pdf" + ), + ) + self.assertEqual( + ["Documents", "Newsletters\\Summer2018.pdf"], + BaseArchive().split_leading_dir("\\Documents\\Newsletters\\Summer2018.pdf"), + ) + self.assertEqual( + ["C:Documents", "Newsletters\\Summer2018.pdf"], + BaseArchive().split_leading_dir("C:Documents\\Newsletters\\Summer2018.pdf"), + ) + self.assertEqual( + ["..", "Publications\\TravelBrochure.pdf"], + BaseArchive().split_leading_dir("..\\Publications\\TravelBrochure.pdf"), + ) + self.assertEqual( + ["Publications", "TravelBrochure.pdf"], + BaseArchive().split_leading_dir("Publications\\TravelBrochure.pdf"), + ) + + def test_has_leading_dir_has_no_prefix(self): + self.assertFalse(BaseArchive().has_leading_dir(["/\\/foo.pdf"])) + + def test_extract(self): + with self.assertRaises(NotImplementedError) as context: + BaseArchive().extract() + + self.assertEqual( + "subclasses of BaseArchive must provide an extract() method", + str(context.exception), + ) + + def test_list(self): + with self.assertRaises(NotImplementedError) as context: + BaseArchive().list() + + self.assertEqual( + "subclasses of BaseArchive must provide a list() method", + str(context.exception), + ) + + +class TestTarArchive(unittest.TestCase): + def test_extract_with_corrupt_file(self): + test_file_path = os.path.join( + os.path.dirname(__file__), "archives", "foobar.tar" + ) + with tempfile.TemporaryDirectory() as tmpdir: + tar_archive = TarArchive(test_file_path) + + tar_archive._archive.extractfile = Mock(side_effect=KeyError()) + with self.assertRaises( + UnboundLocalError + ): # this is possible bug, need to be changed when fixed + tar_archive.extract(tmpdir) + + tar_archive._archive.extractfile = Mock(side_effect=AttributeError()) + with self.assertRaises( + UnboundLocalError + ): # this is possible bug, need to be changed when fixed + tar_archive.extract(tmpdir) + + tar_archive.close() diff --git a/tests/utils_tests/test_os_utils.py b/tests/utils_tests/test_os_utils.py index 7204167688..71776fec67 100644 --- a/tests/utils_tests/test_os_utils.py +++ b/tests/utils_tests/test_os_utils.py @@ -1,9 +1,10 @@ import os import unittest from pathlib import Path +from unittest.mock import Mock from django.core.exceptions import SuspiciousFileOperation -from django.utils._os import safe_join, to_path +from django.utils._os import safe_join, symlinks_supported, to_path class SafeJoinTests(unittest.TestCase): @@ -38,3 +39,17 @@ class ToPathTests(unittest.TestCase): def test_to_path_invalid_value(self): with self.assertRaises(TypeError): to_path(42) + + +class SymlinksSupportedTests(unittest.TestCase): + def test_supported(self): + os.symlink = Mock() + self.assertTrue(symlinks_supported()) + + def test_os_error(self): + os.symlink = Mock(side_effect=OSError("os_failed")) + self.assertFalse(symlinks_supported()) + + def test_not_implemented_error(self): + os.symlink = Mock(side_effect=NotImplementedError("not_implemented")) + self.assertFalse(symlinks_supported())