Labels came from a fixed table matched by subset, so Storage (no bits) matched any level the table lacked, and a level was named after the first entry it contained: the server's Brain/MINDS 2.0 folders (0x10000) showed as Storage and 0x10104 as CBS or PW Open. Labels are now composed from the bits. Projects (bit 16 and up) take the names the server reports to their members through users/current/, and otherwise Project<n>; restrict slot 1 is CBS and any other Restrict<n>. A level with no meaning is shown as Unknown (0x...). chacl reads keys built the same way, refuses public and storage before any request, and lists the user's projects with their keys when given an invalid level.
464 lines
20 KiB
Python
464 lines
20 KiB
Python
import argparse
|
|
import json
|
|
import unittest
|
|
from io import StringIO
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from mdrsclient.client import MdrsClient
|
|
from mdrsclient.commands import (
|
|
ChaclCommand,
|
|
ConfigCommand,
|
|
CpCommand,
|
|
DownloadCommand,
|
|
FileMetadataCommand,
|
|
LabsCommand,
|
|
LoginCommand,
|
|
LogoutCommand,
|
|
LsCommand,
|
|
MetadataCommand,
|
|
MkdirCommand,
|
|
MvCommand,
|
|
RmCommand,
|
|
UploadCommand,
|
|
VersionCommand,
|
|
WhoamiCommand,
|
|
)
|
|
from mdrsclient.exceptions import IllegalArgumentException, MissingConfigurationException
|
|
from mdrsclient.models import Folder, Laboratory
|
|
|
|
|
|
class TestCommands(unittest.TestCase):
|
|
def parse_args(self, cmd_class, args_list):
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(title="subcommands")
|
|
cmd_class.register(subparsers)
|
|
return parser.parse_args(args_list)
|
|
|
|
def test_version_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.return_value = mock_client
|
|
mock_client.version.return_value = "mdrs 1.3.17"
|
|
|
|
args = self.parse_args(VersionCommand, ["version"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
self.assertEqual(fake_out.getvalue().strip(), "mdrs 1.3.17")
|
|
|
|
mock_client_class.assert_called_once_with(None)
|
|
mock_client.version.assert_called_once()
|
|
|
|
def test_login_command_with_args(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client_class.parse_remote_host.return_value = "myremote"
|
|
|
|
args = self.parse_args(LoginCommand, ["login", "-u", "myuser", "-p", "mypass", "myremote"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
self.assertIn("Login Successful", fake_out.getvalue())
|
|
|
|
mock_client_class.parse_remote_host.assert_called_once_with("myremote")
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.login.assert_called_once_with("myuser", "mypass")
|
|
|
|
def test_login_command_interactive(self):
|
|
with (
|
|
patch("mdrsclient.client.MdrsClient") as mock_client_class,
|
|
patch("builtins.input", return_value="myuser_int") as mock_input,
|
|
patch("getpass.getpass", return_value="mypass_int") as mock_getpass,
|
|
):
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client_class.parse_remote_host.return_value = "myremote"
|
|
|
|
args = self.parse_args(LoginCommand, ["login", "myremote"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
self.assertIn("Login Successful", fake_out.getvalue())
|
|
|
|
mock_input.assert_called_once_with("Username: ")
|
|
mock_getpass.assert_called_once_with("Password: ")
|
|
mock_client.login.assert_called_once_with("myuser_int", "mypass_int")
|
|
|
|
def test_logout_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client_class.parse_remote_host.return_value = "myremote"
|
|
|
|
args = self.parse_args(LogoutCommand, ["logout", "myremote"])
|
|
args.func(args)
|
|
|
|
mock_client_class.parse_remote_host.assert_called_once_with("myremote")
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.logout.assert_called_once()
|
|
|
|
def test_whoami_command_logged_in(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client_class.parse_remote_host.return_value = "myremote"
|
|
mock_client.connection.token = None
|
|
|
|
mock_user = MagicMock()
|
|
mock_user.username = "test_user"
|
|
mock_client.whoami.return_value = mock_user
|
|
|
|
args = self.parse_args(WhoamiCommand, ["whoami", "myremote"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
self.assertEqual(fake_out.getvalue().strip(), "test_user")
|
|
|
|
mock_client_class.parse_remote_host.assert_called_once_with("myremote")
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.whoami.assert_called_once()
|
|
|
|
def test_whoami_command_anonymous(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client_class.parse_remote_host.return_value = "myremote"
|
|
mock_client.connection.token = None
|
|
mock_client.whoami.side_effect = Exception("Not logged in")
|
|
|
|
args = self.parse_args(WhoamiCommand, ["whoami", "myremote"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
self.assertEqual(fake_out.getvalue().strip(), "(Anonymous)")
|
|
|
|
def test_labs_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client_class.parse_remote_host.return_value = "myremote"
|
|
|
|
mock_lab = MagicMock()
|
|
mock_lab.id = 1
|
|
mock_lab.name = "lab_name"
|
|
mock_lab.pi_name = "pi_name"
|
|
mock_lab.full_name = "full_name"
|
|
mock_client.get_laboratories.return_value = [mock_lab]
|
|
|
|
args = self.parse_args(LabsCommand, ["labs", "myremote"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
output = fake_out.getvalue()
|
|
self.assertIn("Name", output)
|
|
self.assertIn("lab_name", output)
|
|
self.assertIn("pi_name", output)
|
|
|
|
mock_client_class.parse_remote_host.assert_called_once_with("myremote")
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.get_laboratories.assert_called_once()
|
|
|
|
def test_ls_command_plain(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock(spec=MdrsClient)
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
mock_folder = Folder(
|
|
id="folder_id",
|
|
pid=None,
|
|
name="root",
|
|
access_level=1,
|
|
lock=False,
|
|
size=0,
|
|
laboratory_id=1,
|
|
description="",
|
|
created_at="2026-07-02T00:00:00Z",
|
|
updated_at="2026-07-02T00:00:00Z",
|
|
restrict_opened_at=None,
|
|
metadata=[],
|
|
sub_folders=[],
|
|
path="/root",
|
|
)
|
|
|
|
mock_lab = Laboratory(id=1, name="mylab", pi_name="pi_name", full_name="full_name")
|
|
|
|
mock_client.resolve_folder.return_value = (mock_folder, mock_lab)
|
|
mock_client.project_names.return_value = {}
|
|
|
|
mock_file = MagicMock()
|
|
mock_file.name = "file.txt"
|
|
mock_file.size = 100
|
|
mock_file.updated_at_name = "2026-07-02"
|
|
mock_client.find_files.return_value = [mock_file]
|
|
|
|
args = self.parse_args(LsCommand, ["ls", "myremote:/mylab/"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
output = fake_out.getvalue()
|
|
self.assertIn("Type", output)
|
|
self.assertIn("file.txt", output)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.resolve_folder.assert_called_once_with("myremote:/mylab/", None)
|
|
mock_client.find_files.assert_called_once_with(mock_folder.id)
|
|
|
|
def test_ls_command_json(self):
|
|
with (
|
|
patch("mdrsclient.client.MdrsClient") as mock_client_class,
|
|
patch("mdrsclient.commands.ls.FoldersApi") as mock_folders_api_class,
|
|
):
|
|
mock_folders_api = MagicMock()
|
|
mock_folders_api_class.return_value = mock_folders_api
|
|
mock_folders_api.metadata.return_value = {"folder_meta": "val"}
|
|
|
|
mock_client = MagicMock(spec=MdrsClient)
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client.connection = MagicMock()
|
|
|
|
mock_folder = Folder(
|
|
id="folder_id",
|
|
pid="parent_id",
|
|
name="root",
|
|
access_level=1,
|
|
lock=False,
|
|
size=0,
|
|
laboratory_id=1,
|
|
description="Root folder",
|
|
created_at="2026-07-02T00:00:00Z",
|
|
updated_at="2026-07-02T00:00:00Z",
|
|
restrict_opened_at=None,
|
|
metadata=[],
|
|
sub_folders=[],
|
|
path="/root",
|
|
)
|
|
|
|
mock_lab = Laboratory(id=1, name="mylab", pi_name="pi_name", full_name="full_name")
|
|
|
|
mock_client.resolve_folder.return_value = (mock_folder, mock_lab)
|
|
mock_client.project_names.return_value = {}
|
|
mock_client.connection.laboratories.find_by_id.return_value = mock_lab
|
|
|
|
mock_file = MagicMock()
|
|
mock_file.id = "file_id"
|
|
mock_file.name = "file.txt"
|
|
mock_file.type = "text"
|
|
mock_file.size = 100
|
|
mock_file.description = "A file"
|
|
mock_file.metadata = {}
|
|
mock_file.download_url = "download/file"
|
|
mock_file.created_at = "2026-07-02T00:00:00Z"
|
|
mock_file.updated_at = "2026-07-02T00:00:00Z"
|
|
mock_client.find_files.return_value = [mock_file]
|
|
|
|
args = self.parse_args(LsCommand, ["ls", "-J", "myremote:/mylab/"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
output = fake_out.getvalue()
|
|
parsed_json = json.loads(output)
|
|
self.assertEqual(parsed_json["name"], "root")
|
|
self.assertEqual(parsed_json["files"][0]["name"], "file.txt")
|
|
|
|
def test_mkdir_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(MkdirCommand, ["mkdir", "myremote:/mylab/newfolder"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.mkdir.assert_called_once_with("myremote:/mylab/newfolder")
|
|
|
|
def test_rm_command_file(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(RmCommand, ["rm", "myremote:/mylab/file.txt"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.rm.assert_called_once_with("myremote:/mylab/file.txt", False)
|
|
|
|
def test_rm_command_recursive(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(RmCommand, ["rm", "-r", "myremote:/mylab/folder"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.rm.assert_called_once_with("myremote:/mylab/folder", True)
|
|
|
|
def test_cp_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(CpCommand, ["cp", "myremote:/mylab/src", "myremote:/mylab/dest"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.cp.assert_called_once_with("myremote:/mylab/src", "myremote:/mylab/dest", False)
|
|
|
|
def test_mv_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(MvCommand, ["mv", "myremote:/mylab/src", "myremote:/mylab/dest"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.mv.assert_called_once_with("myremote:/mylab/src", "myremote:/mylab/dest")
|
|
|
|
def test_chacl_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(ChaclCommand, ["chacl", "-r", "-p", "secret", "private", "myremote:/mylab/"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.chacl.assert_called_once_with("myremote:/mylab/", 1, True, "secret")
|
|
|
|
def test_chacl_command_project_level(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(ChaclCommand, ["chacl", "cbs_or_project0_open", "myremote:/mylab/"])
|
|
args.func(args)
|
|
|
|
mock_client.chacl.assert_called_once_with("myremote:/mylab/", 0x10100, False, None)
|
|
|
|
def test_chacl_command_invalid_level_lists_projects(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client.project_names.return_value = {0: "Brain/MINDS 2.0"}
|
|
|
|
args = self.parse_args(ChaclCommand, ["chacl", "5kikan_open", "myremote:/mylab/"])
|
|
with self.assertRaises(IllegalArgumentException) as cm:
|
|
args.func(args)
|
|
|
|
self.assertIn("project<n>_open", str(cm.exception))
|
|
self.assertIn("project0_open: Brain/MINDS 2.0", str(cm.exception))
|
|
mock_client.chacl.assert_not_called()
|
|
|
|
def test_chacl_command_invalid_level_without_server(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client_class.from_remote.side_effect = MissingConfigurationException("no remote")
|
|
|
|
args = self.parse_args(ChaclCommand, ["chacl", "5kikan_open", "myremote:/mylab/"])
|
|
with self.assertRaises(IllegalArgumentException) as cm:
|
|
args.func(args)
|
|
|
|
self.assertNotIn("Projects you belong to", str(cm.exception))
|
|
|
|
def test_chacl_command_refuses_public_and_storage(self):
|
|
for level in ("public", "storage"):
|
|
with self.subTest(level=level), patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
args = self.parse_args(ChaclCommand, ["chacl", level, "myremote:/mylab/"])
|
|
with self.assertRaises(IllegalArgumentException):
|
|
args.func(args)
|
|
mock_client_class.from_remote.assert_not_called()
|
|
|
|
def test_metadata_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client.metadata.return_value = {"meta_key": "meta_val"}
|
|
|
|
args = self.parse_args(MetadataCommand, ["metadata", "-p", "secret", "myremote:/mylab/"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
parsed_json = json.loads(fake_out.getvalue())
|
|
self.assertEqual(parsed_json["meta_key"], "meta_val")
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.metadata.assert_called_once_with("myremote:/mylab/", "secret")
|
|
|
|
def test_file_metadata_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
mock_client.file_metadata.return_value = {"file_meta": "val"}
|
|
|
|
args = self.parse_args(FileMetadataCommand, ["file-metadata", "-p", "secret", "myremote:/mylab/file.txt"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
parsed_json = json.loads(fake_out.getvalue())
|
|
self.assertEqual(parsed_json["file_meta"], "val")
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.file_metadata.assert_called_once_with("myremote:/mylab/file.txt", "secret")
|
|
|
|
def test_upload_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(UploadCommand, ["upload", "-r", "-s", "local_file.txt", "myremote:/mylab/"])
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.upload.assert_called_once_with("local_file.txt", "myremote:/mylab/", True, True)
|
|
|
|
def test_download_command(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.from_remote.return_value = mock_client
|
|
|
|
args = self.parse_args(
|
|
DownloadCommand, ["download", "-r", "-s", "-p", "pass", "-e", "ex1", "myremote:/mylab/", "local_dir"]
|
|
)
|
|
args.func(args)
|
|
|
|
mock_client_class.from_remote.assert_called_once_with("myremote")
|
|
mock_client.download.assert_called_once_with("myremote:/mylab/", "local_dir", True, True, "pass", ["ex1"])
|
|
|
|
def test_config_create(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.return_value = mock_client
|
|
|
|
args = self.parse_args(ConfigCommand, ["config", "create", "myremote", "http://example.com"])
|
|
args.func(args)
|
|
|
|
mock_client_class.assert_called_once_with(None)
|
|
mock_client.config_create.assert_called_once_with("myremote", "http://example.com")
|
|
|
|
def test_config_update(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.return_value = mock_client
|
|
|
|
args = self.parse_args(ConfigCommand, ["config", "update", "myremote", "http://example.com"])
|
|
args.func(args)
|
|
|
|
mock_client_class.assert_called_once_with(None)
|
|
mock_client.config_update.assert_called_once_with("myremote", "http://example.com")
|
|
|
|
def test_config_delete(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.return_value = mock_client
|
|
|
|
args = self.parse_args(ConfigCommand, ["config", "delete", "myremote"])
|
|
args.func(args)
|
|
|
|
mock_client_class.assert_called_once_with(None)
|
|
mock_client.config_delete.assert_called_once_with("myremote")
|
|
|
|
def test_config_list(self):
|
|
with patch("mdrsclient.client.MdrsClient") as mock_client_class:
|
|
mock_client = MagicMock()
|
|
mock_client_class.return_value = mock_client
|
|
mock_client.config_list.return_value = [("remote1", "url1"), ("remote2", "url2")]
|
|
|
|
args = self.parse_args(ConfigCommand, ["config", "list"])
|
|
with patch("sys.stdout", new=StringIO()) as fake_out:
|
|
args.func(args)
|
|
self.assertEqual(fake_out.getvalue(), "remote1:\turl1\nremote2:\turl2\n")
|
|
|
|
mock_client_class.assert_called_once_with(None)
|
|
mock_client.config_list.assert_called_once()
|