Merge pull request #2094 from pbiering/tox-test-filesystems
Tox test filesystems
This commit is contained in:
54
.github/workflows/test.yml
vendored
54
.github/workflows/test.yml
vendored
@@ -30,7 +30,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
test-ubuntu-python-newest:
|
test-ubuntu-python-newest:
|
||||||
name: Test Ubuntu:latest Python:newest
|
name: Test Python:newest Ubuntu:latest
|
||||||
needs: lint
|
needs: lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
run: tox -c pyproject.toml -e py
|
run: tox -c pyproject.toml -e py
|
||||||
|
|
||||||
test-ubuntu-python-newest-with-passlib:
|
test-ubuntu-python-newest-with-passlib:
|
||||||
name: Test Ubuntu:latest Python:newest passlib
|
name: Test passlib Python:newest Ubuntu:latest
|
||||||
needs: [lint, test-ubuntu-python-newest]
|
needs: [lint, test-ubuntu-python-newest]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -64,12 +64,48 @@ jobs:
|
|||||||
run: pip install tox
|
run: pip install tox
|
||||||
- name: Switch back to passlib
|
- name: Switch back to passlib
|
||||||
run: sed -i 's|libpass[^"]*|passlib|' pyproject.toml
|
run: sed -i 's|libpass[^"]*|passlib|' pyproject.toml
|
||||||
- name: Test with newest Python on latest Ubuntu
|
- name: Test with newest Python on latest Ubuntu using passlib
|
||||||
run: tox -c pyproject.toml -e py
|
run: tox -c pyproject.toml -e py
|
||||||
|
|
||||||
|
test-ubuntu-python-newest-with-vfat:
|
||||||
|
name: Test VFAT Python:newest Ubuntu:latest
|
||||||
|
needs: [lint, test-ubuntu-python-newest]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
python-version: ['3.14']
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install Test dependencies
|
||||||
|
run: pip install tox
|
||||||
|
- name: Test with newest Python on latest Ubuntu using VFAT
|
||||||
|
run: tox -c pyproject.toml -e py_filesystem_vfat
|
||||||
|
|
||||||
|
test-ubuntu-python-newest-with-ntfs:
|
||||||
|
name: Test NTFS Python:newest Ubuntu:latest
|
||||||
|
needs: [lint, test-ubuntu-python-newest]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
python-version: ['3.14']
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install Test dependencies
|
||||||
|
run: pip install tox
|
||||||
|
- name: Test with newest Python on latest Ubuntu using NTFS
|
||||||
|
run: tox -c pyproject.toml -e py_filesystem_ntfs
|
||||||
|
|
||||||
test-python-32bit:
|
test-python-32bit:
|
||||||
name: Test Ubuntu:latest Python:32-bit
|
name: Test 32-bit Python:3.11 Ubuntu:latest
|
||||||
needs: [lint, test-ubuntu-python-newest, integ-test]
|
needs: [lint, test-ubuntu-python-newest, integ-test, test-ubuntu-python-newest-with-passlib]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
@@ -102,13 +138,13 @@ jobs:
|
|||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install --upgrade pip
|
pip install --upgrade pip
|
||||||
- name: Test 32-bit
|
- name: Test with 32-bit Python on latest Ubuntu
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
tox -c /__w/Radicale/Radicale/pyproject.toml -e py
|
tox -c /__w/Radicale/Radicale/pyproject.toml -e py
|
||||||
|
|
||||||
test-ubuntu-python-oldest:
|
test-ubuntu-python-oldest:
|
||||||
name: Test Ubuntu:latest Python:oldest
|
name: Test Python:oldest Ubuntu:latest
|
||||||
needs: [lint, test-ubuntu-python-newest, test-python-32bit]
|
needs: [lint, test-ubuntu-python-newest, test-python-32bit]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@@ -127,7 +163,7 @@ jobs:
|
|||||||
|
|
||||||
test-otheros-python-newest:
|
test-otheros-python-newest:
|
||||||
name: Test MacOS/Windows:latest Python:newest
|
name: Test MacOS/Windows:latest Python:newest
|
||||||
needs: [lint, test-ubuntu-python-newest, test-python-32bit]
|
needs: [lint, test-ubuntu-python-newest, test-ubuntu-python-newest-with-ntfs, test-ubuntu-python-newest-with-vfat]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, windows-latest]
|
os: [macos-latest, windows-latest]
|
||||||
@@ -229,7 +265,7 @@ jobs:
|
|||||||
js-test:
|
js-test:
|
||||||
name: JS Type Check
|
name: JS Type Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: test-ubuntu-python-newest
|
needs: [test-ubuntu-python-newest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/setup-node@v5
|
- uses: actions/setup-node@v5
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
* Improve: `path_to_filesystem()` by pre-detection of collision-free file system
|
* Improve: `path_to_filesystem()` by pre-detection of collision-free file system
|
||||||
* Adjustment: MKCOL/MKCALENDAR return now CONFLICT instead of BADREQUEST of file name collision
|
* Adjustment: MKCOL/MKCALENDAR return now CONFLICT instead of BADREQUEST of file name collision
|
||||||
* Improve: [auth] catch bcrypt>=5.0.0 enforced max password length early and support legacy "passlib" as well as "libpass" (rework 3.6.0, "packaging" not needed anymore)
|
* Improve: [auth] catch bcrypt>=5.0.0 enforced max password length early and support legacy "passlib" as well as "libpass" (rework 3.6.0, "packaging" not needed anymore)
|
||||||
|
* Improve: application will stop on startup if TEMP is provided but not existing or not writable
|
||||||
|
* Extension: tox with new optional test cases to test with LinuxOS vfat, hfsplus, ntfs filesystems
|
||||||
|
* Adjust: respond with 500 in case principal collection cannot be created (e.g. filesystem issues)
|
||||||
|
|
||||||
## 3.7.1
|
## 3.7.1
|
||||||
* Fix: share address book collection as birthday calendar not working on non-DEBUG level
|
* Fix: share address book collection as birthday calendar not working on non-DEBUG level
|
||||||
|
|||||||
@@ -87,6 +87,88 @@ deps = [
|
|||||||
]
|
]
|
||||||
commands = [["pytest", "-r", "s", "--log-level", "5", "."]]
|
commands = [["pytest", "-r", "s", "--log-level", "5", "."]]
|
||||||
|
|
||||||
|
[tool.tox.env.py_filesystem_vfat]
|
||||||
|
allowlist_externals = [ "sudo", "dd", "mkfs.vfat", "mkdir", "rm", "rmdir", "chmod" ]
|
||||||
|
extras = ["test"]
|
||||||
|
deps = [
|
||||||
|
"pytest"
|
||||||
|
]
|
||||||
|
commands_pre = [
|
||||||
|
# create 64 MByte disk image
|
||||||
|
["dd", "if=/dev/zero", "of=/tmp/vfat.img", "bs=1M", "count=64"],
|
||||||
|
# create file system
|
||||||
|
["mkfs.vfat", "/tmp/vfat.img", "-n", "VFAT"],
|
||||||
|
# unconditionally create mount point
|
||||||
|
["mkdir", "-p", "/tmp/vfat"],
|
||||||
|
# mount image
|
||||||
|
["sudo", "/usr/bin/mount", "-o", "loop,umask=000", "/tmp/vfat.img", "/tmp/vfat"],
|
||||||
|
]
|
||||||
|
setenv = { TEMP = "/tmp/vfat" }
|
||||||
|
commands = [["pytest", "-r", "s", "."]]
|
||||||
|
commands_post = [
|
||||||
|
# umount image
|
||||||
|
["sudo", "/usr/bin/umount", "-d", "/tmp/vfat"],
|
||||||
|
# remove mount point
|
||||||
|
["rmdir", "/tmp/vfat"],
|
||||||
|
# remove image
|
||||||
|
["rm", "/tmp/vfat.img"]
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.tox.env.py_filesystem_hfsplus]
|
||||||
|
# Fedora
|
||||||
|
allowlist_externals = [ "sudo", "dd", "mkfs.hfsplus", "mkdir", "rm", "rmdir", "chmod" ]
|
||||||
|
extras = ["test"]
|
||||||
|
deps = [
|
||||||
|
"pytest"
|
||||||
|
]
|
||||||
|
commands_pre = [
|
||||||
|
# create 64 MByte disk image
|
||||||
|
["dd", "if=/dev/zero", "of=/tmp/hfsp.img", "bs=1M", "count=64"],
|
||||||
|
# create file system
|
||||||
|
["mkfs.hfsplus", "/tmp/hfsp.img", "-v", "HFSP"],
|
||||||
|
# unconditionally create mount point
|
||||||
|
["mkdir", "-p", "/tmp/hfsp"],
|
||||||
|
# mount image
|
||||||
|
["sudo", "/usr/bin/mount", "-o", "loop,umask=000", "/tmp/hfsp.img", "/tmp/hfsp", "-t", "hfsplus"],
|
||||||
|
]
|
||||||
|
setenv = { TEMP = "/tmp/hfsp" }
|
||||||
|
commands = [["pytest", "-r", "s", "."]]
|
||||||
|
commands_post = [
|
||||||
|
# umount image
|
||||||
|
["sudo", "/usr/bin/umount", "-d", "/tmp/hfsp"],
|
||||||
|
# remove mount point
|
||||||
|
["rmdir", "/tmp/hfsp"],
|
||||||
|
# remove image
|
||||||
|
["rm", "/tmp/hfsp.img"]
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.tox.env.py_filesystem_ntfs]
|
||||||
|
allowlist_externals = [ "sudo", "dd", "mkfs.ntfs", "mkdir", "rm", "rmdir", "chmod" ]
|
||||||
|
extras = ["test"]
|
||||||
|
deps = [
|
||||||
|
"pytest"
|
||||||
|
]
|
||||||
|
commands_pre = [
|
||||||
|
# create 64 MByte disk image
|
||||||
|
["dd", "if=/dev/zero", "of=/tmp/ntfs.img", "bs=1M", "count=64"],
|
||||||
|
# create file system
|
||||||
|
["mkfs.ntfs", "/tmp/ntfs.img", "-L", "NTFS", "-F"],
|
||||||
|
# unconditionally create mount point
|
||||||
|
["mkdir", "-p", "/tmp/ntfs"],
|
||||||
|
# mount image
|
||||||
|
["sudo", "/usr/bin/mount", "-o", "loop,umask=0000", "/tmp/ntfs.img", "/tmp/ntfs"],
|
||||||
|
]
|
||||||
|
setenv = { TEMP = "/tmp/ntfs" }
|
||||||
|
commands = [["pytest", "-r", "s", "."]]
|
||||||
|
commands_post = [
|
||||||
|
# umount image
|
||||||
|
["sudo", "/usr/bin/umount", "-d", "/tmp/ntfs"],
|
||||||
|
# remove mount point
|
||||||
|
["rmdir", "/tmp/ntfs"],
|
||||||
|
# remove image
|
||||||
|
["rm", "/tmp/ntfs.img"]
|
||||||
|
]
|
||||||
|
|
||||||
[tool.tox.env.flake8]
|
[tool.tox.env.flake8]
|
||||||
deps = ["flake8==7.1.0"]
|
deps = ["flake8==7.1.0"]
|
||||||
commands = [["flake8", "."]]
|
commands = [["flake8", "."]]
|
||||||
|
|||||||
@@ -100,6 +100,11 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
super().__init__(configuration)
|
super().__init__(configuration)
|
||||||
|
if 'TEMP' in os.environ:
|
||||||
|
if not os.path.isdir(os.environ['TEMP']):
|
||||||
|
raise RuntimeError("TEMP found in environment, but directory is not existing: %r" % os.environ['TEMP'])
|
||||||
|
if not os.access(os.environ['TEMP'], os.W_OK):
|
||||||
|
raise RuntimeError("TEMP found in environment, but not writable: %r" % os.environ['TEMP'])
|
||||||
self._mask_passwords = configuration.get("logging", "mask_passwords")
|
self._mask_passwords = configuration.get("logging", "mask_passwords")
|
||||||
self._delay_on_error = configuration.get("server", "delay_on_error")
|
self._delay_on_error = configuration.get("server", "delay_on_error")
|
||||||
logger.info("delay_on_error set to: %.3f seconds", self._delay_on_error)
|
logger.info("delay_on_error set to: %.3f seconds", self._delay_on_error)
|
||||||
@@ -535,9 +540,13 @@ class Application(ApplicationPartDelete, ApplicationPartHead,
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.warning("Failed to create predefined collection %r: %s", name_coll, e)
|
logger.warning("Failed to create predefined collection %r: %s", name_coll, e)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
logger.warning("Failed to create principal "
|
logger.error("Failed to create principal "
|
||||||
"collection %r: %s", user, e)
|
"collection for user %r: ValueError %s", user, e)
|
||||||
user = ""
|
return response(*httputils.INTERNAL_SERVER_ERROR)
|
||||||
|
except OSError as e:
|
||||||
|
logger.error("Failed to create principal "
|
||||||
|
"collection for user %r: OSerror %s", user, e)
|
||||||
|
return response(*httputils.INTERNAL_SERVER_ERROR)
|
||||||
else:
|
else:
|
||||||
logger.warning("Access to principal path %r denied by "
|
logger.warning("Access to principal path %r denied by "
|
||||||
"rights backend", principal_path)
|
"rights backend", principal_path)
|
||||||
|
|||||||
@@ -407,7 +407,7 @@ def path_supports_symlink(path):
|
|||||||
|
|
||||||
|
|
||||||
def path_is_collision_free_case_sensitive(path):
|
def path_is_collision_free_case_sensitive(path):
|
||||||
# Test: case sensitive
|
"""Check whether path supports case sensitive entries."""
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
raise ValueError("%r is not a path" % (path))
|
raise ValueError("%r is not a path" % (path))
|
||||||
base_dir = tempfile.mkdtemp(dir=path)
|
base_dir = tempfile.mkdtemp(dir=path)
|
||||||
@@ -426,7 +426,7 @@ def path_is_collision_free_case_sensitive(path):
|
|||||||
# cleanup
|
# cleanup
|
||||||
os.rmdir(test_dir_uc)
|
os.rmdir(test_dir_uc)
|
||||||
os.rmdir(base_dir)
|
os.rmdir(base_dir)
|
||||||
logger.debug("path_is_collision_free (case-sensitive): path=%r result=%s", path, result)
|
logger.trace("path_is_collision_free (case-sensitive): path=%r result=%s", path, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -450,5 +450,78 @@ def path_is_collision_free_no_short_filename(path):
|
|||||||
# cleanup
|
# cleanup
|
||||||
os.rmdir(test_dir_long)
|
os.rmdir(test_dir_long)
|
||||||
os.rmdir(base_dir)
|
os.rmdir(base_dir)
|
||||||
logger.debug("path_is_collision_free (no short-filename): path=%r result=%s", path, result)
|
logger.trace("path_is_collision_free (no short-filename): path=%r result=%s", path, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def path_supports_unicode(path):
|
||||||
|
"""Check whether path supports unicode."""
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
raise ValueError("%r is not a path" % (path))
|
||||||
|
base_dir = tempfile.mkdtemp(dir=path)
|
||||||
|
part = "TEST😀"
|
||||||
|
test_dir = os.path.join(base_dir, part)
|
||||||
|
result = True
|
||||||
|
try:
|
||||||
|
os.mkdir(test_dir)
|
||||||
|
except OSError:
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
with os.scandir(base_dir) as entries:
|
||||||
|
if part not in (e.name for e in entries):
|
||||||
|
result = False
|
||||||
|
# cleanup
|
||||||
|
os.rmdir(test_dir)
|
||||||
|
# cleanup
|
||||||
|
os.rmdir(base_dir)
|
||||||
|
logger.trace("path_supports_unicode: path=%r result=%s", path, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def path_supports_trailing_whitespace(path):
|
||||||
|
"""Check whether path supports trailing whitespace."""
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
raise ValueError("%r is not a path" % (path))
|
||||||
|
base_dir = tempfile.mkdtemp(dir=path)
|
||||||
|
part = "TEST "
|
||||||
|
test_dir = os.path.join(base_dir, part)
|
||||||
|
result = True
|
||||||
|
try:
|
||||||
|
os.mkdir(test_dir)
|
||||||
|
except OSError:
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
with os.scandir(base_dir) as entries:
|
||||||
|
if part not in (e.name for e in entries):
|
||||||
|
result = False
|
||||||
|
# cleanup
|
||||||
|
os.rmdir(test_dir)
|
||||||
|
# cleanup
|
||||||
|
os.rmdir(base_dir)
|
||||||
|
logger.trace("path_supports_trailing_whitespace: path=%r result=%s", path, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def path_supports_problematic_chars(path):
|
||||||
|
"""Check whether path supports problematic chars."""
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
raise ValueError("%r is not a path" % (path))
|
||||||
|
base_dir = tempfile.mkdtemp(dir=path)
|
||||||
|
result = True
|
||||||
|
for char in ['*', '?']:
|
||||||
|
part = "TES" + char + "T"
|
||||||
|
test_dir = os.path.join(base_dir, part)
|
||||||
|
try:
|
||||||
|
os.mkdir(test_dir)
|
||||||
|
except OSError:
|
||||||
|
result = False
|
||||||
|
else:
|
||||||
|
with os.scandir(base_dir) as entries:
|
||||||
|
if part not in (e.name for e in entries):
|
||||||
|
result = False
|
||||||
|
# cleanup
|
||||||
|
os.rmdir(test_dir)
|
||||||
|
# cleanup
|
||||||
|
os.rmdir(base_dir)
|
||||||
|
logger.trace("path_supports_problematic chars: path=%r result=%s", path, result)
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class Storage(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("Storage item mtime resolution test not possible, cannot set utime on file: %r (%s)", path, e)
|
logger.warning("Storage item mtime resolution test not possible, cannot set utime on file: %r (%s)", path, e)
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
raise
|
raise ValueError # do not raise a hard PermissionError
|
||||||
logger.debug("Storage item mtime resoultion test set: %d ns" % MTIME_NS_TEST)
|
logger.debug("Storage item mtime resoultion test set: %d ns" % MTIME_NS_TEST)
|
||||||
mtime_ns = os.stat(path).st_mtime_ns - mtime_ns
|
mtime_ns = os.stat(path).st_mtime_ns - mtime_ns
|
||||||
logger.debug("Storage item mtime resoultion test get: %d ns" % mtime_ns)
|
logger.debug("Storage item mtime resoultion test get: %d ns" % mtime_ns)
|
||||||
@@ -180,6 +180,9 @@ class Storage(
|
|||||||
self._filesystem_root_folder_is_collision_free,
|
self._filesystem_root_folder_is_collision_free,
|
||||||
filesystem_root_folder_is_collision_free_case_sensitive,
|
filesystem_root_folder_is_collision_free_case_sensitive,
|
||||||
filesystem_root_folder_is_collision_free_no_short_filename)
|
filesystem_root_folder_is_collision_free_no_short_filename)
|
||||||
|
logger.info("Storage location subfolder suppports unicode: %s", pathutils.path_supports_unicode(self._get_collection_root_folder()))
|
||||||
|
logger.info("Storage location subfolder suppports trailing whitespace: %s", pathutils.path_supports_trailing_whitespace(self._get_collection_root_folder()))
|
||||||
|
logger.info("Storage location subfolder suppports problematic chars: %s", pathutils.path_supports_problematic_chars(self._get_collection_root_folder()))
|
||||||
logger.info("Storage cache subfolder usage for 'item': %s", self._use_cache_subfolder_for_item)
|
logger.info("Storage cache subfolder usage for 'item': %s", self._use_cache_subfolder_for_item)
|
||||||
logger.info("Storage cache subfolder usage for 'history': %s", self._use_cache_subfolder_for_history)
|
logger.info("Storage cache subfolder usage for 'history': %s", self._use_cache_subfolder_for_history)
|
||||||
logger.info("Storage cache subfolder usage for 'sync-token': %s", self._use_cache_subfolder_for_synctoken)
|
logger.info("Storage cache subfolder usage for 'sync-token': %s", self._use_cache_subfolder_for_synctoken)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ from typing import Iterable, Tuple, Union
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from radicale import xmlutils
|
from radicale import pathutils, xmlutils
|
||||||
from radicale.auth import htpasswd
|
from radicale.auth import htpasswd
|
||||||
from radicale.tests import BaseTest
|
from radicale.tests import BaseTest
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
|
|
||||||
def _test_htpasswd(self, htpasswd_encryption: str, htpasswd_content: str,
|
def _test_htpasswd(self, htpasswd_encryption: str, htpasswd_content: str,
|
||||||
test_matrix: Union[str, Iterable[Tuple[str, str, bool]]]
|
test_matrix: Union[str, Iterable[Tuple[str, str, bool]]]
|
||||||
= "ascii", delay: float = 0) -> None:
|
= "ascii", delay: float = 0, check: int = 207) -> None:
|
||||||
"""Test htpasswd authentication with user "tmp" and password "bepo" for
|
"""Test htpasswd authentication with user "tmp" and password "bepo" for
|
||||||
``test_matrix`` "ascii" or user "😀" and password "🔑" for
|
``test_matrix`` "ascii" or user "😀" and password "🔑" for
|
||||||
``test_matrix`` "unicode"."""
|
``test_matrix`` "unicode"."""
|
||||||
@@ -88,7 +88,7 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
elif isinstance(test_matrix, str):
|
elif isinstance(test_matrix, str):
|
||||||
raise ValueError("Unknown test matrix %r" % test_matrix)
|
raise ValueError("Unknown test matrix %r" % test_matrix)
|
||||||
for user, password, valid in test_matrix:
|
for user, password, valid in test_matrix:
|
||||||
self.propfind("/", check=207 if valid else 401,
|
self.propfind("/", check=check if valid else 401,
|
||||||
login="%s:%s" % (user, password))
|
login="%s:%s" % (user, password))
|
||||||
|
|
||||||
def test_htpasswd_plain(self) -> None:
|
def test_htpasswd_plain(self) -> None:
|
||||||
@@ -102,7 +102,11 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
("tmp", "be:po", True), ("tmp", "bepo", False)))
|
("tmp", "be:po", True), ("tmp", "bepo", False)))
|
||||||
|
|
||||||
def test_htpasswd_plain_unicode(self) -> None:
|
def test_htpasswd_plain_unicode(self) -> None:
|
||||||
self._test_htpasswd("plain", "😀:🔑", "unicode")
|
if not pathutils.path_supports_unicode(self.colpath):
|
||||||
|
check = 500
|
||||||
|
else:
|
||||||
|
check = 207
|
||||||
|
self._test_htpasswd("plain", "😀:🔑", "unicode", check=check)
|
||||||
|
|
||||||
def test_htpasswd_md5(self) -> None:
|
def test_htpasswd_md5(self) -> None:
|
||||||
self._test_htpasswd("md5", "tmp:$apr1$BI7VKCZh$GKW4vq2hqDINMr8uv7lDY/")
|
self._test_htpasswd("md5", "tmp:$apr1$BI7VKCZh$GKW4vq2hqDINMr8uv7lDY/")
|
||||||
@@ -111,8 +115,12 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
self._test_htpasswd("autodetect", "tmp:$apr1$BI7VKCZh$GKW4vq2hqDINMr8uv7lDY/")
|
self._test_htpasswd("autodetect", "tmp:$apr1$BI7VKCZh$GKW4vq2hqDINMr8uv7lDY/")
|
||||||
|
|
||||||
def test_htpasswd_md5_unicode(self):
|
def test_htpasswd_md5_unicode(self):
|
||||||
|
if not pathutils.path_supports_unicode(self.colpath):
|
||||||
|
check = 500
|
||||||
|
else:
|
||||||
|
check = 207
|
||||||
self._test_htpasswd(
|
self._test_htpasswd(
|
||||||
"md5", "😀:$apr1$w4ev89r1$29xO8EvJmS2HEAadQ5qy11", "unicode")
|
"md5", "😀:$apr1$w4ev89r1$29xO8EvJmS2HEAadQ5qy11", "unicode", check=check)
|
||||||
|
|
||||||
def test_htpasswd_sha256(self) -> None:
|
def test_htpasswd_sha256(self) -> None:
|
||||||
self._test_htpasswd("sha256", "tmp:$5$i4Ni4TQq6L5FKss5$ilpTjkmnxkwZeV35GB9cYSsDXTALBn6KtWRJAzNlCL/")
|
self._test_htpasswd("sha256", "tmp:$5$i4Ni4TQq6L5FKss5$ilpTjkmnxkwZeV35GB9cYSsDXTALBn6KtWRJAzNlCL/")
|
||||||
@@ -166,7 +174,11 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
|
|
||||||
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
||||||
def test_htpasswd_bcrypt_unicode(self) -> None:
|
def test_htpasswd_bcrypt_unicode(self) -> None:
|
||||||
self._test_htpasswd("bcrypt", "😀:$2y$10$Oyz5aHV4MD9eQJbk6GPemOs4T6edK6U9Sqlzr.W1mMVCS8wJUftnW", "unicode")
|
if not pathutils.path_supports_unicode(self.colpath):
|
||||||
|
check = 500
|
||||||
|
else:
|
||||||
|
check = 207
|
||||||
|
self._test_htpasswd("bcrypt", "😀:$2y$10$Oyz5aHV4MD9eQJbk6GPemOs4T6edK6U9Sqlzr.W1mMVCS8wJUftnW", "unicode", check=check)
|
||||||
|
|
||||||
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
||||||
def test_htpasswd_bcrypt_long(self) -> None:
|
def test_htpasswd_bcrypt_long(self) -> None:
|
||||||
@@ -280,12 +292,23 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.platform == "win32", reason="leading and trailing "
|
|
||||||
"whitespaces not allowed in file names")
|
|
||||||
def test_htpasswd_whitespace_user(self) -> None:
|
def test_htpasswd_whitespace_user(self) -> None:
|
||||||
for user in (" tmp", "tmp ", " tmp "):
|
for user in (" tmp", "tmp ", " tmp "):
|
||||||
|
if not pathutils.path_supports_trailing_whitespace(self.colpath) and user.endswith(' '):
|
||||||
|
check = 500
|
||||||
|
else:
|
||||||
|
check = 207
|
||||||
self._test_htpasswd("plain", "%s:bepo" % user, (
|
self._test_htpasswd("plain", "%s:bepo" % user, (
|
||||||
(user, "bepo", True), ("tmp", "bepo", False)))
|
(user, "bepo", True), ("tmp", "bepo", False)), check=check)
|
||||||
|
|
||||||
|
def test_htpasswd_problem_user(self) -> None:
|
||||||
|
for user in ("tm*p", "tm?p"):
|
||||||
|
if not pathutils.path_supports_problematic_chars(self.colpath):
|
||||||
|
check = 500
|
||||||
|
else:
|
||||||
|
check = 207
|
||||||
|
self._test_htpasswd("plain", "%s:bepo" % user, (
|
||||||
|
(user, "bepo", True), ("tmp", "bepo", False)), check=check)
|
||||||
|
|
||||||
def test_htpasswd_whitespace_password(self) -> None:
|
def test_htpasswd_whitespace_password(self) -> None:
|
||||||
for password in (" bepo", "bepo ", " bepo "):
|
for password in (" bepo", "bepo ", " bepo "):
|
||||||
|
|||||||
@@ -190,6 +190,26 @@ class TestMultiFileSystem(BaseTest):
|
|||||||
assert answer is not None
|
assert answer is not None
|
||||||
assert "\r\nUID:%s\r\n" % uid in answer
|
assert "\r\nUID:%s\r\n" % uid in answer
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not pathutils.path_is_collision_free_case_sensitive(tempfile.mkdtemp()), reason="TEMP is not case sensitive")
|
||||||
|
def test_collection_storage_dummy_case_sensitivity(self) -> None:
|
||||||
|
"""Test collection storage case sensitivity."""
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not pathutils.path_is_collision_free_no_short_filename(tempfile.mkdtemp()), reason="TEMP has short filename")
|
||||||
|
def test_collection_storage_dummy_no_short_filename(self) -> None:
|
||||||
|
"""Test collection storage no short filename."""
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not pathutils.path_supports_unicode(tempfile.mkdtemp()), reason="TEMP is not supporting unicode")
|
||||||
|
def test_collection_storage_dummy_no_support_of_unicode(self) -> None:
|
||||||
|
"""Test collection storage no support of unicode."""
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not pathutils.path_supports_trailing_whitespace(tempfile.mkdtemp()), reason="TEMP is not supporting trailing whitespace")
|
||||||
|
def test_collection_storage_dummy_no_support_of_trailing_whitespace(self) -> None:
|
||||||
|
"""Test collection storage no support of trailing space."""
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not pathutils.path_supports_problematic_chars(tempfile.mkdtemp()), reason="TEMP is not supporting problematic chars")
|
||||||
|
def test_collection_storage_dummy_no_support_of_problematic_chars(self) -> None:
|
||||||
|
"""Test collection storage no support of problematic chars."""
|
||||||
|
|
||||||
@pytest.mark.skipif(not pathutils.path_supports_symlink(tempfile.mkdtemp()), reason="TEMP is not supporting symlink")
|
@pytest.mark.skipif(not pathutils.path_supports_symlink(tempfile.mkdtemp()), reason="TEMP is not supporting symlink")
|
||||||
def test_collection_sharing_by_softlink(self) -> None:
|
def test_collection_sharing_by_softlink(self) -> None:
|
||||||
"""Test collection sharing by softlink."""
|
"""Test collection sharing by softlink."""
|
||||||
|
|||||||
Reference in New Issue
Block a user