Fetching a locked folder before its password is sent returns 200 with only its listing fields, without metadata, sub_folders and path, so FoldersApi.retrieve failed with a pydantic ValidationError. The DOI route fetches the folder before it can see the lock, so a password-protected DOI folder could not be opened even with its password. - Add FolderLockedException, a subclass of UnauthorizedException, and raise it from retrieve on that short record and from the lock checks. - On a DOI folder, send the password and fetch it again when the first fetch finds it locked.
105 lines
3.8 KiB
Python
105 lines
3.8 KiB
Python
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from mdrsclient.api import FoldersApi
|
|
from mdrsclient.exceptions import FolderLockedException
|
|
from mdrsclient.models import Doi, Folder, Laboratories, Laboratory
|
|
from mdrsclient.services import MdrsService
|
|
|
|
TIMESTAMP = "2026-01-01T00:00:00+09:00"
|
|
DOI = "10.60178/cbs.20260429-001"
|
|
LABORATORY = Laboratory(id=1, name="mylab", pi_name="PI", full_name="My Laboratory")
|
|
|
|
|
|
def make_locked_folder() -> Folder:
|
|
return Folder(
|
|
id="f1",
|
|
pid=None,
|
|
name="root",
|
|
access_level=4,
|
|
lock=True,
|
|
size=0,
|
|
laboratory_id=1,
|
|
description="",
|
|
created_at=TIMESTAMP,
|
|
updated_at=TIMESTAMP,
|
|
restrict_opened_at=None,
|
|
metadata=[],
|
|
sub_folders=[],
|
|
path="/root/",
|
|
)
|
|
|
|
|
|
# What the server returns for a locked folder not yet opened with its password.
|
|
SHORT_RECORD = {
|
|
"id": "f1",
|
|
"pid": None,
|
|
"name": "root",
|
|
"access_level": 4,
|
|
"lock": True,
|
|
"size": 0,
|
|
"laboratory_id": 1,
|
|
"description": "",
|
|
"created_at": TIMESTAMP,
|
|
"updated_at": TIMESTAMP,
|
|
"restrict_opened_at": None,
|
|
"application": {"id": 0, "status": 0},
|
|
}
|
|
|
|
|
|
class TestRetrieveLockedFolder(unittest.TestCase):
|
|
def retrieve(self, data: dict) -> Folder:
|
|
connection = MagicMock()
|
|
connection.get.return_value.json.return_value = data
|
|
with patch.object(FoldersApi, "_raise_response_error"):
|
|
return FoldersApi(connection).retrieve("f1")
|
|
|
|
def test_short_record_means_locked(self):
|
|
with self.assertRaisesRegex(FolderLockedException, "Folder `root` is locked."):
|
|
self.retrieve(SHORT_RECORD)
|
|
|
|
def test_full_record_of_a_locked_folder_is_read(self):
|
|
full = dict(SHORT_RECORD, metadata=[], sub_folders=[], path="/root/")
|
|
self.assertEqual(self.retrieve(full).path, "/root/")
|
|
|
|
|
|
class TestFindFolderByDoi(unittest.TestCase):
|
|
"""The DOI response carries the folder id as a field, not as a nested object."""
|
|
|
|
def find(self, retrieved: list, password: str | None) -> tuple[MagicMock, tuple]:
|
|
laboratories = Laboratories()
|
|
laboratories.append(LABORATORY)
|
|
|
|
with (
|
|
patch("mdrsclient.services.DoiApi") as doi_api,
|
|
patch("mdrsclient.services.FoldersApi") as folders_api,
|
|
patch("mdrsclient.services.LaboratoriesApi") as laboratories_api,
|
|
):
|
|
doi_api.return_value.retrieve.return_value = Doi(id="20260429-001", doi=DOI, folder_id="f1")
|
|
folders_api.return_value.retrieve.side_effect = retrieved
|
|
laboratories_api.return_value.list.return_value = laboratories
|
|
return folders_api.return_value, MdrsService(MagicMock()).find_folder_by_doi(DOI, password)
|
|
|
|
def test_a_locked_doi_folder_is_unlocked_with_its_folder_id(self):
|
|
api, (folder, laboratory) = self.find([FolderLockedException("locked"), make_locked_folder()], "secret")
|
|
api.auth.assert_called_once_with("f1", "secret")
|
|
self.assertEqual(api.retrieve.call_count, 2)
|
|
self.assertEqual(folder.id, "f1")
|
|
self.assertEqual(laboratory.name, "mylab")
|
|
|
|
def test_a_locked_doi_folder_without_a_password(self):
|
|
with self.assertRaisesRegex(FolderLockedException, f"Folder for DOI `{DOI}` is locked."):
|
|
self.find([FolderLockedException("locked")], None)
|
|
|
|
def test_a_full_record_still_asks_for_the_password(self):
|
|
# A locked folder whose record is readable anyway, e.g. its owner's.
|
|
api, (folder, _) = self.find([make_locked_folder()], "secret")
|
|
api.auth.assert_called_once_with("f1", "secret")
|
|
self.assertEqual(api.retrieve.call_count, 1)
|
|
with self.assertRaises(FolderLockedException):
|
|
self.find([make_locked_folder()], None)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|