From fbad8e240f100f562d7d12afaa07a7d2c27475d2 Mon Sep 17 00:00:00 2001 From: Athanasius Date: Fri, 19 Mar 2021 16:17:20 +0000 Subject: [PATCH] obtain_lock() Read-Only test Only tested under win32 so far, but I'd hope os.chmod() is the way to go on Linux and macOS. The win32 implementation currently relies on the pywin32 package being installed in order to use its ntsecuritycon and win32security modules. It might be possible to do this with ctypes instead, but this sure looks cleaner than most ctypes code I've seen. --- tests/journal_lock.py/test_journal_lock.py | 66 +++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/tests/journal_lock.py/test_journal_lock.py b/tests/journal_lock.py/test_journal_lock.py index d24bd98e..286dd67e 100644 --- a/tests/journal_lock.py/test_journal_lock.py +++ b/tests/journal_lock.py/test_journal_lock.py @@ -33,7 +33,9 @@ # # - Not sure about testing JournalAlreadyLocked class. +import os import pathlib +import sys import pytest # Import as other names else they get picked up when used as fixtures @@ -42,7 +44,7 @@ from _pytest import tmpdir as _pytest_tmpdir from py._path.local import LocalPath as py_path_local_LocalPath from config import config -from journal_lock import JournalLock +from journal_lock import JournalLock, JournalLockResult class TestJournalLock: @@ -51,6 +53,9 @@ class TestJournalLock: @pytest.fixture def mock_journaldir(self, monkeypatch: _pytest_monkeypatch, tmpdir: _pytest_tmpdir) -> py_path_local_LocalPath: """Fixture for mocking config.get_str('journaldir').""" + # Force the directory to pre-defined for testing making !Write + # tmpdir = pathlib.Path(r'C:\Users\Athan\AppData\Local\Temp\rotest') + def get_str(key: str, *, default: str = None) -> str: """Mock config.*Config get_str to provide fake journaldir.""" if key == 'journaldir': @@ -93,3 +98,62 @@ class TestJournalLock: jlock.journal_dir = tmpdir jlock.set_path_from_journaldir() assert isinstance(jlock.journal_dir_path, pathlib.Path) + + def test_obtain_lock_with_none(self): + """Test JournalLock.obtain_lock() with None.""" + jlock = JournalLock() + + # Check that 'None' is handled correctly. + jlock.journal_dir = None + jlock.set_path_from_journaldir() + assert jlock.journal_dir_path is None + locked = jlock.obtain_lock() + assert locked == JournalLockResult.JOURNALDIR_IS_NONE + + def test_obtain_lock_with_tmpdir(self, mock_journaldir: py_path_local_LocalPath): + """Test JournalLock.obtain_lock() with tmpdir.""" + jlock = JournalLock() + + # Check that an actual journaldir is handled correctly. + locked = jlock.obtain_lock() + assert locked == JournalLockResult.LOCKED + + def test_obtain_lock_with_tmpdir_ro(self, mock_journaldir: py_path_local_LocalPath): + """Test JournalLock.obtain_lock() with tmpdir.""" + tmpdir = mock_journaldir + + # Make tmpdir read-only ? + if sys.platform == 'win32': + # Ref: + # 1. Look up the current user + user = os.environ.get('USERNAME') + + # From pywin32 + import ntsecuritycon as con + import win32security + + # 2. Fetch its details + winuser, domain, type = win32security.LookupAccountName("", user) + + # 3. Fetch the current security of tmpdir for that user. + sd = win32security.GetFileSecurity(str(tmpdir), win32security.DACL_SECURITY_INFORMATION) + dacl = sd.GetSecurityDescriptorDacl() # instead of dacl = win32security.ACL() + + # 4. Add Write to Denied list + dacl.AddAccessDeniedAce(win32security.ACL_REVISION, con.FILE_GENERIC_WRITE, winuser) + # 5. Apply that change. + sd.SetSecurityDescriptorDacl(1, dacl, 0) # may not be necessary + win32security.SetFileSecurity(str(tmpdir), win32security.DACL_SECURITY_INFORMATION, sd) + # + # And, we might need to undo all of that to exit tests cleanly ? + + else: + import stat + os.chmod(tmpdir, stat.S_IRUSR | stat.S_IUSR) + + jlock = JournalLock() + + # Check that an actual journaldir is handled correctly. + locked = jlock.obtain_lock() + print(f'{locked=}') + assert locked == JournalLockResult.JOURNALDIR_READONLY