import unittest from unittest.mock import Mock, patch from click.testing import CliRunner from tests.helpers import TestContext, temporary_root from tutor.__about__ import __version__ from tutor import images, plugins from tutor.commands.config import config_command from tutor.commands.images import ImageNotFoundError, images_command class ImagesTests(unittest.TestCase): def test_images_help(self) -> None: runner = CliRunner() result = runner.invoke(images_command, ["--help"]) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) def test_images_pull_image(self) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() result = runner.invoke(images_command, ["pull"], obj=context) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) def test_images_pull_plugin_invalid_plugin_should_throw_error(self) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() result = runner.invoke(images_command, ["pull", "plugin"], obj=context) self.assertEqual(1, result.exit_code) self.assertEqual(ImageNotFoundError, type(result.exception)) @patch.object(plugins.BasePlugin, "iter_installed", return_value=[]) @patch.object( plugins.Plugins, "iter_hooks", return_value=[ ( "dev-plugins", {"plugin": "plugin:dev-1.0.0", "plugin2": "plugin2:dev-1.0.0"}, ) ], ) @patch.object(images, "pull", return_value=None) def test_images_pull_plugin( self, _image_pull: Mock, iter_hooks: Mock, iter_installed: Mock ) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() result = runner.invoke(images_command, ["pull", "plugin"], obj=context) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) iter_hooks.assert_called_once_with("remote-image") _image_pull.assert_called_once_with("plugin:dev-1.0.0") iter_installed.assert_called() def test_images_printtag_image(self) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() result = runner.invoke(images_command, ["printtag", "openedx"], obj=context) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) self.assertRegex( result.output, r"docker.io/overhangio/openedx:{}\n".format(__version__) ) @patch.object(plugins.BasePlugin, "iter_installed", return_value=[]) @patch.object( plugins.Plugins, "iter_hooks", return_value=[ ( "dev-plugins", {"plugin": "plugin:dev-1.0.0", "plugin2": "plugin2:dev-1.0.0"}, ) ], ) def test_images_printtag_plugin( self, iter_hooks: Mock, iter_installed: Mock ) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() result = runner.invoke(images_command, ["printtag", "plugin"], obj=context) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) iter_hooks.assert_called_once_with("build-image") iter_installed.assert_called() self.assertEqual(result.output, "plugin:dev-1.0.0\n") @patch.object(plugins.BasePlugin, "iter_installed", return_value=[]) @patch.object( plugins.Plugins, "iter_hooks", return_value=[ ( "dev-plugins", {"plugin": "plugin:dev-1.0.0", "plugin2": "plugin2:dev-1.0.0"}, ) ], ) @patch.object(images, "build", return_value=None) def test_images_build_plugin( self, image_build: Mock, iter_hooks: Mock, iter_installed: Mock ) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() runner.invoke(config_command, ["save"], obj=context) result = runner.invoke(images_command, ["build", "plugin"], obj=context) self.assertIsNone(result.exception) self.assertEqual(0, result.exit_code) image_build.assert_called() iter_hooks.assert_called_once_with("build-image") iter_installed.assert_called() self.assertIn("plugin:dev-1.0.0", image_build.call_args[0]) @patch.object(plugins.BasePlugin, "iter_installed", return_value=[]) @patch.object( plugins.Plugins, "iter_hooks", return_value=[ ( "dev-plugins", {"plugin": "plugin:dev-1.0.0", "plugin2": "plugin2:dev-1.0.0"}, ) ], ) @patch.object(images, "build", return_value=None) def test_images_build_plugin_with_args( self, image_build: Mock, iter_hooks: Mock, iter_installed: Mock ) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() runner.invoke(config_command, ["save"], obj=context) args = [ "build", "--no-cache", "-a", "myarg=value", "--add-host", "host", "--target", "target", "-d", "docker_args", "plugin", ] result = runner.invoke( images_command, args, obj=context, ) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception) iter_hooks.assert_called_once_with("build-image") iter_installed.assert_called() image_build.assert_called() self.assertIn("plugin:dev-1.0.0", image_build.call_args[0]) for arg in image_build.call_args[0][2:]: if arg == "--build-arg": continue self.assertIn(arg, args) def test_images_push(self) -> None: with temporary_root() as root: context = TestContext(root) runner = CliRunner() result = runner.invoke(images_command, ["push"], obj=context) self.assertEqual(0, result.exit_code) self.assertIsNone(result.exception)