From 57aabaf0a5e0d8ae09d9b99d5a025fa912e9b574 Mon Sep 17 00:00:00 2001
From: Sun <a24230928@gmail.com>
Date: Thu, 8 Jun 2023 12:21:25 +0800
Subject: [PATCH] test: for `utils.archive`, `utils._os`

---
 .../utils_tests/files/archive_unsupported.txt |   1 +
 .../files/archive_weird_extension.zip.txt     | Bin 0 -> 1434 bytes
 tests/utils_tests/test_archive.py             | 121 ++++++++++++++++++
 tests/utils_tests/test_os_utils.py            |  17 ++-
 4 files changed, 138 insertions(+), 1 deletion(-)
 create mode 100644 tests/utils_tests/files/archive_unsupported.txt
 create mode 100644 tests/utils_tests/files/archive_weird_extension.zip.txt

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 0000000000000000000000000000000000000000..9956385ee29b15d116b75831333abd994ab2515e
GIT binary patch
literal 1434
zcmb7@ze~eF6vtnhMA``ApyD9@0V0SUwUjzIly>N#5L~3y2nB7VX{DfoQK<SqbdqlV
z1v>ffIMlg|Ki=KDv}dlp(1aYfd_V8qy-zA7({g}4k1xX<--yM4HP}8p$bgxDD3#xq
zPTh8A9t`W#1mH_$6n&Zy&GKj=DSG6FMY*bjRlSaP(r#62=N@Og77MPn{C<pKA>+*V
zw|M$~)^u8=Yt`nM;4Ev7AI6TKkqC5yS-ghl6%C@RLC{U+k`UK1ge|(D4mOhFH&fzN
zeKRROX!ZWhaRIA0{Nqcnc~K97y59)+G`>(H_9-eEsdaOjhpQt&`XSrmiEOe`gHfH7
zF}sMtlOcu~s==s!jxpci6=Gytk;iP+@KjeL-XaY4-jPS<B8izh11SwMX(&i!aWYMH
zIHCdc!ySYAVl=vaX0)a`)n^%ZE8%Dg%x(=%bz8=L2po0BOxK`P&t=rFKv7rBdJRf-
eK0+boE}sJB55PH0wBMe?ae;&H52m{K0sH}GS?*8(

literal 0
HcmV?d00001

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())