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