class TestInstallCommands(TestCase): def setUp(self): from tethys_apps.models import TethysApp self.src_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) self.root_app_path = os.path.join(self.src_dir, 'apps', 'tethysapp-test_app') self.app_model = TethysApp( name='test_app', package='test_app' ) self.app_model.save() def tearDown(self): self.app_model.delete() @mock.patch('tethys_cli.cli_colors.pretty_output') @mock.patch('builtins.input', side_effect=['x', 'n']) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.install_commands.call') def test_install_file_not_generate(self, mock_call, mock_exit, _, __): args = mock.MagicMock(file=None, quiet=False, only_dependencies=False, without_dependencies=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) self.assertEqual(3, len(mock_call.call_args_list)) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.cli_colors.pretty_output') @mock.patch('builtins.input', side_effect=['y']) @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.exit') def test_install_file_generate(self, mock_exit, mock_call, _, __): args = mock.MagicMock(file=None, quiet=False, only_dependencies=False, without_dependencies=False) check_call = ['tethys', 'gen', 'install'] mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) mock_call.assert_called_with(check_call) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_no_conda_input_file(self, mock_pretty_output, mock_exit, _, __): file_path = os.path.join(self.root_app_path, 'install-no-dep.yml') args = mock.MagicMock(file=file_path, verbose=False, only_dependencies=False, without_dependencies=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Running application install....", po_call_args[0][0][0]) self.assertIn("Quiet mode: No additional service setting validation will be performed.", po_call_args[1][0][0]) self.assertIn("Services Configuration Completed.", po_call_args[2][0][0]) self.assertIn("Skipping syncstores.", po_call_args[3][0][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_input_file_with_post(self, mock_pretty_output, mock_exit, _, __): file_path = os.path.join(self.root_app_path, 'install-with-post.yml') args = mock.MagicMock(file=file_path, verbose=False, only_dependencies=False, without_dependencies=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Running application install....", po_call_args[1][0][0]) self.assertIn("Quiet mode: No additional service setting validation will be performed.", po_call_args[2][0][0]) self.assertIn("Services Configuration Completed.", po_call_args[3][0][0]) self.assertIn("Skipping syncstores.", po_call_args[4][0][0]) self.assertIn("Running post installation tasks...", po_call_args[5][0][0]) self.assertIn("Post Script Result: b'test\\n'", po_call_args[6][0][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.run_sync_stores') @mock.patch('tethys_cli.install_commands.run_interactive_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.run_portal_install', return_value=False) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_skip_input_file(self, mock_pretty_output, mock_exit, _, __, ___, ____, _____, ______): file_path = os.path.join(self.root_app_path, 'install-skip-setup.yml') mock_exit.side_effect = SystemExit args = mock.MagicMock(file=file_path, verbose=False, only_dependencies=False, without_dependencies=False) self.assertRaises(SystemExit, install_commands.install_command, args) args = mock.MagicMock(file=file_path, develop=False, only_dependencies=False, without_dependencies=False) self.assertRaises(SystemExit, install_commands.install_command, args) args = mock.MagicMock(file=file_path, verbose=False, develop=False, force_services=False, quiet=False, no_sync=False, only_dependencies=False, without_dependencies=False) self.assertRaises(SystemExit, install_commands.install_command, args) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Skipping package installation, Skip option found.", po_call_args[0][0][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.conda_run', return_value=['', '', 1]) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_conda_and_pip_package_install(self, mock_pretty_output, mock_exit, mock_conda_run, mock_call, _): file_path = os.path.join(self.root_app_path, 'install-dep.yml') args = mock.MagicMock(file=file_path, develop=False, verbose=False, services_file=None, update_installed=False, only_dependencies=False, without_dependencies=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) mock_conda_run.assert_called_with(Commands.INSTALL, '-c', 'tacaswell', '--freeze-installed', 'geojson', use_exception_handler=False, stdout=None, stderr=None) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Running conda installation tasks...", po_call_args[0][0][0]) self.assertIn("Warning: Packages installation ran into an error.", po_call_args[1][0][0]) self.assertEqual("Running pip installation tasks...", po_call_args[2][0][0]) self.assertEqual("Running application install....", po_call_args[3][0][0]) self.assertEqual("Quiet mode: No additional service setting validation will be performed.", po_call_args[4][0][0]) self.assertEqual("Services Configuration Completed.", po_call_args[5][0][0]) self.assertEqual(['pip', 'install', 'see'], mock_call.mock_calls[0][1][0]) self.assertEqual(['python', 'setup.py', 'clean', '--all'], mock_call.mock_calls[1][1][0]) self.assertEqual(['python', 'setup.py', 'install'], mock_call.mock_calls[2][1][0]) self.assertEqual(['tethys', 'db', 'sync'], mock_call.mock_calls[3][1][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.conda_run', return_value=['', '', 1]) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_without_dependencies(self, mock_pretty_output, mock_exit, mock_conda_run, mock_call, _): file_path = os.path.join(self.root_app_path, 'install-dep.yml') args = mock.MagicMock(file=file_path, develop=False, verbose=False, services_file=None, update_installed=False, only_dependencies=False, without_dependencies=True) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) # Ensure conda command wasn't called to install dependencies mock_conda_run.assert_not_called() # Make sure 'pip install' isn't in any of the calls self.assertFalse(any(['pip install' in ' '.join(mc[1][0]) for mc in mock_call.mock_calls])) # Validate output displayed to the user po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Skipping package installation.", po_call_args[0][0][0]) self.assertEqual("Running application install....", po_call_args[1][0][0]) self.assertEqual("Quiet mode: No additional service setting validation will be performed.", po_call_args[2][0][0]) self.assertEqual("Services Configuration Completed.", po_call_args[3][0][0]) # Verify that the application install still happens self.assertEqual(['python', 'setup.py', 'clean', '--all'], mock_call.mock_calls[0][1][0]) self.assertEqual(['python', 'setup.py', 'install'], mock_call.mock_calls[1][1][0]) self.assertEqual(['tethys', 'db', 'sync'], mock_call.mock_calls[2][1][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.conda_run', return_value=['', '', 1]) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_conda_and_pip_package_install_only_dependencies(self, mock_pretty_output, mock_exit, mock_conda_run, mock_call, _): file_path = os.path.join(self.root_app_path, 'install-dep.yml') args = mock.MagicMock(file=file_path, develop=False, verbose=False, services_file=None, update_installed=False, only_dependencies=True, without_dependencies=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) mock_conda_run.assert_called_with(Commands.INSTALL, '-c', 'tacaswell', '--freeze-installed', 'geojson', use_exception_handler=False, stdout=None, stderr=None) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Running conda installation tasks...", po_call_args[0][0][0]) self.assertIn("Warning: Packages installation ran into an error.", po_call_args[1][0][0]) self.assertEqual("Running pip installation tasks...", po_call_args[2][0][0]) self.assertEqual("Installed dependencies only. Skipping remaining installation.", po_call_args[3][0][0]) self.assertEqual(1, len(mock_call.mock_calls)) self.assertEqual(['pip', 'install', 'see'], mock_call.mock_calls[0][1][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.conda_run', return_value=['', '', 1]) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_conda_and_pip_package_install_update_installed(self, mock_pretty_output, mock_exit, mock_conda_run, mock_call, _): file_path = os.path.join(self.root_app_path, 'install-dep.yml') args = mock.MagicMock(file=file_path, develop=False, verbose=False, services_file=None, update_installed=True, only_dependencies=False, without_dependencies=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) mock_conda_run.assert_called_with(Commands.INSTALL, '-c', 'tacaswell', 'geojson', use_exception_handler=False, stdout=None, stderr=None) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Warning: Updating previously installed packages. This could break your Tethys environment.", po_call_args[0][0][0]) self.assertEqual("Running conda installation tasks...", po_call_args[1][0][0]) self.assertIn("Warning: Packages installation ran into an error.", po_call_args[2][0][0]) self.assertEqual("Running pip installation tasks...", po_call_args[3][0][0]) self.assertEqual("Running application install....", po_call_args[4][0][0]) self.assertEqual("Quiet mode: No additional service setting validation will be performed.", po_call_args[5][0][0]) self.assertEqual("Services Configuration Completed.", po_call_args[6][0][0]) self.assertEqual(['pip', 'install', 'see'], mock_call.mock_calls[0][1][0]) self.assertEqual(['python', 'setup.py', 'clean', '--all'], mock_call.mock_calls[1][1][0]) self.assertEqual(['python', 'setup.py', 'install'], mock_call.mock_calls[2][1][0]) self.assertEqual(['tethys', 'db', 'sync'], mock_call.mock_calls[3][1][0]) mock_exit.assert_called_with(0) @mock.patch('builtins.input', side_effect=['x', 5]) @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_custom_setting_set(self, mock_pretty_output, mock_gas, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_cs.save.side_effect = [ValidationError('error'), mock.DEFAULT] mock_gas.return_value = {'unlinked_settings': [mock_cs]} install_commands.run_interactive_services('foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_cs", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("Enter the desired value", po_call_args[4][0][0]) self.assertIn("Incorrect value type", po_call_args[5][0][0]) self.assertIn("Enter the desired value", po_call_args[6][0][0]) self.assertEqual(mock_cs.name + " successfully set with value: 5.", po_call_args[7][0][0]) @mock.patch('builtins.input', side_effect=['']) @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_custom_setting_skip(self, mock_pretty_output, mock_gas, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_gas.return_value = {'unlinked_settings': [mock_cs]} install_commands.run_interactive_services('foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_cs", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("Enter the desired value", po_call_args[4][0][0]) self.assertEqual(f"Skipping setup of {mock_cs.name}", po_call_args[5][0][0]) @mock.patch('builtins.input', side_effect=KeyboardInterrupt) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_custom_setting_interrupt(self, mock_pretty_output, mock_gas, mock_exit, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_gas.return_value = {'unlinked_settings': [mock_cs]} mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.run_interactive_services, 'foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_cs", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("Enter the desired value", po_call_args[4][0][0]) self.assertEqual("\nInstall Command cancelled.", po_call_args[5][0][0]) mock_exit.assert_called_with(0) @mock.patch('builtins.input', side_effect=['1', '1', '', '1', '1', KeyboardInterrupt]) @mock.patch('tethys_cli.install_commands.validate_service_id', side_effect=[False, True]) @mock.patch('tethys_cli.install_commands.get_setting_type', return_value='persistent') @mock.patch('tethys_cli.install_commands.get_setting_type_from_setting', side_effect=['ps_database', 'ps_database', 'ps_database', RuntimeError('setting_type_not_found')]) @mock.patch('tethys_cli.install_commands.get_service_type_from_setting', side_effect=['persistent', 'persistent', RuntimeError('service_type_not_found'), 'persistent']) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.install_commands.services_list_command') @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.install_commands.link_service_to_app_setting') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_service_setting_all(self, mock_pretty_output, mock_lstas, mock_gas, mock_slc, mock_exit, _, __, ___, ____, _____): mock_ss = mock.MagicMock() del mock_ss.value mock_ss.name = 'mock_ss' mock_ss.description = 'This is a fake setting for testing.' mock_ss.required = True mock_ss.save.side_effect = [ValidationError('error'), mock.DEFAULT] mock_gas.return_value = {'unlinked_settings': [mock_ss, mock_ss, mock_ss, mock_ss, mock_ss, mock_ss]} mock_s = mock.MagicMock() mock_slc.side_effect = [[[]], [[mock_s]], [[mock_s]], [[mock_s]], [[mock_s]], [[mock_s]]] mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.run_interactive_services, 'foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual('Running Interactive Service Mode. Any configuration options in services.yml or ' 'portal_config.yml will be ignored...', po_call_args[0][0][0]) self.assertIn("Hit return at any time to skip a step.", po_call_args[1][0][0]) self.assertIn("Configuring mock_ss", po_call_args[2][0][0]) self.assertIn("Type: MagicMock", po_call_args[3][0][0]) self.assertIn(f"Description: {mock_ss.description}", po_call_args[3][0][0]) self.assertIn(f"Required: {mock_ss.required}", po_call_args[3][0][0]) self.assertIn("No compatible services found.", po_call_args[4][0][0]) self.assertIn("tethys services create persistent -h", po_call_args[4][0][0]) self.assertIn("Enter the service ID/Name", po_call_args[7][0][0]) self.assertIn("Incorrect service ID/Name. Please try again.", po_call_args[8][0][0]) self.assertIn("Enter the service ID/Name", po_call_args[9][0][0]) self.assertIn(f"Skipping setup of {mock_ss.name}", po_call_args[13][0][0]) self.assertEqual("service_type_not_found Skipping...", po_call_args[17][0][0]) self.assertEqual("setting_type_not_found Skipping...", po_call_args[21][0][0]) self.assertEqual("\nInstall Command cancelled.", po_call_args[25][0][0]) mock_lstas.assert_called_with('persistent', '1', 'foo', 'ps_database', 'mock_ss') mock_exit.assert_called_with(0) @mock.patch('builtins.input', side_effect=['x', 5]) @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_no_settings(self, mock_pretty_output, mock_gas, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_cs.save.side_effect = [ValidationError('error'), mock.DEFAULT] mock_gas.return_value = None install_commands.run_interactive_services('foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn('No settings found for app "foo". Skipping interactive configuration...', po_call_args[2][0][0])
class TestInstallCommands(TestCase): def setUp(self): from tethys_apps.models import TethysApp self.src_dir = os.path.dirname( os.path.dirname(os.path.dirname(__file__))) self.root_app_path = os.path.join(self.src_dir, 'apps', 'tethysapp-test_app') self.app_model = TethysApp(name='test_app', package='test_app') self.app_model.save() pass def tearDown(self): self.app_model.delete() pass @mock.patch('tethys_cli.cli_colors.pretty_output') @mock.patch('builtins.input', side_effect=['x', 'n']) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.install_commands.call') def test_install_file_not_generate(self, mock_call, mock_exit, _, __): args = mock.MagicMock(file=None, quiet=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) self.assertEqual(3, len(mock_call.call_args_list)) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.cli_colors.pretty_output') @mock.patch('builtins.input', side_effect=['y']) @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.exit') def test_install_file_generate(self, mock_exit, mock_call, _, __): args = mock.MagicMock(file=None, quiet=False) check_call = ['tethys', 'gen', 'install'] mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) mock_call.assert_called_with(check_call) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_no_conda_input_file(self, mock_pretty_output, mock_exit, _, __): file_path = os.path.join(self.root_app_path, 'install-no-dep.yml') args = mock.MagicMock(file=file_path, verbose=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Running application install....", po_call_args[0][0][0]) self.assertIn( "Quiet mode: No additional service setting validation will be performed.", po_call_args[1][0][0]) self.assertIn("Services Configuration Completed.", po_call_args[2][0][0]) self.assertIn("Skipping syncstores.", po_call_args[3][0][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_input_file_with_post(self, mock_pretty_output, mock_exit, _, __): file_path = os.path.join(self.root_app_path, 'install-with-post.yml') args = mock.MagicMock(file=file_path, verbose=False) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Running application install....", po_call_args[1][0][0]) self.assertIn( "Quiet mode: No additional service setting validation will be performed.", po_call_args[2][0][0]) self.assertIn("Services Configuration Completed.", po_call_args[3][0][0]) self.assertIn("Skipping syncstores.", po_call_args[4][0][0]) self.assertIn("Running post installation tasks...", po_call_args[5][0][0]) self.assertIn("Post Script Result: b'test\\n'", po_call_args[6][0][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.run_sync_stores') @mock.patch('tethys_cli.install_commands.run_interactive_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.run_portal_install', return_value=False) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_skip_input_file(self, mock_pretty_output, mock_exit, _, __, ___, ____, _____, ______): file_path = os.path.join(self.root_app_path, 'install-skip-setup.yml') mock_exit.side_effect = SystemExit args = mock.MagicMock(file=file_path, verbose=False) self.assertRaises(SystemExit, install_commands.install_command, args) args = mock.MagicMock(file=file_path, develop=False) self.assertRaises(SystemExit, install_commands.install_command, args) args = mock.MagicMock(file=file_path, verbose=False, develop=False, force_services=False, quiet=False, no_sync=False) self.assertRaises(SystemExit, install_commands.install_command, args) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Skipping package installation, Skip option found.", po_call_args[0][0][0]) mock_exit.assert_called_with(0) @mock.patch('tethys_cli.install_commands.run_services') @mock.patch('tethys_cli.install_commands.call') @mock.patch('tethys_cli.install_commands.conda_run', return_value=['', '', 1]) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_conda_and_pip_package_install(self, mock_pretty_output, mock_exit, mock_conda_run, mock_call, _): file_path = os.path.join(self.root_app_path, 'install-dep.yml') args = mock.MagicMock(file=file_path, develop=False, verbose=False, services_file=None) mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.install_command, args) mock_conda_run.assert_called_with(Commands.INSTALL, '-c', 'tacaswell', 'geojson', use_exception_handler=False, stdout=None, stderr=None) po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertEqual("Running conda installation tasks...", po_call_args[0][0][0]) self.assertIn("Warning: Packages installation ran into an error.", po_call_args[1][0][0]) self.assertEqual("Running pip installation tasks...", po_call_args[2][0][0]) self.assertEqual("Running application install....", po_call_args[3][0][0]) self.assertEqual( "Quiet mode: No additional service setting validation will be performed.", po_call_args[4][0][0]) self.assertEqual("Services Configuration Completed.", po_call_args[5][0][0]) self.assertEqual(['pip', 'install', 'see'], mock_call.mock_calls[0][1][0]) self.assertEqual(['python', 'setup.py', 'clean', '--all'], mock_call.mock_calls[1][1][0]) self.assertEqual(['python', 'setup.py', 'install'], mock_call.mock_calls[2][1][0]) self.assertEqual(['tethys', 'db', 'sync'], mock_call.mock_calls[3][1][0]) mock_exit.assert_called_with(0) @mock.patch('builtins.input', side_effect=['x', 5]) @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_custom_setting_set(self, mock_pretty_output, mock_get_settings, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_cs.save.side_effect = [ValidationError('error'), mock.DEFAULT] mock_get_settings.return_value = {'unlinked_settings': [mock_cs]} install_commands.run_interactive_services('foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_cs", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("Enter the desired value", po_call_args[4][0][0]) self.assertIn("Incorrect value type", po_call_args[5][0][0]) self.assertIn("Enter the desired value", po_call_args[6][0][0]) self.assertEqual(mock_cs.name + " successfully set with value: 5.", po_call_args[7][0][0]) @mock.patch('builtins.input', side_effect=['']) @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_custom_setting_skip(self, mock_pretty_output, mock_get_settings, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_get_settings.return_value = {'unlinked_settings': [mock_cs]} install_commands.run_interactive_services('foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_cs", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("Enter the desired value", po_call_args[4][0][0]) self.assertEqual(f"Skipping setup of {mock_cs.name}", po_call_args[5][0][0]) @mock.patch('builtins.input', side_effect=KeyboardInterrupt) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_custom_setting_interrupt(self, mock_pretty_output, mock_get_settings, mock_exit, _): mock_cs = mock.MagicMock() mock_cs.name = 'mock_cs' mock_get_settings.return_value = {'unlinked_settings': [mock_cs]} mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.run_interactive_services, 'foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_cs", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("Enter the desired value", po_call_args[4][0][0]) self.assertEqual("\nInstall Command cancelled.", po_call_args[5][0][0]) mock_exit.assert_called_with(0) @mock.patch('builtins.input', side_effect=['1', '1', '', KeyboardInterrupt]) @mock.patch('tethys_cli.install_commands.get_setting_type', return_value='persistent') @mock.patch('tethys_cli.install_commands.get_service_from_id', side_effect=ValueError) @mock.patch('tethys_cli.install_commands.get_service_from_name', side_effect=[False, { 'service_type': 'st', 'linkParam': 'lp' }]) @mock.patch('tethys_cli.install_commands.exit') @mock.patch('tethys_cli.install_commands.services_list_command') @mock.patch('tethys_cli.install_commands.get_app_settings') @mock.patch('tethys_cli.install_commands.link_service_to_app_setting') @mock.patch('tethys_cli.cli_colors.pretty_output') def test_interactive_service_setting_all(self, mock_pretty_output, mock_lstas, mock_get_settings, mock_slc, mock_exit, ____, ___, __, _): mock_ss = mock.MagicMock() del mock_ss.value mock_ss.name = 'mock_ss' mock_ss.save.side_effect = [ValidationError('error'), mock.DEFAULT] mock_get_settings.return_value = { 'unlinked_settings': [mock_ss, mock_ss, mock_ss, mock_ss] } mock_s = mock.MagicMock() mock_slc.side_effect = [[[]], [[mock_s]], [[mock_s]], [[mock_s]]] mock_exit.side_effect = SystemExit self.assertRaises(SystemExit, install_commands.run_interactive_services, 'foo') po_call_args = mock_pretty_output().__enter__().write.call_args_list self.assertIn("Configuring mock_ss", po_call_args[2][0][0]) self.assertIn("Type", po_call_args[3][0][0]) self.assertIn("No compatible services found.", po_call_args[4][0][0]) self.assertIn("Enter the service ID/Name", po_call_args[7][0][0]) self.assertIn("Incorrect service ID/Name.", po_call_args[8][0][0]) self.assertIn("Enter the service ID/Name", po_call_args[9][0][0]) self.assertIn(f"Skipping setup of {mock_ss.name}", po_call_args[13][0][0]) self.assertEqual("\nInstall Command cancelled.", po_call_args[17][0][0]) mock_lstas.assert_called_with('st', '1', 'foo', 'lp', 'mock_ss') mock_exit.assert_called_with(0) def test_get_setting_type(self): from tethys_apps.models import PersistentStoreDatabaseSetting self.assertEqual( 'persistent', install_commands.get_setting_type( PersistentStoreDatabaseSetting()))
class ResourceQuotaHandlerTest(TestCase): def setUp(self): user = User.objects.create_user(username="******", email="*****@*****.**", password="******") self.resource_quota_handler = WorkspaceQuotaHandler(user) self.resource_quota_user = ResourceQuota( codename='test_user_codename', name='test_name', description='test_description', default=1.21, units='gb', applies_to='django.contrib.auth.models.User', impose_default=False, help='Exceeded quota', _handler='tethys_quotas.handlers.workspace.WorkspaceQuotaHandler') self.resource_quota_user.save() self.entity_quota_user = UserQuota( resource_quota=self.resource_quota_user, entity=user, value=100, ) self.entity_quota_user.save() self.resource_quota_app = ResourceQuota( codename='test_app_codename', name='test_name', description='test_description', default=1.21, units='gb', applies_to='tethys_apps.models.TethysApp', impose_default=True, help='Exceeded quota', _handler='tethys_quotas.handlers.workspace.WorkspaceQuotaHandler') self.resource_quota_app.save() self.app_model = TethysApp(name='Test App') self.app_model.save() self.entity_quota_app = TethysAppQuota( resource_quota=self.resource_quota_app, entity=self.app_model, value=200, ) self.entity_quota_app.save() def tearDown(self): self.resource_quota_user.delete() self.entity_quota_user.delete() self.resource_quota_app.delete() self.entity_quota_app.delete() self.app_model.delete() @mock.patch('tethys_quotas.utilities.log') def test_rqh_check_rq_dne(self, _): bad_rqh = WorkspaceQuotaHandler("not.a.user") self.assertTrue(bad_rqh.check()) @mock.patch('tethys_quotas.utilities.log') def test_rqh_check_eq_dne(self, _): with transaction.atomic(): user = User.objects.create_user(username="******", email="*****@*****.**", password="******") resource_quota_handler = WorkspaceQuotaHandler(user) self.assertEquals("workspace_quota", resource_quota_handler.codename) self.assertTrue(resource_quota_handler.check()) @mock.patch('tethys_quotas.utilities.log') def test_rqh_check_eq_passes(self, _): self.assertTrue(self.resource_quota_handler.check()) @mock.patch('tethys_quotas.utilities.log') def test_rqh_check_eq_app_passes(self, _): resource_quota_handler = WorkspaceQuotaHandler(self.app_model) self.assertTrue(resource_quota_handler.check())
class TestTethysAppAdmin(unittest.TestCase): def setUp(self): from tethys_apps.models import TethysApp self.src_dir = os.path.dirname( os.path.dirname(os.path.dirname(__file__))) self.root_app_path = os.path.join(self.src_dir, 'apps', 'tethysapp-test_app') self.app_model = TethysApp(name='test_app', package='test_app') self.app_model.save() from django.contrib.auth.models import ContentType, Group, Permission app_content_type_id = ContentType.objects.get(app_label='tethys_apps', model='tethysapp').pk self.perm_model = Permission(name='Test Perm | Test', content_type_id=app_content_type_id, codename='test_perm:test') self.perm_model.save() self.group_model = Group(name='test_group') self.group_model.save() def tearDown(self): self.app_model.delete() self.perm_model.delete() self.group_model.delete() def test_TethysAppSettingInline(self): expected_template = 'tethys_portal/admin/edit_inline/tabular.html' TethysAppSettingInline.model = mock.MagicMock() ret = TethysAppSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_template, ret.template) def test_has_delete_permission(self): TethysAppSettingInline.model = mock.MagicMock() ret = TethysAppSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertFalse(ret.has_delete_permission(mock.MagicMock())) def test_has_add_permission(self): TethysAppSettingInline.model = mock.MagicMock() ret = TethysAppSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertFalse(ret.has_add_permission(mock.MagicMock())) def test_CustomSettingInline(self): expected_readonly_fields = ('name', 'description', 'type', 'required') expected_fields = ('name', 'description', 'type', 'value', 'required') expected_model = CustomSetting ret = CustomSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_model, ret.model) def test_DatasetServiceSettingInline(self): expected_readonly_fields = ('name', 'description', 'required', 'engine') expected_fields = ('name', 'description', 'dataset_service', 'engine', 'required') expected_model = DatasetServiceSetting ret = DatasetServiceSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_model, ret.model) def test_SpatialDatasetServiceSettingInline(self): expected_readonly_fields = ('name', 'description', 'required', 'engine') expected_fields = ('name', 'description', 'spatial_dataset_service', 'engine', 'required') expected_model = SpatialDatasetServiceSetting ret = SpatialDatasetServiceSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_model, ret.model) def test_WebProcessingServiceSettingInline(self): expected_readonly_fields = ('name', 'description', 'required') expected_fields = ('name', 'description', 'web_processing_service', 'required') expected_model = WebProcessingServiceSetting ret = WebProcessingServiceSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_model, ret.model) def test_PersistentStoreConnectionSettingInline(self): expected_readonly_fields = ('name', 'description', 'required') expected_fields = ('name', 'description', 'persistent_store_service', 'required') expected_model = PersistentStoreConnectionSetting ret = PersistentStoreConnectionSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_model, ret.model) def test_PersistentStoreDatabaseSettingInline(self): expected_readonly_fields = ('name', 'description', 'required', 'spatial', 'initialized') expected_fields = ('name', 'description', 'spatial', 'initialized', 'persistent_store_service', 'required') expected_model = PersistentStoreDatabaseSetting ret = PersistentStoreDatabaseSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_model, ret.model) # Need to check def test_PersistentStoreDatabaseSettingInline_get_queryset(self): obj = PersistentStoreDatabaseSettingInline(mock.MagicMock(), mock.MagicMock()) mock_request = mock.MagicMock() obj.get_queryset(mock_request) def test_TethysAppQuotasSettingInline(self): expected_readonly_fields = ('name', 'description', 'default', 'units') expected_fields = ('name', 'description', 'value', 'default', 'units') expected_model = TethysAppQuota ret = TethysAppQuotasSettingInline(mock.MagicMock(), mock.MagicMock()) self.assertEquals(expected_readonly_fields, ret.readonly_fields) self.assertEquals(expected_fields, ret.fields) self.assertEquals(expected_model, ret.model) # Need to check # def test_TethysAppQuotasSettingInline_get_queryset(self): # obj = TethysAppQuotasSettingInline(mock.MagicMock(), mock.MagicMock()) # mock_request = mock.MagicMock() # obj.get_queryset(mock_request) def test_TethysAppAdmin(self): expected_readonly_fields = ( 'package', 'manage_app_storage', ) expected_fields = ( 'package', 'name', 'description', 'icon', 'tags', 'enabled', 'show_in_apps_library', 'enable_feedback', 'manage_app_storage', ) expected_inlines = [ CustomSettingInline, PersistentStoreConnectionSettingInline, PersistentStoreDatabaseSettingInline, DatasetServiceSettingInline, SpatialDatasetServiceSettingInline, WebProcessingServiceSettingInline, TethysAppQuotasSettingInline ] ret = TethysAppAdmin(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) self.assertEqual(expected_inlines, ret.inlines) def test_TethysAppAdmin_has_delete_permission(self): ret = TethysAppAdmin(mock.MagicMock(), mock.MagicMock()) self.assertFalse(ret.has_delete_permission(mock.MagicMock())) def test_TethysAppAdmin_has_add_permission(self): ret = TethysAppAdmin(mock.MagicMock(), mock.MagicMock()) self.assertFalse(ret.has_add_permission(mock.MagicMock())) @mock.patch('tethys_apps.admin.get_quota') @mock.patch('tethys_apps.admin._convert_storage_units') def test_TethysAppAdmin_manage_app_storage(self, mock_convert, mock_get_quota): ret = TethysAppAdmin(mock.MagicMock(), mock.MagicMock()) app = mock.MagicMock() app.id = 1 mock_convert.return_value = '0 bytes' mock_get_quota.return_value = {'quota': None} url = reverse('admin:clear_workspace', kwargs={'app_id': app.id}) expected_html = format_html(""" <span>{} of {}</span> <a id="clear-workspace" class="btn btn-danger btn-sm" href="{url}"> Clear Workspace</a> """.format('0 bytes', "∞", url=url)) actual_html = ret.manage_app_storage(app) self.assertEquals(expected_html.replace(" ", ""), actual_html.replace(" ", "")) mock_convert.return_value = '0 bytes' mock_get_quota.return_value = {'quota': 5, 'units': 'gb'} url = reverse('admin:clear_workspace', kwargs={'app_id': app.id}) expected_html = format_html(""" <span>{} of {}</span> <a id="clear-workspace" class="btn btn-danger btn-sm" href="{url}"> Clear Workspace</a> """.format('0 bytes', "0 bytes", url=url)) actual_html = ret.manage_app_storage(app) self.assertEquals(expected_html.replace(" ", ""), actual_html.replace(" ", "")) def test_TethysExtensionAdmin(self): expected_readonly_fields = ('package', 'name', 'description') expected_fields = ('package', 'name', 'description', 'enabled') ret = TethysExtensionAdmin(mock.MagicMock(), mock.MagicMock()) self.assertEqual(expected_readonly_fields, ret.readonly_fields) self.assertEqual(expected_fields, ret.fields) def test_TethysExtensionAdmin_has_delete_permission(self): ret = TethysExtensionAdmin(mock.MagicMock(), mock.MagicMock()) self.assertFalse(ret.has_delete_permission(mock.MagicMock())) def test_TethysExtensionAdmin_has_add_permission(self): ret = TethysExtensionAdmin(mock.MagicMock(), mock.MagicMock()) self.assertFalse(ret.has_add_permission(mock.MagicMock())) @mock.patch('django.contrib.auth.admin.UserAdmin.change_view') @mock.patch('django.contrib.auth.admin.UserAdmin.add_view') def test_admin_site_register_custom_user(self, mock_ua_add_view, mock_ua_change_view): from django.contrib import admin ret = CustomUser(mock.MagicMock(), mock.MagicMock()) # Add custom inline when change_view is called ret.change_view(mock.MagicMock()) mock_ua_change_view.assert_called() self.assertIn(UserQuotasSettingInline, ret.inlines) # Remove custom inline when change_view is called ret.add_view(mock.MagicMock()) mock_ua_add_view.assert_called() self.assertNotIn(UserQuotasSettingInline, ret.inlines) # Repeat to complete full cycle (change -> add -> change -> add) # Add custom inline when change_view is called ret.change_view(mock.MagicMock()) mock_ua_change_view.assert_called() self.assertIn(UserQuotasSettingInline, ret.inlines) # Remove custom inline when change_view is called ret.add_view(mock.MagicMock()) mock_ua_add_view.assert_called() self.assertNotIn(UserQuotasSettingInline, ret.inlines) # Check registration registry = admin.site._registry self.assertIn(User, registry) self.assertIsInstance(registry[User], CustomUser) def test_admin_site_register_tethys_app_admin(self): from django.contrib import admin registry = admin.site._registry self.assertIn(TethysApp, registry) self.assertIsInstance(registry[TethysApp], TethysAppAdmin) def test_admin_site_register_tethys_app_extension(self): from django.contrib import admin registry = admin.site._registry self.assertIn(TethysExtension, registry) self.assertIsInstance(registry[TethysExtension], TethysExtensionAdmin) def test_admin_site_register_proxy_app(self): from django.contrib import admin registry = admin.site._registry self.assertIn(ProxyApp, registry) @mock.patch('tethys_apps.admin.GroupObjectPermission.objects') @mock.patch('tethys_apps.admin.TethysApp.objects.all') def test_make_gop_app_access_form(self, mock_all_apps, mock_gop): mock_all_apps.return_value = [self.app_model] mock_gop.filter().values().distinct.return_value = [{'group_id': 9999}] ret = make_gop_app_access_form() self.assertIn('test_app_permissions', ret.base_fields) self.assertIn('test_app_groups', ret.base_fields) @mock.patch('tethys_apps.admin.Group.objects') @mock.patch('tethys_apps.admin.Permission.objects') @mock.patch('tethys_apps.admin.GroupObjectPermission.objects') @mock.patch('tethys_apps.admin.TethysApp.objects.all') def test_gop_form_init(self, mock_all_apps, mock_gop, mock_perms, mock_groups): mock_all_apps.return_value = [self.app_model] mock_obj = mock.MagicMock(pk=True) mock_gop.values().distinct().filter.return_value = [{ 'object_pk': self.app_model.pk }] mock_gop.values_list().filter().distinct.side_effect = [ [9999], [9999], mock.MagicMock(exclude=mock.MagicMock( return_value=[self.app_model.pk, 9999])), [self.app_model.pk] ] mock_perms.filter().exclude.side_effect = [ mock_perms.none(), '_permissions_test' ] mock_groups.filter().exclude.return_value = '_groups_test' gop_app_access_form_dynamic = make_gop_app_access_form() ret = gop_app_access_form_dynamic(instance=mock_obj) self.assertIn(self.app_model, ret.fields['apps'].initial) self.assertEqual(ret.fields['test_app_permissions'].initial, '_permissions_test') self.assertEqual(ret.fields['test_app_groups'].initial, '_groups_test') @mock.patch('tethys_apps.admin.TethysApp.objects.all') def test_gop_form_clean(self, mock_all_apps): mock_all_apps.return_value = [self.app_model] mock_obj = mock.MagicMock(pk=True) mock_data = mock.MagicMock(getlist=mock.MagicMock(return_value=[9999])) gop_app_access_form_dynamic = make_gop_app_access_form() ret = gop_app_access_form_dynamic(instance=mock_obj) ret.data = mock_data ret.cleaned_data = {} ret.clean() self.assertIn('test_app_permissions', ret.cleaned_data) self.assertIn('test_app_groups', ret.cleaned_data) @mock.patch('tethys_apps.admin.remove_perm') @mock.patch('tethys_apps.admin.assign_perm') @mock.patch('tethys_apps.admin.TethysApp.objects.all') def test_gop_form_save_new(self, mock_all_apps, _, __): mock_all_apps.return_value = [self.app_model] mock_obj = mock.MagicMock(pk=False) mock_data = mock.MagicMock(getlist=mock.MagicMock(return_value=[9999])) gop_app_access_form_dynamic = make_gop_app_access_form() ret = gop_app_access_form_dynamic(instance=mock_obj) ret.data = mock_data ret.cleaned_data = {'apps': [self.app_model]} ret.fields = {'apps': ret.fields['apps']} ret.save() self.assertEqual(mock_obj.save.call_count, 1) @mock.patch('tethys_apps.admin.assign_perm') @mock.patch('tethys_apps.admin.remove_perm') @mock.patch('tethys_apps.admin.TethysApp.objects') def test_gop_form_save_edit_apps(self, mock_apps, mock_remove_perm, mock_assign_perm): mock_apps.all.return_value = [self.app_model] mock_diff = mock.MagicMock(return_value=[self.app_model]) mock_apps.filter.return_value = mock.MagicMock(difference=mock_diff, return_value=True) mock_obj = mock.MagicMock(pk=True) mock_data = mock.MagicMock(getlist=mock.MagicMock(return_value=[9999])) gop_app_access_form_dynamic = make_gop_app_access_form() ret = gop_app_access_form_dynamic(instance=mock_obj) ret.data = mock_data ret.cleaned_data = {'apps': [self.app_model]} ret.fields = {'apps': ret.fields['apps']} ret.save() mock_remove_perm.assert_called_with('test_app:access_app', mock_obj, self.app_model) mock_assign_perm.assert_called_with('test_app:access_app', mock_obj, self.app_model) @mock.patch('tethys_apps.admin.assign_perm') @mock.patch('tethys_apps.admin.remove_perm') @mock.patch('tethys_apps.admin.TethysApp.objects') def test_gop_form_save_edit_permissions(self, mock_apps, mock_remove_perm, mock_assign_perm): mock_apps.all.return_value = [self.app_model] mock_diff = mock.MagicMock( side_effect=[[self.app_model], [self.perm_model]]) mock_apps.filter.return_value = mock.MagicMock(difference=mock_diff, return_value=True) mock_obj = mock.MagicMock(pk=True) mock_data = mock.MagicMock(getlist=mock.MagicMock(return_value=[9999])) gop_app_access_form_dynamic = make_gop_app_access_form() ret = gop_app_access_form_dynamic(instance=mock_obj) ret.data = mock_data ret.cleaned_data = { 'apps': [self.app_model], 'test_app_permissions': [self.perm_model] } ret.fields = { 'apps': ret.fields['apps'], 'test_app_permissions': ret.fields['apps'] } ret.save() mock_remove_perm.assert_called_with('test_perm:test', mock_obj, mock_apps.filter()) mock_assign_perm.assert_called_with('test_perm:test', mock_obj, mock_apps.filter()) @mock.patch('tethys_apps.admin.assign_perm') @mock.patch('tethys_apps.admin.remove_perm') @mock.patch('tethys_apps.admin.GroupObjectPermission.objects') @mock.patch('tethys_apps.admin.TethysApp.objects') def test_gop_form_save_edit_groups(self, mock_apps, mock_gop, mock_remove_perm, mock_assign_perm): mock_apps.all.return_value = [self.app_model] mock_diff = mock.MagicMock( side_effect=[[self.app_model], mock.MagicMock(values_list=mock.MagicMock( distinct=[self.group_model.pk]))]) mock_apps.filter.return_value = mock.MagicMock(difference=mock_diff, return_value=True) mock_obj = mock.MagicMock(pk=True) mock_data = mock.MagicMock(getlist=mock.MagicMock(return_value=[9999])) mock_gop.filter().values_list().distinct.return_value = [ self.perm_model.pk ] gop_app_access_form_dynamic = make_gop_app_access_form() ret = gop_app_access_form_dynamic(instance=mock_obj) ret.data = mock_data ret.cleaned_data = { 'apps': [self.app_model], 'test_app_groups': mock.MagicMock() } ret.cleaned_data['test_app_groups'].values_list( ).distinct.return_value = [self.group_model.pk] ret.fields = { 'apps': ret.fields['apps'], 'test_app_groups': ret.fields['apps'] } ret.save() mock_remove_perm.assert_called_with('test_perm:test', mock_obj, mock_apps.filter()) mock_assign_perm.assert_called_with('test_perm:test', mock_obj, mock_apps.filter()) @mock.patch('tethys_apps.admin.tethys_log.warning') @mock.patch('tethys_apps.admin.make_gop_app_access_form') def test_admin_programming_error(self, mock_gop_form, mock_logwarning): mock_gop_form.side_effect = ProgrammingError register_custom_group() mock_gop_form.assert_called() mock_logwarning.assert_called_with('Unable to register CustomGroup.') @mock.patch('tethys_apps.admin.tethys_log.warning') @mock.patch('tethys_apps.admin.admin.site.register') def test_admin_user_keys_programming_error(self, mock_register, mock_logwarning): mock_register.side_effect = ProgrammingError register_user_keys_admin() mock_register.assert_called() mock_logwarning.assert_called_with('Unable to register UserKeys.')