diff --git a/.coveragerc b/.coveragerc index e6d50447c3..dcbfac2c1e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -6,4 +6,4 @@ omit = splunk_add_on_ucc_framework/templates/input.module-template [report] -fail_under = 81.5 +fail_under = 84.60 diff --git a/docs/generated_files.md b/docs/generated_files.md index aab74c463b..09a27cc892 100644 --- a/docs/generated_files.md +++ b/docs/generated_files.md @@ -22,4 +22,5 @@ The following table describes the files generated by UCC framework. | inputs.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates inputs.xml based on inputs configuration present in globalConfig, in `default/data/ui/views/inputs.xml` folder | | _redirect.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates ta_name_redirect.xml file, if oauth is mentioned in globalConfig, in `default/data/ui/views/` folder. | | _.html | output/<YOUR_ADDON_NAME>/default/data/ui/alerts | Generates `alert_name.html` file based on alerts configuration present in globalConfig, in `default/data/ui/alerts` folder. | +| globalConfig.json | <source_dir> | Generates globalConfig.json file in the source code if globalConfig is not present in source directory at build time. | diff --git a/splunk_add_on_ucc_framework/commands/build.py b/splunk_add_on_ucc_framework/commands/build.py index e375b123a9..177d5caf67 100644 --- a/splunk_add_on_ucc_framework/commands/build.py +++ b/splunk_add_on_ucc_framework/commands/build.py @@ -52,6 +52,7 @@ ) from splunk_add_on_ucc_framework.generators.file_generator import begin from splunk_add_on_ucc_framework.generators.conf_files.create_app_conf import AppConf +import addonfactory_splunk_conf_parser_lib as conf_parser logger = logging.getLogger("ucc_gen") @@ -442,137 +443,138 @@ def generate( generated_files = [] gc_path = _get_and_check_global_config_path(source, config_path) - if gc_path: - logger.info(f"Using globalConfig file located @ {gc_path}") - global_config = global_config_lib.GlobalConfig(gc_path) - global_config.cleanup_unwanted_params() - # handle the update of globalConfig before validating - global_config_update.handle_global_config_update(global_config) - try: - validator = global_config_validator.GlobalConfigValidator( - internal_root_dir, global_config - ) - validator.validate() - logger.info("globalConfig file is valid") - except exceptions.GlobalConfigValidatorException as e: - logger.error(f"globalConfig file is not valid. Error: {e}") - sys.exit(1) - global_config.update_addon_version(addon_version) - global_config.dump(global_config.original_path) - logger.info( - f"Updated and saved add-on version in the globalConfig file to {addon_version}" - ) - global_config.add_ucc_version(__version__) - global_config.expand() - if ta_name != global_config.product: - logger.error( - "Add-on name mentioned in globalConfig meta tag and that app.manifest are not same," - "please unify them to build the add-on." - ) - sys.exit(1) - global_config.parse_user_defined_handlers() - scheme = global_config_builder_schema.GlobalConfigBuilderSchema(global_config) - if global_config.has_pages(): - utils.recursive_overwrite( - os.path.join(internal_root_dir, "package"), - os.path.join(output_directory, ta_name), - ui_source_map, - ) - global_config_file = ( - "globalConfig.yaml" if gc_path.endswith(".yaml") else "globalConfig.json" - ) - output_build_path = os.path.join( - output_directory, ta_name, "appserver", "static", "js", "build" + + if not gc_path: + app_conf_path = os.path.join(source, "default", "app.conf") + app_conf_content = {} + if os.path.isfile(app_conf_path): + # read only if app.conf exists in source code + app_conf = conf_parser.TABConfigParser() + app_conf.read(app_conf_path) + app_conf_content = app_conf.item_dict() + + global_config = global_config_lib.GlobalConfig( + gc_path, + source=source, + app_manifest=app_manifest, + app_conf_content=app_conf_content, ) - if not os.path.isdir(output_build_path): - # this path may not exist for the .conf-only add-ons - os.makedirs(output_build_path) - global_config.dump(os.path.join(output_build_path, global_config_file)) - logger.info("Copied globalConfig to output") - ucc_lib_target = os.path.join(output_directory, ta_name, "lib") - try: - install_python_libraries( - source, - ucc_lib_target, - python_binary_name, - includes_ui=True, - os_libraries=global_config.os_libraries, - pip_version=pip_version, - pip_legacy_resolver=pip_legacy_resolver, - pip_custom_flag=pip_custom_flag, - includes_oauth=global_config.has_oauth(), - ) - except SplunktaucclibNotFound as e: - logger.error(str(e)) - sys.exit(1) - logger.info( - f"Installed add-on requirements into {ucc_lib_target} from {source}" + gc_path = os.path.join(source, os.pardir, "globalConfig.json") + global_config.dump(gc_path) + logger.info(f"Created minimal globalConfig file located @ {gc_path}") + + logger.info(f"Using globalConfig file located @ {gc_path}") + global_config = global_config_lib.GlobalConfig(gc_path) + global_config.cleanup_unwanted_params() + # handle the update of globalConfig before validating + global_config_update.handle_global_config_update(global_config) + try: + validator = global_config_validator.GlobalConfigValidator( + internal_root_dir, global_config ) - generated_files.extend( - begin( - global_config=global_config, - input_dir=source, - output_dir=output_directory, - ucc_dir=internal_root_dir, - addon_name=ta_name, - app_manifest=app_manifest, - addon_version=addon_version, - has_ui=global_config.meta.get("isVisible", True), - ) + validator.validate() + logger.info("globalConfig file is valid") + except exceptions.GlobalConfigValidatorException as e: + logger.error(f"globalConfig file is not valid. Error: {e}") + sys.exit(1) + global_config.update_addon_version(addon_version) + global_config.dump(global_config.original_path) + logger.info( + f"Updated and saved add-on version in the globalConfig file to {addon_version}" + ) + global_config.add_ucc_version(__version__) + global_config.expand() + if ta_name != global_config.product: + logger.error( + "Add-on name mentioned in globalConfig meta tag and that app.manifest are not same," + "please unify them to build the add-on." ) - # TODO: all FILES GENERATED object: generated_files, use it for comparison - if global_config.has_pages(): - builder_obj = RestBuilder(scheme, os.path.join(output_directory, ta_name)) - builder_obj.build() - _modify_and_replace_token_for_oauth_templates( - ta_name, - global_config, - output_directory, - ) - if global_config.has_inputs(): - logger.info("Generating inputs code") - _add_modular_input(ta_name, global_config, output_directory) - if global_config.has_alerts(): - logger.info("Generating alerts code") - alert_builder.generate_alerts(global_config, ta_name, output_directory) - - conf_file_names = [] - conf_file_names.extend(list(scheme.settings_conf_file_names)) - conf_file_names.extend(list(scheme.configs_conf_file_names)) - conf_file_names.extend(list(scheme.oauth_conf_file_names)) - - if global_config.has_dashboard(): - logger.info("Including dashboard") - dashboard_definition_json_path = os.path.join( - output_directory, - ta_name, - "appserver", - "static", - "js", - "build", - "custom", - ) - dashboard.generate_dashboard( - global_config, ta_name, dashboard_definition_json_path - ) - - else: - global_config = None - conf_file_names = [] - logger.warning( - "Skipped generating UI components as globalConfig file does not exist" + sys.exit(1) + global_config.parse_user_defined_handlers() + scheme = global_config_builder_schema.GlobalConfigBuilderSchema(global_config) + if global_config.has_pages(): + utils.recursive_overwrite( + os.path.join(internal_root_dir, "package"), + os.path.join(output_directory, ta_name), + ui_source_map, ) - ucc_lib_target = os.path.join(output_directory, ta_name, "lib") + global_config_file = ( + "globalConfig.yaml" if gc_path.endswith(".yaml") else "globalConfig.json" + ) + output_build_path = os.path.join( + output_directory, ta_name, "appserver", "static", "js", "build" + ) + if not os.path.isdir(output_build_path): + # this path may not exist for the .conf-only add-ons + os.makedirs(output_build_path) + global_config.dump(os.path.join(output_build_path, global_config_file)) + logger.info("Copied globalConfig to output") + ucc_lib_target = os.path.join(output_directory, ta_name, "lib") + ui_available = False + if global_config and global_config.has_pages(): + ui_available = global_config.meta.get("isVisible", True) + try: install_python_libraries( source, ucc_lib_target, python_binary_name, + includes_ui=ui_available, + os_libraries=global_config.os_libraries, pip_version=pip_version, pip_legacy_resolver=pip_legacy_resolver, pip_custom_flag=pip_custom_flag, + includes_oauth=global_config.has_oauth(), ) - logger.info( - f"Installed add-on requirements into {ucc_lib_target} from {source}" + except SplunktaucclibNotFound as e: + logger.error(str(e)) + sys.exit(1) + logger.info(f"Installed add-on requirements into {ucc_lib_target} from {source}") + generated_files.extend( + begin( + global_config=global_config, + input_dir=source, + output_dir=output_directory, + ucc_dir=internal_root_dir, + addon_name=ta_name, + app_manifest=app_manifest, + addon_version=addon_version, + has_ui=global_config.meta.get("isVisible", True), + ) + ) + # TODO: all FILES GENERATED object: generated_files, use it for comparison + if global_config.has_pages(): + builder_obj = RestBuilder(scheme, os.path.join(output_directory, ta_name)) + builder_obj.build() + _modify_and_replace_token_for_oauth_templates( + ta_name, + global_config, + output_directory, + ) + if global_config.has_inputs(): + logger.info("Generating inputs code") + _add_modular_input(ta_name, global_config, output_directory) + if global_config.has_alerts(): + logger.info("Generating alerts code") + alert_builder.generate_alerts(global_config, ta_name, output_directory) + + conf_file_names = [] + conf_file_names.extend(list(scheme.settings_conf_file_names)) + conf_file_names.extend(list(scheme.configs_conf_file_names)) + conf_file_names.extend(list(scheme.oauth_conf_file_names)) + + if global_config.has_dashboard(): + logger.info("Including dashboard") + dashboard_definition_json_path = os.path.join( + output_directory, + ta_name, + "appserver", + "static", + "js", + "build", + "custom", + ) + dashboard.generate_dashboard( + global_config, ta_name, dashboard_definition_json_path ) ignore_list = _get_ignore_list( @@ -613,10 +615,6 @@ def generate( logger.info( f"Updated {app_manifest_lib.APP_MANIFEST_FILE_NAME} file in the output folder" ) - - ui_available = False - if global_config and global_config.has_pages(): - ui_available = global_config.meta.get("isVisible", True) # NOTE: merging source and generated 'app.conf' as per previous design AppConf( global_config=global_config, diff --git a/splunk_add_on_ucc_framework/generators/doc_generator.py b/splunk_add_on_ucc_framework/generators/doc_generator.py index d51c8db03f..0cbc25b182 100644 --- a/splunk_add_on_ucc_framework/generators/doc_generator.py +++ b/splunk_add_on_ucc_framework/generators/doc_generator.py @@ -42,6 +42,12 @@ def generate_docs() -> None: f"| {f_tup.file_name} | output/<YOUR_ADDON_NAME>/{'/'.join(f_tup.file_path)} " f"| {f_tup.file_description} |" ) + # statically appending this line as we are generating it now + doc_content.append( + "| globalConfig.json | <source_dir> | " + "Generates globalConfig.json file in the source code if globalConfig " + "is not present in source directory at build time. |" + ) doc_content.append("\n") with open( join( diff --git a/splunk_add_on_ucc_framework/global_config.py b/splunk_add_on_ucc_framework/global_config.py index 8f5ad8fd83..cabb47c464 100644 --- a/splunk_add_on_ucc_framework/global_config.py +++ b/splunk_add_on_ucc_framework/global_config.py @@ -19,6 +19,8 @@ from dataclasses import dataclass, field, fields import yaml +import os +from splunk_add_on_ucc_framework import app_manifest as app_manifest_lib from splunk_add_on_ucc_framework import utils from splunk_add_on_ucc_framework.commands.rest_builder.user_defined_rest_handlers import ( @@ -60,20 +62,65 @@ def from_dict(cls, **kwargs: Any) -> "OSDependentLibraryConfig": class GlobalConfig: - def __init__(self, global_config_path: str) -> None: - with open(global_config_path) as f_config: - config_raw = f_config.read() - self._is_global_config_yaml = ( - True if global_config_path.endswith(".yaml") else False - ) - self._content = ( - yaml_load(config_raw) - if self._is_global_config_yaml - else json.loads(config_raw) - ) + def __init__(self, global_config_path: str, **kwargs: Any) -> None: + if global_config_path == "": + global_config_path = self.from_app_conf_and_app_manifest( + kwargs["source"], kwargs["app_manifest"], kwargs["app_conf_content"] + ) + else: + with open(global_config_path) as f_config: + config_raw = f_config.read() + self._is_global_config_yaml = ( + True if global_config_path.endswith(".yaml") else False + ) + self._content = ( + yaml_load(config_raw) + if self._is_global_config_yaml + else json.loads(config_raw) + ) self._original_path = global_config_path self.user_defined_handlers = UserDefinedRestHandlers() + def from_app_conf_and_app_manifest( + self, + source_dir: str, + app_manifest: app_manifest_lib.AppManifest, + app_conf_content: Dict[str, Any], + ) -> str: + check_for_update = app_conf_content.get("package", {}).get( + "check_for_updates", "true" + ) + # checkForUpdates is by default set to 'true' + check_for_update = check_for_update.lower() in ("true", "1", "t", "y", "yes") + supported_themes = app_conf_content.get("ui", {}).get("supported_themes", "") + if supported_themes: + supported_themes = [item.strip() for item in supported_themes.split(",")] + minimal_gc_path = os.path.join(source_dir, os.pardir, "globalConfig.json") + self._is_global_config_yaml = False + + def create_globalConfig( + app_manifest: app_manifest_lib.AppManifest, + check_for_update: bool, + supported_themes: List[str], + ) -> Dict[str, Any]: + minimal_gc = { + "meta": { + "name": f"{app_manifest.get_addon_name()}", + "restRoot": f"{app_manifest.get_addon_name()}", + "displayName": f"{app_manifest.get_title()}", + "version": f"{app_manifest.get_addon_version()}", + "checkForUpdates": check_for_update, + } + } + if supported_themes: + minimal_gc["meta"]["supportedThemes"] = supported_themes + return minimal_gc + + self._content = create_globalConfig( + app_manifest, check_for_update, supported_themes + ) + return minimal_gc_path + def parse_user_defined_handlers(self) -> None: """Parse user-defined REST handlers from globalConfig["options"]["restHandlers"]""" rest_handlers = self._content.get("options", {}).get("restHandlers", []) diff --git a/splunk_add_on_ucc_framework/templates/minimal_globalConfig.json.template b/splunk_add_on_ucc_framework/templates/minimal_globalConfig.json.template new file mode 100644 index 0000000000..c6c28ca7d6 --- /dev/null +++ b/splunk_add_on_ucc_framework/templates/minimal_globalConfig.json.template @@ -0,0 +1,16 @@ +{ + "meta": { + "name": "{{addon_name}}", + "restRoot": "{{addon_name}}", + "displayName": "{{addon_display_name}}", + "version": "{{addon_version}}", + {%- if supported_themes %} + "supportedThemes": {{supported_themes}}, + {%- endif %} + {%- if check_for_update -%} + "checkForUpdates": {{check_for_update}} + {%- else %} + "checkForUpdates": true + {%- endif %} + } +} \ No newline at end of file diff --git a/tests/smoke/test_ucc_build.py b/tests/smoke/test_ucc_build.py index 14f2b3c203..a8c7aef174 100644 --- a/tests/smoke/test_ucc_build.py +++ b/tests/smoke/test_ucc_build.py @@ -445,6 +445,15 @@ def test_ucc_generate_with_configuration_files_only(): "package_no_global_config", "package", ) + global_config_path = path.join( + path.dirname(path.realpath(__file__)), + "..", + "testdata", + "test_addons", + "package_no_global_config", + "globalConfig.json", + ) + assert not path.exists(global_config_path) build.generate(source=package_folder, output_directory=temp_dir) expected_folder = path.join( @@ -474,24 +483,12 @@ def test_ucc_generate_with_configuration_files_only(): expected_folder, actual_folder, ) - - -def test_ucc_generate_openapi_with_configuration_files_only(): - with tempfile.TemporaryDirectory() as temp_dir: - package_folder = path.join( - path.dirname(path.realpath(__file__)), - "..", - "testdata", - "test_addons", - "package_no_global_config", - "package", - ) - build.generate(source=package_folder, output_directory=temp_dir) - - actual_file_path = path.join( + openapi_file_path = path.join( temp_dir, "Splunk_TA_UCCExample", "appserver", "static", "openapi.json" ) - assert not path.exists(actual_file_path) + assert path.exists(openapi_file_path) + assert path.exists(global_config_path) + os.remove(global_config_path) def test_ucc_build_verbose_mode(caplog): diff --git a/tests/testdata/expected_addons/expected_output_no_global_config/Splunk_TA_UCCExample/appserver/static/js/build/globalConfig.json b/tests/testdata/expected_addons/expected_output_no_global_config/Splunk_TA_UCCExample/appserver/static/js/build/globalConfig.json new file mode 100644 index 0000000000..9e7fc2baad --- /dev/null +++ b/tests/testdata/expected_addons/expected_output_no_global_config/Splunk_TA_UCCExample/appserver/static/js/build/globalConfig.json @@ -0,0 +1,11 @@ +{ + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "Splunk_TA_UCCExample", + "displayName": "Splunk Add-on for UCC Example", + "version": "5.59.0+728642d3c", + "checkForUpdates": true, + "schemaVersion": "0.0.9", + "_uccVersion": "5.59.0" + } +} diff --git a/tests/testdata/expected_addons/expected_output_no_global_config/Splunk_TA_UCCExample/appserver/static/openapi.json b/tests/testdata/expected_addons/expected_output_no_global_config/Splunk_TA_UCCExample/appserver/static/openapi.json new file mode 100644 index 0000000000..4ca8a163b9 --- /dev/null +++ b/tests/testdata/expected_addons/expected_output_no_global_config/Splunk_TA_UCCExample/appserver/static/openapi.json @@ -0,0 +1,40 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Splunk_TA_UCCExample", + "version": "5.59.0+728642d3c", + "description": "Splunk Add-on for UCC Example", + "contact": { + "name": "Splunk Inc." + } + }, + "servers": [ + { + "url": "https://{domain}:{port}/servicesNS/-/Splunk_TA_UCCExample", + "variables": { + "domain": { + "default": "localhost" + }, + "port": { + "default": "8089" + } + }, + "description": "Access via management interface" + } + ], + "components": { + "schemas": {}, + "securitySchemes": { + "BasicAuth": { + "type": "http", + "scheme": "basic" + } + } + }, + "paths": {}, + "security": [ + { + "BasicAuth": [] + } + ] +} \ No newline at end of file diff --git a/tests/unit/commands/test_build.py b/tests/unit/commands/test_build.py index 0452a48250..6b48113302 100644 --- a/tests/unit/commands/test_build.py +++ b/tests/unit/commands/test_build.py @@ -192,3 +192,74 @@ def test_ta_name_mismatch( pip_legacy_resolver=False, ui_source_map=False, ) + + +@patch("splunk_add_on_ucc_framework.commands.build._get_app_manifest") +@patch("splunk_add_on_ucc_framework.commands.build._get_and_check_global_config_path") +@patch("os.path.isfile") +def app_conf_not_present( + mock_os_isfile, + mock_get_and_check_global_config_path, + mock_get_app_manifest, +): + mock_os_isfile.return_value = False + + mock_app_manifest = MagicMock() + mock_get_app_manifest.return_value = mock_app_manifest + mock_get_and_check_global_config_path.return_value = "" + + generate( + source="source/path", + addon_version="1.0.0", + python_binary_name="python3", + verbose_file_summary_report=False, + pip_version="latest", + pip_legacy_resolver=False, + ui_source_map=False, + ) + + +@patch("splunk_add_on_ucc_framework.global_config.GlobalConfig") +@patch("splunk_add_on_ucc_framework.commands.build._get_app_manifest") +@patch("splunk_add_on_ucc_framework.commands.build._get_and_check_global_config_path") +@patch("os.path.isfile") +@patch("os.path.exists") +def test_generate_when_gc_path_is_empty( + mock_os_path, + mock_os_isfile, + mock_get_and_check_global_config_path, + mock_get_app_manifest, + mock_global_config, +): + """Test generate() when global_config_path is empty.""" + + mock_os_path.return_value = True + mock_get_and_check_global_config_path.return_value = "" + + mock_app_manifest = MagicMock() + mock_get_app_manifest.return_value = mock_app_manifest + + mock_os_isfile.return_value = False + + # Exception is raised to stop the further execution of generate function. + mock_global_config.side_effect = Exception("Stop execution after condition is met") + try: + generate( + source="mock_source", + addon_version="1.0.0", + python_binary_name="python3", + verbose_file_summary_report=False, + pip_version="latest", + pip_legacy_resolver=False, + ui_source_map=False, + ) + except Exception as e: + assert str(e) == "Stop execution after condition is met" + + # Ensure GlobalConfig is instantiated when gc_path is empty + mock_global_config.assert_called_once_with( + "", + source="mock_source", + app_manifest=mock_app_manifest, + app_conf_content={}, # Since app.conf does not exist, it should be an empty dict + ) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index da209c897d..6aaa5ede3c 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest +from unittest import mock from splunk_add_on_ucc_framework import app_manifest as app_manifest_lib from splunk_add_on_ucc_framework import global_config as global_config_lib @@ -45,6 +46,33 @@ def app_manifest_correct() -> app_manifest_lib.AppManifest: return app_manifest +@pytest.fixture +def mock_app_manifest(): + mock_manifest = mock.MagicMock() + mock_manifest.get_addon_name.return_value = "test_addon" + mock_manifest.get_addon_version.return_value = "1.0.0" + mock_manifest.get_title.return_value = "Test title" + return mock_manifest + + +@pytest.fixture +def mock_app_conf_content(): + content = { + "package": {"check_for_updates": True}, + "ui": {"supported_themes": "light, dark"}, + } + return content + + +@pytest.fixture +def mock_app_conf_content_without_themes(): + content = { + "package": {"check_for_updates": True}, + "ui": {}, + } + return content + + @pytest.fixture def global_config_all_json_content(): with open(helpers.get_testdata_file_path("valid_config.json")) as fp: diff --git a/tests/unit/generators/test_doc_generator.py b/tests/unit/generators/test_doc_generator.py index 820b76b298..d132141663 100644 --- a/tests/unit/generators/test_doc_generator.py +++ b/tests/unit/generators/test_doc_generator.py @@ -40,6 +40,9 @@ def test_generate_docs(mock_open, mock_dirname, mock_realpath): "| ------------ | ------------ | ----------------- |", "| file1.conf | output/<YOUR_ADDON_NAME>/some/path | Conf file |", "| file2.xml | output/<YOUR_ADDON_NAME>/xml/path | XML file |", + "| globalConfig.json | <source_dir> | " + "Generates globalConfig.json file in the source code if globalConfig " + "is not present in source directory at build time. |", "\n", ] ) @@ -75,7 +78,10 @@ def test_generate_docs_empty_list(mock_open_file, mock_dirname, mock_realpath): "", "| File Name | File Location | File Description |", "| ------------ | ------------ | ----------------- |", - "\n", + "| globalConfig.json | <source_dir> | " + "Generates globalConfig.json file in the source code if globalConfig " + "is not present in source directory at build time. |" + "\n\n", ] ) diff --git a/tests/unit/generators/test_init_.py b/tests/unit/generators/test_init_.py index 01b48a6931..43195b7793 100644 --- a/tests/unit/generators/test_init_.py +++ b/tests/unit/generators/test_init_.py @@ -12,7 +12,13 @@ def test___init__gen(): assert gen.__all__ == expected_classes - not_allowed = ["conf_files", "xml_files", "html_files", "doc_generator"] + not_allowed = [ + "conf_files", + "xml_files", + "html_files", + "doc_generator", + "globalConfig_generator", + ] for attrib in dir(gen): if attrib.startswith("__") and attrib.endswith("__") or attrib in not_allowed: # ignore the builtin modules diff --git a/tests/unit/test_global_config.py b/tests/unit/test_global_config.py index a924177ffb..263f8d3911 100644 --- a/tests/unit/test_global_config.py +++ b/tests/unit/test_global_config.py @@ -11,6 +11,100 @@ from splunk_add_on_ucc_framework import global_config as global_config_lib +@mock.patch( + "splunk_add_on_ucc_framework.global_config.GlobalConfig.from_app_conf_and_app_manifest", + return_value="tmp_path", +) +def test_globalconfig_init_with_empty_path( + mock_function, tmp_path, mock_app_manifest, mock_app_conf_content +): + """Test GlobalConfig initialization when global_config_path is empty.""" + + source_dir = str(tmp_path / "source") + + global_config_lib.GlobalConfig( + global_config_path="", + source=source_dir, + app_manifest=mock_app_manifest, + app_conf_content=mock_app_conf_content, + ) + + mock_function.assert_called_once_with( + source_dir, mock_app_manifest, mock_app_conf_content + ) + + +@pytest.mark.parametrize( + "check_for_updates, expected_check_for_updates", + [ + ("true", True), + ("false", False), + ("1", True), + ("0", False), + ("yes", True), + ("no", False), + ("t", True), + ("f", False), + ], +) +@pytest.mark.parametrize( + "supported_themes, expected_supported_themes", + [ + ("light, dark", ["light", "dark"]), + ("light", ["light"]), + ("", []), # Empty case + (None, []), # None case + ], +) +def test_from_app_conf_and_app_manifest( + check_for_updates, + expected_check_for_updates, + supported_themes, + expected_supported_themes, + tmp_path, +): + mock_app_manifest = mock.MagicMock() + mock_app_manifest.get_addon_name.return_value = "test_addon" + mock_app_manifest.get_title.return_value = "Test Addon" + mock_app_manifest.get_addon_version.return_value = "1.0.0" + + mock_app_conf_content = { + "package": {"check_for_updates": check_for_updates}, + "ui": {"supported_themes": supported_themes}, + } + + source_dir = str(tmp_path / "source") + os.makedirs(source_dir, exist_ok=True) + gc_instance = global_config_lib.GlobalConfig( + global_config_path="", + source=source_dir, + app_manifest=mock_app_manifest, + app_conf_content=mock_app_conf_content, + ) + global_config_path = gc_instance.from_app_conf_and_app_manifest( + source_dir, mock_app_manifest, mock_app_conf_content + ) + + expected_path = os.path.join(source_dir, os.pardir, "globalConfig.json") + + # Verify the correct JSON content is written + expected_content = { + "meta": { + "name": "test_addon", + "restRoot": "test_addon", + "displayName": "Test Addon", + "version": "1.0.0", + "checkForUpdates": expected_check_for_updates, + } + } + + if expected_supported_themes: + expected_content["meta"]["supportedThemes"] = expected_supported_themes + + assert global_config_path == expected_path + assert gc_instance._content == expected_content + + @pytest.mark.parametrize( "filename", [ diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index f0e97c0087..cc4d507fed 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -33,6 +33,7 @@ def test_get_j2_env(): "conf_files/settings_conf.template", "conf_files/tags_conf.template", "conf_files/web_conf.template", + "minimal_globalConfig.json.template", ] assert sorted(expected_list_of_templates) == sorted(list_of_templates)