diff --git a/.changelog/4663.yml b/.changelog/4663.yml new file mode 100644 index 00000000000..83b1b0075b7 --- /dev/null +++ b/.changelog/4663.yml @@ -0,0 +1,4 @@ +changes: +- description: Introducing functionality for updating CONTENT_PATH global. + type: feature +pr_number: 4663 diff --git a/TestSuite/repo.py b/TestSuite/repo.py index b19a53702c7..39651435406 100644 --- a/TestSuite/repo.py +++ b/TestSuite/repo.py @@ -4,6 +4,7 @@ from typing import List, Optional from demisto_sdk.commands.common.constants import DEMISTO_GIT_PRIMARY_BRANCH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.content_graph.commands.create import create_content_graph from demisto_sdk.commands.content_graph.interface import ( @@ -43,6 +44,9 @@ def __init__(self, tmpdir: Path, init_git: bool = False): self._packs_path: Path = tmpdir / "Packs" self._packs_path.mkdir() self.path = str(self._tmpdir) + # Update global content's path for the lifetime of this instance. + self.old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(self.path) # Initiate ./Tests/ dir self._test_dir = tmpdir / "Tests" @@ -99,6 +103,9 @@ def __del__(self): if self.graph_interface: self.graph_interface.close() + # Restore original content path. + ContentPaths.update_content_path(self.old_content_path) + def setup_one_pack( self, name: Optional[str] = None, marketplaces: List[str] = DEFAULT_MARKETPLACES ) -> Pack: diff --git a/conftest.py b/conftest.py index af46bfece8f..4daf5b5fd24 100644 --- a/conftest.py +++ b/conftest.py @@ -2,7 +2,6 @@ import os import shutil -from pathlib import Path from typing import Generator from unittest import mock @@ -24,13 +23,15 @@ def get_repo(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Repo: - tmp_dir = _mk_tmp(request, tmp_path_factory) - return Repo(tmp_dir) + content_tmp_dir = _mk_tmp(request, tmp_path_factory) / "content" + content_tmp_dir.mkdir() + return Repo(content_tmp_dir) def get_git_repo(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Repo: - tmp_dir = _mk_tmp(request, tmp_path_factory) - return Repo(tmp_dir, init_git=True) + content_tmp_dir = _mk_tmp(request, tmp_path_factory) / "content" + content_tmp_dir.mkdir() + return Repo(content_tmp_dir, init_git=True) def get_pack(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Pack: @@ -101,11 +102,9 @@ def graph_repo(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Ge import demisto_sdk.commands.content_graph.objects.base_content as bc repo = get_repo(request, tmp_path_factory) + neo4j_path = bc.ContentPaths.CONTENT_PATH.parent.parent / "neo4j" - bc.CONTENT_PATH = Path(repo.path) - neo4j_path = bc.CONTENT_PATH.parent.parent / "neo4j" - - mock.patch.object(ContentGraphInterface, "repo_path", bc.CONTENT_PATH) + mock.patch.object(ContentGraphInterface, "repo_path", bc.ContentPaths.CONTENT_PATH) mock.patch.object(neo4j_service, "REPO_PATH", neo4j_path) yield repo diff --git a/demisto_sdk/__init__.py b/demisto_sdk/__init__.py index 5bf2275f6b7..81d1498c526 100644 --- a/demisto_sdk/__init__.py +++ b/demisto_sdk/__init__.py @@ -1,4 +1,10 @@ -if __name__ in ["__main__", "demisto_sdk"]: +import os + +if os.environ.get("DEMISTO_SDK_SKIP_LOGGER_SETUP", "False").lower() not in [ + "true", + "yes", + "1", +]: from demisto_sdk.commands.common.logger import logging_setup logging_setup(initial=True, calling_function="__init__") diff --git a/demisto_sdk/__main__.py b/demisto_sdk/__main__.py index 5bfda94b65d..26289e59ddf 100644 --- a/demisto_sdk/__main__.py +++ b/demisto_sdk/__main__.py @@ -37,10 +37,7 @@ FileType, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import ( - ALL_PACKS_DEPENDENCIES_DEFAULT_PATH, - CONTENT_PATH, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.hook_validations.readme import ReadMeValidator @@ -212,7 +209,7 @@ def main(ctx, config, version, release_notes, **kwargs): import dotenv dotenv.load_dotenv( - CONTENT_PATH / ".env", override=True + ContentPaths.CONTENT_PATH / ".env", override=True ) # load .env file from the cwd if platform.system() == "Windows": @@ -2712,7 +2709,9 @@ def find_dependencies(ctx, **kwargs): use_pack_metadata = kwargs.get("use_pack_metadata", False) all_packs_dependencies = kwargs.get("all_packs_dependencies", False) get_dependent_on = kwargs.get("get_dependent_on", False) - output_path = kwargs.get("output_path", ALL_PACKS_DEPENDENCIES_DEFAULT_PATH) + output_path = kwargs.get( + "output_path", ContentPaths.ALL_PACKS_DEPENDENCIES_DEFAULT_PATH + ) dependency = kwargs.get("dependency", "") try: PackDependencies.find_dependencies_manager( @@ -3644,12 +3643,12 @@ def setup_env( if ide == "auto-detect": # Order decides which IDEType will be selected for configuration if multiple IDEs are detected - if (CONTENT_PATH / ".vscode").exists(): + if (ContentPaths.CONTENT_PATH / ".vscode").exists(): logger.info( "Visual Studio Code IDEType has been detected and will be configured." ) ide_type = IDEType.VSCODE - elif (CONTENT_PATH / ".idea").exists(): + elif (ContentPaths.CONTENT_PATH / ".idea").exists(): logger.info( "PyCharm / IDEA IDEType has been detected and will be configured." ) @@ -3912,7 +3911,7 @@ def xsoar_linter( def dump_api( ctx: typer.Context, output_path: Path = typer.Option( - CONTENT_PATH, + ContentPaths.CONTENT_PATH, "-o", "--output", help="The output directory or JSON file to save the demisto-sdk api.", diff --git a/demisto_sdk/commands/common/content_constant_paths.py b/demisto_sdk/commands/common/content_constant_paths.py index 266b6e969b8..f45dd9978a9 100644 --- a/demisto_sdk/commands/common/content_constant_paths.py +++ b/demisto_sdk/commands/common/content_constant_paths.py @@ -1,45 +1,89 @@ from pathlib import Path +from typing import Union from demisto_sdk.commands.common.constants import NATIVE_IMAGE_FILE_NAME, TESTS_DIR from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import get_content_path -CONTENT_PATH: Path = Path(get_content_path()) - -ALL_PACKS_DEPENDENCIES_DEFAULT_PATH = CONTENT_PATH / "all_packs_dependencies.json" - -CONF_PATH = CONTENT_PATH / TESTS_DIR / "conf.json" - -DEFAULT_ID_SET_PATH = CONTENT_PATH / TESTS_DIR / "id_set.json" -MP_V2_ID_SET_PATH = CONTENT_PATH / TESTS_DIR / "id_set_mp_v2.json" -XPANSE_ID_SET_PATH = CONTENT_PATH / TESTS_DIR / "id_set_xpanse.json" -LANDING_PAGE_SECTIONS_PATH = ( - CONTENT_PATH / TESTS_DIR / "Marketplace" / "landingPage_sections.json" -) -NATIVE_IMAGE_PATH = CONTENT_PATH / "Tests" / NATIVE_IMAGE_FILE_NAME - -COMMON_SERVER_PYTHON_PATH = ( - CONTENT_PATH / "Packs" / "Base" / "Scripts" / "CommonServerPython" -) -DEMISTO_MOCK_PATH = CONTENT_PATH / TESTS_DIR / "demistomock" -API_MODULES_SCRIPTS_DIR = CONTENT_PATH / "Packs" / "ApiModules" / "Scripts" - -PYTHONPATH = [ - path.absolute() - for path in [ - Path(CONTENT_PATH), - COMMON_SERVER_PYTHON_PATH, - DEMISTO_MOCK_PATH, - Path(__file__).parent.parent / "lint" / "resources" / "pylint_plugins", + +class ContentPaths: + # Class-level variables (shared across all instances) + CONTENT_PATH: Path = Path(get_content_path()) + ALL_PACKS_DEPENDENCIES_DEFAULT_PATH: Path = ( + CONTENT_PATH / "all_packs_dependencies.json" + ) + CONF_PATH: Path = CONTENT_PATH / TESTS_DIR / "conf.json" + DEFAULT_ID_SET_PATH: Path = CONTENT_PATH / TESTS_DIR / "id_set.json" + MP_V2_ID_SET_PATH: Path = CONTENT_PATH / TESTS_DIR / "id_set_mp_v2.json" + XPANSE_ID_SET_PATH: Path = CONTENT_PATH / TESTS_DIR / "id_set_xpanse.json" + LANDING_PAGE_SECTIONS_PATH: Path = ( + CONTENT_PATH / TESTS_DIR / "Marketplace" / "landingPage_sections.json" + ) + NATIVE_IMAGE_PATH: Path = CONTENT_PATH / TESTS_DIR / NATIVE_IMAGE_FILE_NAME + COMMON_SERVER_PYTHON_PATH: Path = ( + CONTENT_PATH / "Packs" / "Base" / "Scripts" / "CommonServerPython" + ) + DEMISTO_MOCK_PATH: Path = CONTENT_PATH / TESTS_DIR / "demistomock" + API_MODULES_SCRIPTS_DIR: Path = CONTENT_PATH / "Packs" / "ApiModules" / "Scripts" + PYTHONPATH = [ + path.absolute() + for path in [ + CONTENT_PATH, + COMMON_SERVER_PYTHON_PATH, + DEMISTO_MOCK_PATH, + Path(__file__).parent.parent / "lint" / "resources" / "pylint_plugins", + ] ] -] + PYTHONPATH_STR = ":".join(str(path) for path in PYTHONPATH) -if API_MODULES_SCRIPTS_DIR.exists(): - PYTHONPATH.extend(path.absolute() for path in API_MODULES_SCRIPTS_DIR.iterdir()) + @classmethod + def update_content_path(cls, content_path: Union[str, Path]) -> None: + """ + Updates the class-level content path and derived paths. -else: - logger.debug( - "Could not add API modules to 'PYTHONPATH' as the base directory does not exist." - ) + Args: + content_path (Union[str, Path]): The new content path to set. + """ + logger.info(f"Updating content_path globally: {content_path}") + + cls.CONTENT_PATH = Path(content_path) + logger.info(f"CONTENT_PATH Type: {type(cls.CONTENT_PATH)}") + cls.ALL_PACKS_DEPENDENCIES_DEFAULT_PATH = ( + cls.CONTENT_PATH / "all_packs_dependencies.json" + ) + cls.CONF_PATH = cls.CONTENT_PATH / TESTS_DIR / "conf.json" + cls.DEFAULT_ID_SET_PATH = cls.CONTENT_PATH / TESTS_DIR / "id_set.json" + cls.MP_V2_ID_SET_PATH = cls.CONTENT_PATH / TESTS_DIR / "id_set_mp_v2.json" + cls.XPANSE_ID_SET_PATH = cls.CONTENT_PATH / TESTS_DIR / "id_set_xpanse.json" + cls.LANDING_PAGE_SECTIONS_PATH = ( + cls.CONTENT_PATH / TESTS_DIR / "Marketplace" / "landingPage_sections.json" + ) + cls.NATIVE_IMAGE_PATH = cls.CONTENT_PATH / TESTS_DIR / NATIVE_IMAGE_FILE_NAME + cls.COMMON_SERVER_PYTHON_PATH = ( + cls.CONTENT_PATH / "Packs" / "Base" / "Scripts" / "CommonServerPython" + ) + cls.DEMISTO_MOCK_PATH = cls.CONTENT_PATH / TESTS_DIR / "demistomock" + cls.API_MODULES_SCRIPTS_DIR = ( + cls.CONTENT_PATH / "Packs" / "ApiModules" / "Scripts" + ) + + cls.PYTHONPATH = [ + path.absolute() + for path in [ + cls.CONTENT_PATH, + cls.COMMON_SERVER_PYTHON_PATH, + cls.DEMISTO_MOCK_PATH, + Path(__file__).parent.parent / "lint" / "resources" / "pylint_plugins", + ] + ] + + if cls.API_MODULES_SCRIPTS_DIR.exists(): + cls.PYTHONPATH.extend( + path.absolute() for path in cls.API_MODULES_SCRIPTS_DIR.iterdir() + ) + else: + logger.debug( + "Could not add API modules to 'PYTHONPATH' as the base directory does not exist." + ) -PYTHONPATH_STR = ":".join(str(path) for path in PYTHONPATH) + cls.PYTHONPATH_STR = ":".join(str(path) for path in cls.PYTHONPATH) diff --git a/demisto_sdk/commands/common/errors.py b/demisto_sdk/commands/common/errors.py index 67652cec8b4..c7484e32151 100644 --- a/demisto_sdk/commands/common/errors.py +++ b/demisto_sdk/commands/common/errors.py @@ -20,7 +20,7 @@ FileType, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.tools import is_external_repository FOUND_FILES_AND_ERRORS: list = [] @@ -2512,8 +2512,8 @@ def integration_not_registered( file_path, missing_test_playbook_configurations, no_tests_key ): return ( - f"The following integration is not registered in {CONF_PATH} file.\n" - f"Please add:\n{missing_test_playbook_configurations}\nto {CONF_PATH} " + f"The following integration is not registered in {ContentPaths.CONF_PATH} file.\n" + f"Please add:\n{missing_test_playbook_configurations}\nto {ContentPaths.CONF_PATH} " f"path under 'tests' key.\n" f"If you don't want to add a test playbook for this integration, please add: \n{no_tests_key}to the " f"file {file_path} or run 'demisto-sdk format -i {file_path}'" @@ -2539,10 +2539,10 @@ def test_playbook_not_configured( missing_integration_configurations, ): return ( - f"The TestPlaybook {content_item_id} is not registered in {CONF_PATH} file.\n " + f"The TestPlaybook {content_item_id} is not registered in {ContentPaths.CONF_PATH} file.\n " f"Please add\n{missing_test_playbook_configurations}\n " f"or if this test playbook is for an integration\n{missing_integration_configurations}\n " - f"to {CONF_PATH} path under 'tests' key." + f"to {ContentPaths.CONF_PATH} path under 'tests' key." ) @staticmethod diff --git a/demisto_sdk/commands/common/hook_validations/conf_json.py b/demisto_sdk/commands/common/hook_validations/conf_json.py index 10b78c739ba..193052fb5e2 100644 --- a/demisto_sdk/commands/common/hook_validations/conf_json.py +++ b/demisto_sdk/commands/common/hook_validations/conf_json.py @@ -1,7 +1,7 @@ from pathlib import Path from demisto_sdk.commands.common.constants import API_MODULES_PACK, FileType -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.errors import Errors from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.hook_validations.base_validator import ( @@ -37,7 +37,7 @@ def __init__( self.conf_data = self.load_conf_file() def load_conf_file(self): - with open(CONF_PATH) as data_file: + with open(ContentPaths.CONF_PATH) as data_file: return json.load(data_file) def is_valid_conf_json(self): @@ -69,7 +69,9 @@ def is_valid_description_in_conf_dict(self, checked_dict): error_message, error_code = Errors.description_missing_from_conf_json( problematic_instances ) - if self.handle_error(error_message, error_code, file_path=CONF_PATH): + if self.handle_error( + error_message, error_code, file_path=ContentPaths.CONF_PATH + ): self._is_valid = False return self._is_valid @@ -91,7 +93,9 @@ def is_test_in_conf_json(self, file_id): return True error_message, error_code = Errors.test_not_in_conf_json(file_id) - if self.handle_error(error_message, error_code, file_path=CONF_PATH): + if self.handle_error( + error_message, error_code, file_path=ContentPaths.CONF_PATH + ): return False return True diff --git a/demisto_sdk/commands/common/hook_validations/content_entity_validator.py b/demisto_sdk/commands/common/hook_validations/content_entity_validator.py index a4c02fb3e7b..3eb064ee3ed 100644 --- a/demisto_sdk/commands/common/hook_validations/content_entity_validator.py +++ b/demisto_sdk/commands/common/hook_validations/content_entity_validator.py @@ -30,7 +30,7 @@ FileType, ) from demisto_sdk.commands.common.content import Content -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH, CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.errors import Errors from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.handlers import DEFAULT_YAML_HANDLER as yaml @@ -224,7 +224,7 @@ def is_valid_marketplaces_on_modified(self) -> bool: try: old_pack_marketplaces = set( get_remote_file( - f"{CONTENT_PATH}/Packs/{pack_name}/pack_metadata.json", + f"{ContentPaths.CONTENT_PATH}/Packs/{pack_name}/pack_metadata.json", tag=self.prev_ver, ).get(MARKETPLACE_KEY_PACK_METADATA, ()) ) @@ -368,7 +368,7 @@ def _is_id_equals_name(self, file_type): return True def _load_conf_file(self): - with open(CONF_PATH) as data_file: + with open(ContentPaths.CONF_PATH) as data_file: return json.load(data_file) @error_codes("CJ104,CJ102") diff --git a/demisto_sdk/commands/common/hook_validations/readme.py b/demisto_sdk/commands/common/hook_validations/readme.py index af0d7d52550..b25239bb2fb 100644 --- a/demisto_sdk/commands/common/hook_validations/readme.py +++ b/demisto_sdk/commands/common/hook_validations/readme.py @@ -18,7 +18,7 @@ RELATIVE_HREF_URL_REGEX, RELATIVE_MARKDOWN_URL_REGEX, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.docker_helper import init_global_docker_client from demisto_sdk.commands.common.errors import ( FOUND_FILES_AND_ERRORS, @@ -173,7 +173,7 @@ def __init__( json_file_path=json_file_path, specific_validations=specific_validations, ) - self.content_path = CONTENT_PATH + self.content_path = ContentPaths.CONTENT_PATH self.file_path_str = file_path self.file_path = Path(file_path) self.pack_path = self.file_path.parent @@ -757,7 +757,9 @@ def empty_context_mgr(bool): if mdx_server_is_up(): # this allows for this context to be reentrant logger.debug("server is already up. Not restarting") return empty_context_mgr(True) - if ReadMeValidator.are_modules_installed_for_verify(CONTENT_PATH): # type: ignore + if ReadMeValidator.are_modules_installed_for_verify( + ContentPaths.CONTENT_PATH + ): # type: ignore ReadMeValidator.add_node_env_vars() return start_local_MDX_server(handle_error, file_path) elif ReadMeValidator.is_docker_available(): @@ -766,7 +768,7 @@ def empty_context_mgr(bool): @staticmethod def add_node_env_vars(): - content_path = CONTENT_PATH + content_path = ContentPaths.CONTENT_PATH node_modules_path = content_path / Path("node_modules") # type: ignore os.environ["NODE_PATH"] = ( str(node_modules_path) + os.pathsep + os.getenv("NODE_PATH", "") diff --git a/demisto_sdk/commands/common/native_image.py b/demisto_sdk/commands/common/native_image.py index babb0c83f72..88844aca01d 100644 --- a/demisto_sdk/commands/common/native_image.py +++ b/demisto_sdk/commands/common/native_image.py @@ -5,7 +5,7 @@ from pydantic import BaseModel from demisto_sdk.commands.common.constants import NATIVE_IMAGE_DOCKER_NAME -from demisto_sdk.commands.common.content_constant_paths import NATIVE_IMAGE_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.hook_validations.docker import DockerImageValidator from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.singleton import PydanticSingleton @@ -68,7 +68,9 @@ def get_instance_from(cls, *args, **kwargs): return cls.from_path(*args, **kwargs) @classmethod - def from_path(cls, native_image_config_file_path: Path = Path(NATIVE_IMAGE_PATH)): + def from_path( + cls, native_image_config_file_path: Path = Path(ContentPaths.NATIVE_IMAGE_PATH) + ): native_image_config = cls.parse_file(native_image_config_file_path) native_image_config.__docker_images_to_native_images_support() return native_image_config diff --git a/demisto_sdk/commands/common/tools.py b/demisto_sdk/commands/common/tools.py index 40ee9eea5cd..306842683e8 100644 --- a/demisto_sdk/commands/common/tools.py +++ b/demisto_sdk/commands/common/tools.py @@ -4443,10 +4443,8 @@ def extract_image_paths_from_str( ) -> List[str]: """ Args: - local_paths (List[str]): list of file paths - is_lower (bool): True to check when line is lower cased. - to_split (bool): True to split the line in order to search specific word text (str): The readme content to search. + regex_str (str): regex to search for. Returns: list of lines which contains the given text. diff --git a/demisto_sdk/commands/common/update_id_set.py b/demisto_sdk/commands/common/update_id_set.py index 782c9de0913..0665e417d6c 100755 --- a/demisto_sdk/commands/common/update_id_set.py +++ b/demisto_sdk/commands/common/update_id_set.py @@ -51,11 +51,7 @@ FileType, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import ( - DEFAULT_ID_SET_PATH, - MP_V2_ID_SET_PATH, - XPANSE_ID_SET_PATH, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.logger import logger @@ -2629,7 +2625,7 @@ def merge_id_sets( def re_create_id_set( # noqa: C901 - id_set_path: Optional[Path] = DEFAULT_ID_SET_PATH, + id_set_path: Optional[Path] = ContentPaths.DEFAULT_ID_SET_PATH, pack_to_create=None, objects_to_create: list = None, print_logs: bool = True, @@ -2652,9 +2648,9 @@ def re_create_id_set( # noqa: C901 """ if id_set_path == "": id_set_path = { - MarketplaceVersions.MarketplaceV2.value: MP_V2_ID_SET_PATH, - MarketplaceVersions.XPANSE.value: XPANSE_ID_SET_PATH, - }.get(marketplace, DEFAULT_ID_SET_PATH) + MarketplaceVersions.MarketplaceV2.value: ContentPaths.MP_V2_ID_SET_PATH, + MarketplaceVersions.XPANSE.value: ContentPaths.XPANSE_ID_SET_PATH, + }.get(marketplace, ContentPaths.DEFAULT_ID_SET_PATH) if not objects_to_create: if marketplace == MarketplaceVersions.MarketplaceV2.value: diff --git a/demisto_sdk/commands/content_graph/commands/get_relationships.py b/demisto_sdk/commands/content_graph/commands/get_relationships.py index 949b3e3424f..319e00a3adf 100644 --- a/demisto_sdk/commands/content_graph/commands/get_relationships.py +++ b/demisto_sdk/commands/content_graph/commands/get_relationships.py @@ -248,12 +248,12 @@ def path_to_str( ) -> str: def node_to_str(node_data: dict) -> str: name = f"{node_data['name']}" - content_type = f"{node_data['content_type']}" + content_type = f"{node_data['content_type']}" path = node_data["path"] return f"• ({name}:{content_type} {{path: {path}}})\n" def rel_to_str(rel: RelationshipType, props: dict) -> str: - return f" └─ [{rel}]{props or ''} ↴\n" + return f" └─ [{rel}]{props or ''} ↴\n" path_str = "" for idx, path_element in enumerate(path): diff --git a/demisto_sdk/commands/content_graph/interface/graph.py b/demisto_sdk/commands/content_graph/interface/graph.py index 2ea6af070bb..df5e595468b 100644 --- a/demisto_sdk/commands/content_graph/interface/graph.py +++ b/demisto_sdk/commands/content_graph/interface/graph.py @@ -4,7 +4,7 @@ from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Tuple, Union from demisto_sdk.commands.common.constants import MarketplaceVersions -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import ( @@ -32,7 +32,7 @@ class DeprecatedItemUsage(NamedTuple): class ContentGraphInterface(ABC): - repo_path = CONTENT_PATH # type: ignore + repo_path = ContentPaths.CONTENT_PATH # type: ignore METADATA_FILE_NAME = "metadata.json" DEPENDS_ON_FILE_NAME = "depends_on.json" _depends_on = None diff --git a/demisto_sdk/commands/content_graph/objects/base_content.py b/demisto_sdk/commands/content_graph/objects/base_content.py index 50c173d1ff1..2fff5e8433b 100644 --- a/demisto_sdk/commands/content_graph/objects/base_content.py +++ b/demisto_sdk/commands/content_graph/objects/base_content.py @@ -28,7 +28,7 @@ GitStatuses, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import JSON_Handler from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import set_value, write_dict @@ -182,7 +182,7 @@ def to_dict(self) -> Dict[str, Any]: ) if "path" in json_dct and Path(json_dct["path"]).is_absolute(): json_dct["path"] = ( - Path(json_dct["path"]).relative_to(CONTENT_PATH) + Path(json_dct["path"]).relative_to(ContentPaths.CONTENT_PATH) ).as_posix() # type: ignore json_dct["content_type"] = self.content_type return json_dct diff --git a/demisto_sdk/commands/content_graph/objects/conf_json.py b/demisto_sdk/commands/content_graph/objects/conf_json.py index 781f6425436..d95236ccef6 100644 --- a/demisto_sdk/commands/content_graph/objects/conf_json.py +++ b/demisto_sdk/commands/content_graph/objects/conf_json.py @@ -13,7 +13,7 @@ from pydantic import BaseModel, Extra, Field, validator from demisto_sdk.commands.common.constants import MarketplaceVersions -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.tools import get_json from demisto_sdk.commands.content_graph.common import ContentType @@ -88,7 +88,7 @@ class ConfJSON(StrictBaseModel): reputation_tests: List[str] @staticmethod - def from_path(path: Path = CONF_PATH) -> "ConfJSON": + def from_path(path: Path = ContentPaths.CONF_PATH) -> "ConfJSON": return ConfJSON(**get_json(path)) # type:ignore[assignment] @property diff --git a/demisto_sdk/commands/content_graph/objects/content_item.py b/demisto_sdk/commands/content_graph/objects/content_item.py index 452756cae2c..355e94eb5f6 100644 --- a/demisto_sdk/commands/content_graph/objects/content_item.py +++ b/demisto_sdk/commands/content_graph/objects/content_item.py @@ -22,7 +22,7 @@ from pydantic import DirectoryPath, Field, fields, validator from demisto_sdk.commands.common.constants import PACKS_FOLDER, MarketplaceVersions -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import ( get_file, @@ -60,9 +60,12 @@ class ContentItem(BaseContent): def validate_path(cls, v: Path, values) -> Path: if v.is_absolute(): return v - if not CONTENT_PATH.name: - return CONTENT_PATH / v - return CONTENT_PATH.with_name(values.get("source_repo", "content")) / v + if not ContentPaths.CONTENT_PATH.name: + return ContentPaths.CONTENT_PATH / v + return ( + ContentPaths.CONTENT_PATH.with_name(values.get("source_repo", "content")) + / v + ) @staticmethod @abstractmethod @@ -126,7 +129,8 @@ def get_pack( if not pack: if pack_name := get_pack_name(path): pack = BaseContent.from_path( - CONTENT_PATH / PACKS_FOLDER / pack_name, metadata_only=True + ContentPaths.CONTENT_PATH / PACKS_FOLDER / pack_name, + metadata_only=True, ) # type: ignore[assignment] return pack # type: ignore[return-value] @@ -134,13 +138,13 @@ def get_pack( def ignored_errors(self) -> List[str]: if ignored_errors := self.get_ignored_errors(self.path.name): return ignored_errors - file_path = get_relative_path(self.path, CONTENT_PATH) + file_path = get_relative_path(self.path, ContentPaths.CONTENT_PATH) return self.get_ignored_errors(file_path) def ignored_errors_related_files(self, file_path: Path) -> List[str]: if ignored_errors := self.get_ignored_errors((Path(file_path)).name): return ignored_errors - file_path = get_relative_path(file_path, CONTENT_PATH) + file_path = get_relative_path(file_path, ContentPaths.CONTENT_PATH) return self.get_ignored_errors(file_path) def get_ignored_errors(self, path: Union[str, Path]) -> List[str]: diff --git a/demisto_sdk/commands/content_graph/objects/pack.py b/demisto_sdk/commands/content_graph/objects/pack.py index 22db0e16149..d593fad3102 100644 --- a/demisto_sdk/commands/content_graph/objects/pack.py +++ b/demisto_sdk/commands/content_graph/objects/pack.py @@ -19,7 +19,7 @@ ImagesFolderNames, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import ( MarketplaceTagParser, @@ -143,9 +143,12 @@ def from_orm(cls, obj) -> "Pack": def validate_path(cls, v: Path, values) -> Path: if v.is_absolute(): return v - if not CONTENT_PATH.name: - return CONTENT_PATH / v - return CONTENT_PATH.with_name(values.get("source_repo", "content")) / v + if not ContentPaths.CONTENT_PATH.name: + return ContentPaths.CONTENT_PATH / v + return ( + ContentPaths.CONTENT_PATH.with_name(values.get("source_repo", "content")) + / v + ) @property def is_private(self) -> bool: @@ -159,13 +162,13 @@ def pack_id(self) -> str: def ignored_errors(self) -> List[str]: if ignored_errors := self.get_ignored_errors(PACK_METADATA_FILENAME): return ignored_errors - file_path = get_relative_path(self.path, CONTENT_PATH) + file_path = get_relative_path(self.path, ContentPaths.CONTENT_PATH) return self.get_ignored_errors(file_path / PACK_METADATA_FILENAME) def ignored_errors_related_files(self, file_path: Path) -> List[str]: if ignored_errors := self.get_ignored_errors((Path(file_path)).name): return ignored_errors - file_path = get_relative_path(file_path, CONTENT_PATH) + file_path = get_relative_path(file_path, ContentPaths.CONTENT_PATH) return self.get_ignored_errors(file_path) def get_ignored_errors(self, path: Union[str, Path]) -> List[str]: @@ -532,7 +535,7 @@ def _upload_item_by_item( def _copy_base_pack_docs( self, destination_path: Path, marketplace: MarketplaceVersions ): - documentation_path = CONTENT_PATH / "Documentation" + documentation_path = ContentPaths.CONTENT_PATH / "Documentation" documentation_output = destination_path / "Documentation" documentation_output.mkdir(exist_ok=True, parents=True) if ( diff --git a/demisto_sdk/commands/content_graph/objects/pack_metadata.py b/demisto_sdk/commands/content_graph/objects/pack_metadata.py index 8dcb585415e..d4ca91652e0 100644 --- a/demisto_sdk/commands/content_graph/objects/pack_metadata.py +++ b/demisto_sdk/commands/content_graph/objects/pack_metadata.py @@ -9,9 +9,7 @@ DEFAULT_CONTENT_ITEM_TO_VERSION, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import ( - LANDING_PAGE_SECTIONS_PATH, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import JSON_Handler from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import get_json, is_external_repository @@ -459,10 +457,10 @@ def _get_tags_from_landing_page(self, pack_id: str) -> set: tags: set = set() try: - landing_page_sections = get_json(LANDING_PAGE_SECTIONS_PATH) + landing_page_sections = get_json(ContentPaths.LANDING_PAGE_SECTIONS_PATH) except FileNotFoundError as e: logger.warning( - f"Couldn't find the landing_page file in path {LANDING_PAGE_SECTIONS_PATH}. Skipping collecting tags by landing page sections.\n{e}" + f"Couldn't find the landing_page file in path {ContentPaths.LANDING_PAGE_SECTIONS_PATH}. Skipping collecting tags by landing page sections.\n{e}" ) return tags diff --git a/demisto_sdk/commands/content_graph/objects/repository.py b/demisto_sdk/commands/content_graph/objects/repository.py index 76ce4184c6d..30e0cc43eef 100644 --- a/demisto_sdk/commands/content_graph/objects/repository.py +++ b/demisto_sdk/commands/content_graph/objects/repository.py @@ -9,7 +9,7 @@ from pydantic import BaseModel, DirectoryPath from demisto_sdk.commands.common.constants import MarketplaceVersions -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.content_graph.objects.pack import Pack @@ -19,7 +19,9 @@ @lru_cache -def from_path(path: Path = CONTENT_PATH, packs_to_parse: Optional[Tuple[str]] = None): +def from_path( + path: Path = ContentPaths.CONTENT_PATH, packs_to_parse: Optional[Tuple[str]] = None +): """ Returns a ContentDTO object with all the packs of the content repository. @@ -40,12 +42,13 @@ def from_path(path: Path = CONTENT_PATH, packs_to_parse: Optional[Tuple[str]] = class ContentDTO(BaseModel): - path: DirectoryPath = Path(CONTENT_PATH) # type: ignore + path: DirectoryPath = Path(ContentPaths.CONTENT_PATH) # type: ignore packs: List[Pack] @staticmethod def from_path( - path: Path = CONTENT_PATH, packs_to_parse: Optional[Tuple[str, ...]] = None + path: Path = ContentPaths.CONTENT_PATH, + packs_to_parse: Optional[Tuple[str, ...]] = None, ): """ Returns a ContentDTO object with all the packs of the content repository. diff --git a/demisto_sdk/commands/content_graph/parsers/base_content.py b/demisto_sdk/commands/content_graph/parsers/base_content.py index 9db360275d7..61b0013f8c0 100644 --- a/demisto_sdk/commands/content_graph/parsers/base_content.py +++ b/demisto_sdk/commands/content_graph/parsers/base_content.py @@ -7,9 +7,7 @@ from pydantic import Field from demisto_sdk.commands.common.constants import MarketplaceVersions -from demisto_sdk.commands.common.content_constant_paths import ( - CONTENT_PATH, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.common import ContentType from demisto_sdk.commands.content_graph.strict_objects.base_strict_model import ( StructureError, @@ -60,7 +58,7 @@ def node_id(self) -> str: @property def source_repo(self) -> Optional[str]: - return CONTENT_PATH.name + return ContentPaths.CONTENT_PATH.name @staticmethod def update_marketplaces_set_with_xsoar_values(marketplaces_set: set) -> set: diff --git a/demisto_sdk/commands/content_graph/tests/create_content_graph_test.py b/demisto_sdk/commands/content_graph/tests/create_content_graph_test.py index 536679da348..551727afc38 100644 --- a/demisto_sdk/commands/content_graph/tests/create_content_graph_test.py +++ b/demisto_sdk/commands/content_graph/tests/create_content_graph_test.py @@ -9,6 +9,7 @@ SKIP_PREPARE_SCRIPT_NAME, MarketplaceVersions, ) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.docker.docker_image import DockerImage from demisto_sdk.commands.content_graph.common import ( ContentType, @@ -38,16 +39,25 @@ @pytest.fixture -def repository(mocker): +def repository(mocker, tmp_path: Path): + content_temp_dir = tmp_path / "content" + content_temp_dir.mkdir() + + # Using this content path for a test's lifetime only and restoring when terminated. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(content_temp_dir) + repository = ContentDTO( - path=Path(), packs=[], ) mocker.patch( "demisto_sdk.commands.content_graph.content_graph_builder.ContentGraphBuilder._create_content_dto", return_value=repository, ) - return repository + yield repository + + # Cleanup and content path restoration. + ContentPaths.update_content_path(old_content_path) def mock_pack( diff --git a/demisto_sdk/commands/content_graph/tests/format_with_graph_test.py b/demisto_sdk/commands/content_graph/tests/format_with_graph_test.py index 5c32fdff63b..2c4999d697f 100644 --- a/demisto_sdk/commands/content_graph/tests/format_with_graph_test.py +++ b/demisto_sdk/commands/content_graph/tests/format_with_graph_test.py @@ -36,10 +36,13 @@ @pytest.fixture(autouse=True) def setup_method(mocker, tmp_path_factory, repo: Repo): """Auto-used fixture for setup before every test run""" - import demisto_sdk.commands.content_graph.objects.base_content as bc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.files.file import File - bc.CONTENT_PATH = Path(repo.path) + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(Path(repo.path)) + mocker.patch.object( neo4j_service, "NEO4J_DIR", new=tmp_path_factory.mktemp("neo4j") ) @@ -57,11 +60,15 @@ def setup_method(mocker, tmp_path_factory, repo: Repo): }, ) + yield + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + @pytest.fixture def repository(mocker, repo) -> ContentDTO: repository = ContentDTO( - path=Path(repo.path), packs=[], ) relationships = { @@ -303,7 +310,7 @@ def mock__create_content_dto(packs_to_update: List[str]) -> ContentDTO: return repository -def test_format_mapper_with_graph_remove_unknown_content(mocker, repository, repo): +def test_format_mapper_with_graph_remove_unknown_content(mocker, repository): """ Given - A mapper. @@ -327,7 +334,7 @@ def test_format_mapper_with_graph_remove_unknown_content(mocker, repository, rep "demisto_sdk.commands.format.format_module.update_content_graph", return_value=interface, ) - with ChangeCWD(repo.path): + with ChangeCWD(repository.path): runner = CliRunner() result = runner.invoke( main, @@ -358,7 +365,7 @@ def test_format_mapper_with_graph_remove_unknown_content(mocker, repository, rep ) -def test_format_layout_with_graph_remove_unknown_content(mocker, repository, repo): +def test_format_layout_with_graph_remove_unknown_content(mocker, repository): """ Given - A layout. @@ -382,7 +389,7 @@ def test_format_layout_with_graph_remove_unknown_content(mocker, repository, rep "demisto_sdk.commands.format.format_module.update_content_graph", return_value=interface, ) - with ChangeCWD(repo.path): + with ChangeCWD(repository.path): runner = CliRunner() result = runner.invoke( main, [FORMAT_CMD, "-i", layout_path, "-at", "-y", "-nv"] @@ -419,7 +426,7 @@ def test_format_layout_with_graph_remove_unknown_content(mocker, repository, rep def test_format_incident_field_graph_fix_aliases_marketplace( - mocker, monkeypatch, repository, repo + mocker, monkeypatch, repository ): """ Given @@ -451,7 +458,7 @@ def test_format_incident_field_graph_fix_aliases_marketplace( "demisto_sdk.commands.format.format_module.update_content_graph", return_value=interface, ) - with ChangeCWD(repo.path): + with ChangeCWD(repository.path): runner = CliRunner() result = runner.invoke( main, [FORMAT_CMD, "-i", original_incident_field_path, "-at", "-y", "-nv"] diff --git a/demisto_sdk/commands/content_graph/tests/generate_docs_script_test.py b/demisto_sdk/commands/content_graph/tests/generate_docs_script_test.py index 61ec5a4b33c..b680b43cfe7 100644 --- a/demisto_sdk/commands/content_graph/tests/generate_docs_script_test.py +++ b/demisto_sdk/commands/content_graph/tests/generate_docs_script_test.py @@ -35,14 +35,22 @@ @pytest.fixture(autouse=True) def setup_method(mocker, tmp_path_factory, repo: Repo): """Auto-used fixture for setup before every test run""" - import demisto_sdk.commands.content_graph.objects.base_content as bc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths + + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(Path(repo.path)) - bc.CONTENT_PATH = Path(repo.path) mocker.patch.object( neo4j_service, "NEO4J_DIR", new=tmp_path_factory.mktemp("neo4j") ) mocker.patch.object(ContentGraphInterface, "repo_path", Path(repo.path)) + yield + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + @pytest.fixture def repository(mocker, repo) -> ContentDTO: diff --git a/demisto_sdk/commands/content_graph/tests/graph_validator_test.py b/demisto_sdk/commands/content_graph/tests/graph_validator_test.py index 27bbb31fcbc..8d48383833c 100644 --- a/demisto_sdk/commands/content_graph/tests/graph_validator_test.py +++ b/demisto_sdk/commands/content_graph/tests/graph_validator_test.py @@ -1,3 +1,4 @@ +import shutil from pathlib import Path from typing import Any, Callable, Dict, List, Optional @@ -9,6 +10,7 @@ SKIP_PREPARE_SCRIPT_NAME, MarketplaceVersions, ) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.docker.docker_image import DockerImage from demisto_sdk.commands.common.hook_validations.graph_validator import GraphValidator from demisto_sdk.commands.common.legacy_git_tools import git_path @@ -39,10 +41,12 @@ @pytest.fixture(autouse=True) def setup_method(mocker, tmp_path_factory): """Auto-used fixture for setup before every test run""" - import demisto_sdk.commands.content_graph.objects.base_content as bc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.files.file import File - bc.CONTENT_PATH = GIT_PATH + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(GIT_PATH) mocker.patch.object( neo4j_service, "NEO4J_DIR", new=tmp_path_factory.mktemp("neo4j") ) @@ -61,11 +65,28 @@ def setup_method(mocker, tmp_path_factory): }, ) + yield + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + + +def mk_content_dir(repo_path: Path): + if repo_path.exists(): + shutil.rmtree(repo_path) + repo_path.mkdir() + @pytest.fixture def repository(mocker) -> ContentDTO: + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + repo_path = ContentPaths.CONTENT_PATH.with_name("content") + mk_content_dir(repo_path) + ContentPaths.update_content_path(repo_path) + repository = ContentDTO( - path=GIT_PATH, + path=repo_path, packs=[], ) relationships = { @@ -373,12 +394,13 @@ def repository(mocker) -> ContentDTO: "demisto_sdk.commands.content_graph.content_graph_builder.ContentGraphBuilder._create_content_dto", return_value=repository, ) - return repository - + yield repository -# HELPERS + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) +# HELPERS def mock_dependency(source: str, target: str, mandatory: bool = True) -> Dict[str, Any]: return { "source_id": source, diff --git a/demisto_sdk/commands/content_graph/tests/pack_metadata_graph_test.py b/demisto_sdk/commands/content_graph/tests/pack_metadata_graph_test.py index e61ef031ac8..3d896aa1b28 100644 --- a/demisto_sdk/commands/content_graph/tests/pack_metadata_graph_test.py +++ b/demisto_sdk/commands/content_graph/tests/pack_metadata_graph_test.py @@ -26,14 +26,20 @@ @pytest.fixture(autouse=True) def setup_method(mocker, tmp_path_factory, repo: Repo): """Auto-used fixture for setup before every test run""" - import demisto_sdk.commands.content_graph.objects.base_content as bc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths - bc.CONTENT_PATH = Path(repo.path) + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(Path(repo.path)) mocker.patch.object( neo4j_service, "NEO4J_DIR", new=tmp_path_factory.mktemp("neo4j") ) mocker.patch.object(ContentGraphInterface, "repo_path", Path(repo.path)) + yield + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + @pytest.fixture def repository(mocker): diff --git a/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py b/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py index f91e445b9e1..d370273fd02 100644 --- a/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py +++ b/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py @@ -28,10 +28,10 @@ from demisto_sdk.commands.content_graph.parsers.pack import PackParser from demisto_sdk.commands.content_graph.tests.test_tools import load_json, load_yaml from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_classifier_object, create_integration_object, create_pack_object, + get_temp_repo, ) from TestSuite.pack import Pack from TestSuite.repo import Repo @@ -284,7 +284,7 @@ def run( class TestParsersAndModels: - def test_classifier_parser_below_min_marketplace_version(self): + def test_classifier_parser_below_min_marketplace_version(self, git_repo): """ Given: - A pack with a classifier. @@ -298,8 +298,10 @@ def test_classifier_parser_below_min_marketplace_version(self): ClassifierParser, ) - with ChangeCWD(REPO.path): - classifier = create_classifier_object(paths=["toVersion"], values=["5.9.9"]) + with ChangeCWD(git_repo.path): + classifier = create_classifier_object( + paths=["toVersion"], values=["5.9.9"], repo=git_repo + ) classifier_path = Path(classifier.path) with pytest.raises(NotAContentItemException): ClassifierParser(classifier_path, list(MarketplaceVersions)) @@ -3057,11 +3059,14 @@ def test_support_attribute_in_integration_object( Then: - Ensure that the support attribute of the Integration object is set to the expected support level, e.g., the integration support level if it is not an empty string, or the pack support level otherwise. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + repo_path = repo.path + with ChangeCWD(repo_path): test_integration = create_integration_object( paths=["supportlevelheader"], values=[integration_support], pack_info={"support": pack_support}, + repo=repo, ) assert test_integration.support == expected_support @@ -3080,7 +3085,7 @@ def test_layout_parser_group(): """ from demisto_sdk.commands.content_graph.parsers.layout import LayoutParser - pack = REPO.create_pack("TestPack") + pack = get_temp_repo().create_pack("TestPack") layout = pack.create_layoutcontainer( "TestLayoutscontainer", content={ diff --git a/demisto_sdk/commands/content_graph/tests/prepare_content_graph_test.py b/demisto_sdk/commands/content_graph/tests/prepare_content_graph_test.py index 18406ee49b2..a12d0f04125 100644 --- a/demisto_sdk/commands/content_graph/tests/prepare_content_graph_test.py +++ b/demisto_sdk/commands/content_graph/tests/prepare_content_graph_test.py @@ -34,10 +34,13 @@ @pytest.fixture(autouse=True) def setup_method(mocker, tmp_path_factory, repo: Repo): """Auto-used fixture for setup before every test run""" - import demisto_sdk.commands.content_graph.objects.base_content as bc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.files.file import File - bc.CONTENT_PATH = Path(repo.path) + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(Path(repo.path)) + mocker.patch.object( neo4j_service, "NEO4J_DIR", new=tmp_path_factory.mktemp("neo4j") ) @@ -55,6 +58,11 @@ def setup_method(mocker, tmp_path_factory, repo: Repo): }, ) + yield + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + @pytest.fixture def repository(mocker): diff --git a/demisto_sdk/commands/content_graph/tests/update_content_graph_test.py b/demisto_sdk/commands/content_graph/tests/update_content_graph_test.py index fa74d8079fb..0c463a0f6b3 100644 --- a/demisto_sdk/commands/content_graph/tests/update_content_graph_test.py +++ b/demisto_sdk/commands/content_graph/tests/update_content_graph_test.py @@ -1,3 +1,4 @@ +import shutil from pathlib import Path from typing import Any, Callable, Dict, List from zipfile import ZipFile @@ -6,6 +7,7 @@ import demisto_sdk.commands.content_graph.neo4j_service as neo4j_service from demisto_sdk.commands.common.constants import MarketplaceVersions +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.legacy_git_tools import git_path from demisto_sdk.commands.content_graph.commands.create import ( create_content_graph, @@ -42,12 +44,13 @@ @pytest.fixture(autouse=True) def setup_method(mocker, tmp_path_factory): """Auto-used fixture for setup before every test run""" - import demisto_sdk.commands.content_graph.objects.base_content as bc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.files.file import File from_path.cache_clear() - - bc.CONTENT_PATH = GIT_PATH + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(GIT_PATH) mocker.patch.object( neo4j_service, "NEO4J_DIR", new=tmp_path_factory.mktemp("neo4j") ) @@ -65,11 +68,28 @@ def setup_method(mocker, tmp_path_factory): }, ) + yield + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + + +def mk_content_dir(repo_path: Path): + if repo_path.exists(): + shutil.rmtree(repo_path) + repo_path.mkdir() + @pytest.fixture def repository(mocker) -> ContentDTO: + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + repo_path = ContentPaths.CONTENT_PATH.with_name("content") + mk_content_dir(repo_path) + ContentPaths.update_content_path(repo_path) + repository = ContentDTO( - path=GIT_PATH, + path=repo_path, packs=[], ) relationships = { @@ -202,13 +222,22 @@ def mock__create_content_dto(packs_to_update: List[str]) -> ContentDTO: "demisto_sdk.commands.content_graph.content_graph_builder.ContentGraphBuilder._create_content_dto", side_effect=mock__create_content_dto, ) - return repository + yield repository + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) @pytest.fixture -def external_repository(mocker) -> ContentDTO: +def external_repository(mocker, tmp_path: Path) -> ContentDTO: + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + + content_temp_dir = tmp_path / "content" + content_temp_dir.mkdir() + ContentPaths.update_content_path(content_temp_dir) + repository = ContentDTO( - path=GIT_PATH, packs=[], ) @@ -238,7 +267,10 @@ def mock__create_content_dto(packs_to_update: List[str]) -> ContentDTO: "demisto_sdk.commands.content_graph.content_graph_builder.ContentGraphBuilder._create_content_dto", side_effect=mock__create_content_dto, ) - return repository + yield repository + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) # HELPERS diff --git a/demisto_sdk/commands/coverage_analyze/helpers.py b/demisto_sdk/commands/coverage_analyze/helpers.py index 6f4c7deecd6..2d20e8786d0 100644 --- a/demisto_sdk/commands/coverage_analyze/helpers.py +++ b/demisto_sdk/commands/coverage_analyze/helpers.py @@ -8,7 +8,7 @@ import coverage import requests -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.logger import logger @@ -89,7 +89,7 @@ def coverage_files() -> Iterable[str]: """ iterate over the '.coverage' files in the repo. """ - packs_path = CONTENT_PATH / "Packs" + packs_path = ContentPaths.CONTENT_PATH / "Packs" for cov_path in packs_path.glob("*/Integrations/*/.coverage"): yield str(cov_path) for cov_path in packs_path.glob("*/Scripts/*/.coverage"): diff --git a/demisto_sdk/commands/create_id_set/create_id_set.py b/demisto_sdk/commands/create_id_set/create_id_set.py index 6c7a9834fe4..68b28658037 100644 --- a/demisto_sdk/commands/create_id_set/create_id_set.py +++ b/demisto_sdk/commands/create_id_set/create_id_set.py @@ -8,11 +8,7 @@ GENERIC_COMMANDS_NAMES, MarketplaceVersions, ) -from demisto_sdk.commands.common.content_constant_paths import ( - DEFAULT_ID_SET_PATH, - MP_V2_ID_SET_PATH, - XPANSE_ID_SET_PATH, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.tools import open_id_set_file from demisto_sdk.commands.common.update_id_set import re_create_id_set @@ -109,11 +105,11 @@ def create_command_to_implemented_integration_map(self): def save_id_set(self): if not self.output: if self.marketplace == MarketplaceVersions.MarketplaceV2: - self.output = MP_V2_ID_SET_PATH + self.output = ContentPaths.MP_V2_ID_SET_PATH elif self.marketplace == MarketplaceVersions.XPANSE: - self.output = XPANSE_ID_SET_PATH + self.output = ContentPaths.XPANSE_ID_SET_PATH else: - self.output = DEFAULT_ID_SET_PATH + self.output = ContentPaths.DEFAULT_ID_SET_PATH if self.output: if not exists(self.output): intermediate_dirs = os.path.dirname(os.path.abspath(self.output)) diff --git a/demisto_sdk/commands/find_dependencies/find_dependencies.py b/demisto_sdk/commands/find_dependencies/find_dependencies.py index 52f0677d785..10cd699ded3 100644 --- a/demisto_sdk/commands/find_dependencies/find_dependencies.py +++ b/demisto_sdk/commands/find_dependencies/find_dependencies.py @@ -17,7 +17,7 @@ IGNORED_PACKS_IN_DEPENDENCY_CALC, PACKS_DIR, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import ( @@ -39,7 +39,7 @@ CORE_ALERT_FIELDS_PACK = "CoreAlertFields" # full path to Packs folder in content repo -PACKS_FULL_PATH = os.path.join(CONTENT_PATH, PACKS_DIR) # type: ignore +PACKS_FULL_PATH = os.path.join(ContentPaths.CONTENT_PATH, PACKS_DIR) # type: ignore def parse_for_pack_metadata( diff --git a/demisto_sdk/commands/format/update_generic_yml.py b/demisto_sdk/commands/format/update_generic_yml.py index 311a10c8304..5fca2c780cb 100644 --- a/demisto_sdk/commands/format/update_generic_yml.py +++ b/demisto_sdk/commands/format/update_generic_yml.py @@ -15,7 +15,7 @@ TEST_PLAYBOOKS_DIR, FileType, ) -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.handlers import DEFAULT_YAML_HANDLER as yaml from demisto_sdk.commands.common.logger import logger @@ -89,7 +89,7 @@ def _load_conf_file(self) -> Dict: Returns: The content of the json file """ - return get_file(CONF_PATH, raise_on_error=True) + return get_file(ContentPaths.CONF_PATH, raise_on_error=True) def get_id_and_version_path_object(self): """Gets the dict that holds the id and version fields. @@ -287,7 +287,7 @@ def remove_from_conf_json(self, file_type, content_item_id) -> None: conf_json_content = self._load_conf_file() except FileNotFoundError: logger.debug( - f"Unable to find {CONF_PATH} - skipping update." + f"Unable to find {ContentPaths.CONF_PATH} - skipping update." ) return conf_json_test_configuration = conf_json_content["tests"] @@ -319,7 +319,7 @@ def update_conf_json(self, file_type: str) -> None: conf_json_content = self._load_conf_file() except FileNotFoundError: logger.debug( - f"Unable to find {CONF_PATH} - skipping update." + f"Unable to find {ContentPaths.CONF_PATH} - skipping update." ) return conf_json_test_configuration = conf_json_content["tests"] @@ -354,7 +354,7 @@ def update_conf_json(self, file_type: str) -> None: def _save_to_conf_json(self, conf_json_content: Dict) -> None: """Save formatted JSON data to destination file.""" - with open(CONF_PATH, "w") as file: + with open(ContentPaths.CONF_PATH, "w") as file: json.dump(conf_json_content, file, indent=4) def update_deprecate(self, file_type=None): diff --git a/demisto_sdk/commands/init/contribution_converter.py b/demisto_sdk/commands/init/contribution_converter.py index 54e944eb4f5..2120628df03 100644 --- a/demisto_sdk/commands/init/contribution_converter.py +++ b/demisto_sdk/commands/init/contribution_converter.py @@ -34,7 +34,7 @@ ContentItems, FileType, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.handlers import YAML_Handler from demisto_sdk.commands.common.logger import logger @@ -168,7 +168,7 @@ def __init__( self.create_new = create_new self.contribution_items_version: Dict[str, Dict[str, str]] = {} self.contribution_items_version_note = "" - base_dir = base_dir or CONTENT_PATH # type: ignore + base_dir = base_dir or ContentPaths.CONTENT_PATH # type: ignore self.packs_dir_path: str = os.path.join(base_dir, "Packs") # type: ignore if not os.path.isdir(self.packs_dir_path): os.makedirs(self.packs_dir_path) diff --git a/demisto_sdk/commands/init/tests/contribution_converter_test.py b/demisto_sdk/commands/init/tests/contribution_converter_test.py index 484dfa479e9..88d8e612fda 100644 --- a/demisto_sdk/commands/init/tests/contribution_converter_test.py +++ b/demisto_sdk/commands/init/tests/contribution_converter_test.py @@ -276,7 +276,8 @@ def test_convert_contribution_zip_updated_pack(tmp_path, mocker): # Create fake content repo and contribution zip repo = Repo(repo_dir) mocker.patch( - "demisto_sdk.commands.init.contribution_converter.CONTENT_PATH", repo.path + "demisto_sdk.commands.init.contribution_converter.ContentPaths.CONTENT_PATH", + repo.path, ) pack = repo.create_pack("TestPack") integration = pack.create_integration("integration0") @@ -501,7 +502,8 @@ def test_convert_contribution_zip( contribution_zip_dir.mkdir() # Create fake content repo and contribution zip mocker.patch( - "demisto_sdk.commands.init.contribution_converter.CONTENT_PATH", git_repo.path + "demisto_sdk.commands.init.contribution_converter.ContentPaths.CONTENT_PATH", + git_repo.path, ) contrib_zip = Contribution(target_dir, "ContribTestPack", git_repo) @@ -633,7 +635,8 @@ def test_convert_contribution_zip_with_args( contribution_zip_dir.mkdir() mocker.patch( - "demisto_sdk.commands.init.contribution_converter.CONTENT_PATH", git_repo.path + "demisto_sdk.commands.init.contribution_converter.ContentPaths.CONTENT_PATH", + git_repo.path, ) contrib_zip = Contribution(target_dir, "ContribTestPack", git_repo) # contrib_zip.create_zip(contribution_zip_dir) @@ -1275,9 +1278,11 @@ def test_process_existing_pack_existing_integration_readme( - The integration README should be updated with the new command. """ - import demisto_sdk.commands.init.contribution_converter as cc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths - cc.CONTENT_PATH = git_repo.path + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(git_repo.path) # Read integration python, yml code and README to create mock integration py_code_path = Path(CONTRIBUTION_TESTS, "common", "integration.py") @@ -1387,6 +1392,11 @@ def test_process_existing_pack_existing_integration_readme( assert actual_integration_python.read_text() != py_code assert "helloworld-new-cmd" in actual_integration_python.read_text() + yield + + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + def test_process_existing_pack_new_integration_readme( self, tmp_path: Path, git_repo: Repo, mocker: MockerFixture ): @@ -1407,9 +1417,11 @@ def test_process_existing_pack_new_integration_readme( - A new Integration README should be generated. """ - import demisto_sdk.commands.init.contribution_converter as cc + from demisto_sdk.commands.common.content_constant_paths import ContentPaths - cc.CONTENT_PATH = git_repo.path + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + ContentPaths.update_content_path(git_repo.path) git_repo.create_pack(self.existing_pack_name) @@ -1442,6 +1454,9 @@ def test_process_existing_pack_new_integration_readme( generated_readme = Path(contrib_converter.readme_files[0]) assert not generated_readme.exists() + # Restore the original content path after the test has terminated. + ContentPaths.update_content_path(old_content_path) + def test_process_new_pack( self, tmp_path: Path, git_repo: Repo, mocker: MockerFixture ): diff --git a/demisto_sdk/commands/lint/lint_manager.py b/demisto_sdk/commands/lint/lint_manager.py index 31b2ac34755..1e747d2c677 100644 --- a/demisto_sdk/commands/lint/lint_manager.py +++ b/demisto_sdk/commands/lint/lint_manager.py @@ -22,7 +22,7 @@ TYPE_PYTHON, DemistoException, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.docker_helper import init_global_docker_client from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json @@ -1351,7 +1351,7 @@ def vulture_error_formatter(self, errors: Dict, json_contents: List) -> None: """ error_messages = errors.get("messages", "") error_messages = error_messages.split("\n") if error_messages else [] - content_path = CONTENT_PATH + content_path = ContentPaths.CONTENT_PATH for message in error_messages: if message: file_name, line_number, error_contents = message.split(":", 2) diff --git a/demisto_sdk/commands/pre_commit/hooks/docker.py b/demisto_sdk/commands/pre_commit/hooks/docker.py index 67d3cc4d2b8..971ff2cfae3 100644 --- a/demisto_sdk/commands/pre_commit/hooks/docker.py +++ b/demisto_sdk/commands/pre_commit/hooks/docker.py @@ -19,7 +19,7 @@ TYPE_PWSH, TYPE_PYTHON, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH, PYTHONPATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.docker_helper import ( DockerBase, @@ -59,7 +59,7 @@ def get_mypy_requirements(): Raises: RuntimeError: If the requirements cannot be read from GitHub. """ - mypy_requirements_path = Path(f"{CONTENT_PATH}/mypy-requirements.txt") + mypy_requirements_path = Path(f"{ContentPaths.CONTENT_PATH}/mypy-requirements.txt") if mypy_requirements_path.exists(): requirements = get_pip_requirements_from_file(mypy_requirements_path) else: @@ -88,8 +88,10 @@ def get_docker_python_path(drop_site_packages: bool = False) -> str: This means CommonServerPython's path is /src/Packs/Base/...CSP.py Returns: A PYTHONPATH formatted string """ - path_to_replace = str(Path(CONTENT_PATH).absolute()) - docker_path = [str(path).replace(path_to_replace, "/src") for path in PYTHONPATH] + path_to_replace = str(Path(ContentPaths.CONTENT_PATH).absolute()) + docker_path = [ + str(path).replace(path_to_replace, "/src") for path in ContentPaths.PYTHONPATH + ] if drop_site_packages: docker_path = [p for p in docker_path if "site-packages" not in p] path = ":".join(docker_path) @@ -361,11 +363,12 @@ def prepare_hook( all_objects = {obj for _, obj in filtered_files_with_objects if obj} for obj in all_objects: for file in copy_files: - source: Path = CONTENT_PATH / file + source: Path = ContentPaths.CONTENT_PATH / file target = obj.path.parent / Path(file).name if source != target and source.exists() and not target.exists(): shutil.copy( - CONTENT_PATH / file, obj.path.parent / Path(file).name + ContentPaths.CONTENT_PATH / file, + obj.path.parent / Path(file).name, ) run_isolated = self._get_property("run_isolated", False) config_arg = self._get_config_file_arg() @@ -464,7 +467,7 @@ def generate_hooks( Path("/src") / ( integration_script.path.parent / config_arg[1] - ).relative_to(CONTENT_PATH) + ).relative_to(ContentPaths.CONTENT_PATH) ), ] ) @@ -477,7 +480,7 @@ def generate_hooks( ) # change the working directory to the integration script, as it runs in an isolated container hook["entry"] = ( - f"-w {Path('/src') / integration_script.path.parent.relative_to(CONTENT_PATH)} {hook['entry']}" + f"-w {Path('/src') / integration_script.path.parent.relative_to(ContentPaths.CONTENT_PATH)} {hook['entry']}" ) if self._set_files_on_hook( diff --git a/demisto_sdk/commands/pre_commit/hooks/hook.py b/demisto_sdk/commands/pre_commit/hooks/hook.py index 4472ce6db6b..3283f56d138 100644 --- a/demisto_sdk/commands/pre_commit/hooks/hook.py +++ b/demisto_sdk/commands/pre_commit/hooks/hook.py @@ -7,7 +7,7 @@ from packaging.version import Version -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.pre_commit.hooks.utils import get_property from demisto_sdk.commands.pre_commit.pre_commit_context import PreCommitContext @@ -70,7 +70,7 @@ def _set_files_on_hook( files: Iterable[Path], should_filter: bool = True, use_args: bool = False, - base_path: Path = CONTENT_PATH, + base_path: Path = ContentPaths.CONTENT_PATH, ) -> int: """ @@ -210,7 +210,9 @@ def join_files(files: Set[Path], separator: str = "|") -> str: str: The joined string. """ return separator.join( - str(file) if (CONTENT_PATH / file).is_file() else f"{str(file)}{os.sep}" + str(file) + if (ContentPaths.CONTENT_PATH / file).is_file() + else f"{str(file)}{os.sep}" for file in files ) diff --git a/demisto_sdk/commands/pre_commit/hooks/pycln.py b/demisto_sdk/commands/pre_commit/hooks/pycln.py index 8533fcf6e8c..c59bf6220e3 100644 --- a/demisto_sdk/commands/pre_commit/hooks/pycln.py +++ b/demisto_sdk/commands/pre_commit/hooks/pycln.py @@ -1,4 +1,4 @@ -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH, PYTHONPATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.pre_commit.hooks.hook import ( GeneratedHooks, Hook, @@ -18,8 +18,8 @@ def prepare_hook(self) -> GeneratedHooks: """ paths_to_skip = tuple( path.name - for path in PYTHONPATH - if path.absolute() != CONTENT_PATH.absolute() + for path in ContentPaths.PYTHONPATH + if path.absolute() != ContentPaths.CONTENT_PATH.absolute() ) builtins_to_skip = ("demisto", "CommonServerUserPython") diff --git a/demisto_sdk/commands/pre_commit/hooks/sourcery.py b/demisto_sdk/commands/pre_commit/hooks/sourcery.py index 1ae798ce45d..9d0f0f89f46 100644 --- a/demisto_sdk/commands/pre_commit/hooks/sourcery.py +++ b/demisto_sdk/commands/pre_commit/hooks/sourcery.py @@ -4,7 +4,7 @@ from typing import Any, Dict from demisto_sdk.commands.common import tools -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.pre_commit.hooks.hook import GeneratedHooks, Hook, join_files @@ -32,7 +32,9 @@ def prepare_hook(self) -> GeneratedHooks: Returns: None """ - config_file = CONTENT_PATH / self._get_property("config_file", ".sourcery.yml") + config_file = ContentPaths.CONTENT_PATH / self._get_property( + "config_file", ".sourcery.yml" + ) if not config_file.exists(): return [] self.base_hook.pop("config_file", None) diff --git a/demisto_sdk/commands/pre_commit/pre_commit_command.py b/demisto_sdk/commands/pre_commit/pre_commit_command.py index 09c11066cc7..56da5004945 100644 --- a/demisto_sdk/commands/pre_commit/pre_commit_command.py +++ b/demisto_sdk/commands/pre_commit/pre_commit_command.py @@ -19,7 +19,7 @@ PACKS_FOLDER, SCRIPTS_DIR, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH, PYTHONPATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.common.logger import logger @@ -184,7 +184,7 @@ def _run_pre_commit_process( ) ), env=precommit_env, - cwd=CONTENT_PATH, + cwd=ContentPaths.CONTENT_PATH, stdout=stdout, stderr=stdout, universal_newlines=True, @@ -301,12 +301,16 @@ def prepare_and_run( ret_val = 0 pre_commit_context.dry_run = dry_run precommit_env = os.environ.copy() - precommit_env["PYTHONPATH"] = ":".join(str(path) for path in PYTHONPATH) + precommit_env["PYTHONPATH"] = ":".join( + str(path) for path in ContentPaths.PYTHONPATH + ) # The PYTHONPATH should be the same as the PYTHONPATH, but without the site-packages because MYPY does not support it precommit_env["MYPYPATH"] = ":".join( - str(path) for path in sorted(PYTHONPATH) if "site-packages" not in str(path) + str(path) + for path in sorted(ContentPaths.PYTHONPATH) + if "site-packages" not in str(path) ) - precommit_env["DEMISTO_SDK_CONTENT_PATH"] = str(CONTENT_PATH) + precommit_env["DEMISTO_SDK_CONTENT_PATH"] = str(ContentPaths.CONTENT_PATH) precommit_env["SYSTEMD_COLORS"] = "1" # for colorful output precommit_env["PRE_COMMIT_COLOR"] = "always" @@ -384,7 +388,7 @@ def group_by_language( ) if not find_path_index: raise Exception(f"Could not find Integrations/Scripts path for {file}") - code_file_path = CONTENT_PATH / Path( + code_file_path = ContentPaths.CONTENT_PATH / Path( *file.parts[: next(find_path_index) + 1] ) if not code_file_path.is_dir(): @@ -430,12 +434,12 @@ def group_by_language( add_related_files( api_module.path if not api_module.path.is_absolute() - else api_module.path.relative_to(CONTENT_PATH) + else api_module.path.relative_to(ContentPaths.CONTENT_PATH) ) | add_related_files( imported_by.path if not imported_by.path.is_absolute() - else imported_by.path.relative_to(CONTENT_PATH) + else imported_by.path.relative_to(ContentPaths.CONTENT_PATH) ) ) logger.debug("Pre-Commit: Finished handling API Modules") @@ -448,11 +452,13 @@ def group_by_language( # the reason we maintain this set is for performance when running with --all-files. It is much faster to exclude. if integration_script.is_unified: exclude_integration_script.add( - integration_script.path.relative_to(CONTENT_PATH) + integration_script.path.relative_to(ContentPaths.CONTENT_PATH) ) else: exclude_integration_script.add( - integration_script.path.parent.relative_to(CONTENT_PATH) + integration_script.path.parent.relative_to( + ContentPaths.CONTENT_PATH + ) ) continue @@ -469,7 +475,7 @@ def group_by_language( }, { ( - integration_script.path.relative_to(CONTENT_PATH) + integration_script.path.relative_to(ContentPaths.CONTENT_PATH) if integration_script.path.is_absolute() else integration_script.path, integration_script, @@ -533,7 +539,7 @@ def pre_commit_manager( int: Return code of pre-commit. """ # We have imports to this module, however it does not exists in the repo. - (CONTENT_PATH / "CommonServerUserPython.py").touch() + (ContentPaths.CONTENT_PATH / "CommonServerUserPython.py").touch() if not any((input_files, staged_only, git_diff, all_files)): logger.info("No arguments were given, running on staged files and git changes.") @@ -690,7 +696,7 @@ def preprocess_files( files_to_run.update(add_related_files(file)) # convert to relative file to content path relative_paths = { - file.relative_to(CONTENT_PATH) if file.is_absolute() else file + file.relative_to(ContentPaths.CONTENT_PATH) if file.is_absolute() else file for file in files_to_run } # filter out files that are not in the content git repo (e.g in .gitignore) diff --git a/demisto_sdk/commands/pre_commit/pre_commit_context.py b/demisto_sdk/commands/pre_commit/pre_commit_context.py index f28555bbe2f..4da8455898d 100644 --- a/demisto_sdk/commands/pre_commit/pre_commit_context.py +++ b/demisto_sdk/commands/pre_commit/pre_commit_context.py @@ -8,7 +8,7 @@ from typing import Dict, List, Optional, Set, Tuple from demisto_sdk.commands.common.constants import CACHE_DIR -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import ( get_file_or_remote, @@ -23,7 +23,7 @@ IS_GITHUB_ACTIONS = string_to_bool(os.getenv("GITHUB_ACTIONS"), False) PRECOMMIT_TEMPLATE_NAME = ".pre-commit-config_template.yaml" -PRECOMMIT_TEMPLATE_PATH = CONTENT_PATH / PRECOMMIT_TEMPLATE_NAME +PRECOMMIT_TEMPLATE_PATH = ContentPaths.CONTENT_PATH / PRECOMMIT_TEMPLATE_NAME PATH = Path(__file__).parents[0].resolve() DEFAULT_PRE_COMMIT_TEMPLATE_PATH = PATH / PRECOMMIT_TEMPLATE_NAME @@ -34,7 +34,7 @@ HOOK_LOG_PATH = Path(ARTIFACTS_FOLDER) / "pre-commit" if ARTIFACTS_FOLDER else None # This has to be relative to content path so the docker will be able to write to it -PRE_COMMIT_FOLDER_SHARED = CONTENT_PATH / ".pre-commit" +PRE_COMMIT_FOLDER_SHARED = ContentPaths.CONTENT_PATH / ".pre-commit" @dataclass diff --git a/demisto_sdk/commands/pre_commit/tests/pre_commit_test.py b/demisto_sdk/commands/pre_commit/tests/pre_commit_test.py index cf568f4d4dc..ec0bb70fc34 100644 --- a/demisto_sdk/commands/pre_commit/tests/pre_commit_test.py +++ b/demisto_sdk/commands/pre_commit/tests/pre_commit_test.py @@ -140,7 +140,9 @@ def devtest_side_effect( TEST_DATA_PATH / ".pre-commit-config_template-test.yaml", ) pack1 = repo.create_pack("Pack1") - mocker.patch.object(pre_commit_command, "CONTENT_PATH", Path(repo.path)) + mocker.patch.object( + pre_commit_command.ContentPaths, "CONTENT_PATH", Path(repo.path) + ) mocker.patch.object( pre_commit_command, @@ -239,7 +241,9 @@ def test_handle_api_modules(mocker, git_repo: Repo): integration = pack2.create_integration( "integration1", code="from TestApiModule import *" ) - mocker.patch.object(pre_commit_command, "CONTENT_PATH", Path(git_repo.path)) + mocker.patch.object( + pre_commit_command.ContentPaths, "CONTENT_PATH", Path(git_repo.path) + ) with ChangeCWD(git_repo.path): git_repo.create_graph() files_to_run = group_by_language( @@ -386,7 +390,9 @@ def test_preprocess_files_with_input_yml_files(self, mocker, repo): - Check that the associated python file was gathered correctly. """ pack1 = repo.create_pack("Pack1") - mocker.patch.object(pre_commit_command, "CONTENT_PATH", Path(repo.path)) + mocker.patch.object( + pre_commit_command.ContentPaths, "CONTENT_PATH", Path(repo.path) + ) integration = pack1.create_integration("integration") relative_paths = { @@ -572,7 +578,7 @@ def test_exclude_hooks_by_version(mocker, repo: Repo): "PRECOMMIT_TEMPLATE_PATH", TEST_DATA_PATH / ".pre-commit-config_template-test.yaml", ) - mocker.patch.object(context, "CONTENT_PATH", Path(repo.path)) + mocker.patch.object(context.ContentPaths, "CONTENT_PATH", Path(repo.path)) mocker.patch.object(pre_commit_command, "logger") python_version_to_files = { "2.7": {(Path("file1.py"), None)}, @@ -609,7 +615,7 @@ def test_exclude_hooks_by_support_level(mocker, repo: Repo): "PRECOMMIT_TEMPLATE_PATH", TEST_DATA_PATH / ".pre-commit-config_template-test.yaml", ) - mocker.patch.object(context, "CONTENT_PATH", Path(repo.path)) + mocker.patch.object(context.ContentPaths, "CONTENT_PATH", Path(repo.path)) mocker.patch.object(pre_commit_command, "logger") python_version_to_files = { "2.7": {(Path("file1.py"), Obj())}, diff --git a/demisto_sdk/commands/setup_env/setup_environment.py b/demisto_sdk/commands/setup_env/setup_environment.py index 2bb5ab9ab24..37641b97fec 100644 --- a/demisto_sdk/commands/setup_env/setup_environment.py +++ b/demisto_sdk/commands/setup_env/setup_environment.py @@ -20,12 +20,7 @@ get_client_from_server_type, ) from demisto_sdk.commands.common.constants import DEF_DOCKER -from demisto_sdk.commands.common.content_constant_paths import ( - COMMON_SERVER_PYTHON_PATH, - CONTENT_PATH, - PYTHONPATH, - PYTHONPATH_STR, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.docker.docker_image import DockerImage from demisto_sdk.commands.common.files import FileReadError, TextFile from demisto_sdk.commands.common.handlers import DEFAULT_JSON5_HANDLER as json5 @@ -43,7 +38,7 @@ from demisto_sdk.commands.content_graph.objects.pack import Pack BACKUP_FILES_SUFFIX = ".demisto_sdk_backup" -DOTENV_PATH = CONTENT_PATH / ".env" +DOTENV_PATH = ContentPaths.CONTENT_PATH / ".env" class IDEType(Enum): @@ -69,14 +64,14 @@ def configure_vscode_settings( settings["python.defaultInterpreterPath"] = str(interpreter_path) if integration_script: if devcontainer: - testing_path = f"workspaces/content/{integration_script.path.parent.relative_to(CONTENT_PATH)}" + testing_path = f"workspaces/content/{integration_script.path.parent.relative_to(ContentPaths.CONTENT_PATH)}" else: testing_path = str(integration_script.path.parent) settings["python.testing.cwd"] = testing_path if devcontainer: python_path = get_docker_python_path("/workspaces/content") else: - python_path = [str(path) for path in PYTHONPATH if str(path)] + python_path = [str(path) for path in ContentPaths.PYTHONPATH if str(path)] settings["python.analysis.extraPaths"] = [ path for path in python_path if "site-packages" not in path @@ -87,11 +82,11 @@ def configure_vscode_settings( def get_docker_python_path(docker_prefix: str) -> List[str]: docker_python_path = [] - for path in PYTHONPATH: + for path in ContentPaths.PYTHONPATH: with contextlib.suppress(ValueError): # we can't add paths which is not relative to CONTENT_PATH, and `is_relative_to is not working on python3.8` docker_python_path.append( - f"{docker_prefix}/{path.relative_to(CONTENT_PATH.absolute())}" + f"{docker_prefix}/{path.relative_to(ContentPaths.CONTENT_PATH.absolute())}" ) if ( @@ -100,22 +95,25 @@ def get_docker_python_path(docker_prefix: str) -> List[str]: ): try: common_server_python_contnet = TextFile.read_from_github_api( - str(COMMON_SERVER_PYTHON_PATH / "CommonServerUserPython.py"), + str( + ContentPaths.COMMON_SERVER_PYTHON_PATH / "CommonServerUserPython.py" + ), verify_ssl=False, ) except (FileReadError, ConnectionError, Timeout): logger.error( - f"Could not retrieve common server python content from content github from path {COMMON_SERVER_PYTHON_PATH}/CommonServerUserPython.py" + f"Could not retrieve common server python content from content github from path {ContentPaths.COMMON_SERVER_PYTHON_PATH}/CommonServerUserPython.py" ) raise RuntimeError( "Could not set debug-in-docker on VSCode. Either CONTENT_PATH is not set properly or CommonServerPython could not be read" ) - Path(COMMON_SERVER_PYTHON_PATH).mkdir(parents=True, exist_ok=True) + Path(ContentPaths.COMMON_SERVER_PYTHON_PATH).mkdir(parents=True, exist_ok=True) TextFile.write( common_server_python_contnet, - output_path=COMMON_SERVER_PYTHON_PATH / "CommonServerUserPython.py", + output_path=ContentPaths.COMMON_SERVER_PYTHON_PATH + / "CommonServerUserPython.py", ) docker_python_path.append( @@ -243,7 +241,7 @@ def update_pycharm_config_xml_data( for python_path in python_discovery_paths: try: - python_path_relative = python_path.relative_to(CONTENT_PATH) + python_path_relative = python_path.relative_to(ContentPaths.CONTENT_PATH) except ValueError: # Skip paths that are not within the project root logger.debug( @@ -275,23 +273,27 @@ def configure_module_discovery(ide_type: IDEType): ide_type (IDEType): The IDE type to configure """ if ide_type == IDEType.VSCODE: - ide_folder = CONTENT_PATH / ".vscode" + ide_folder = ContentPaths.CONTENT_PATH / ".vscode" ide_folder.mkdir(exist_ok=True, parents=True) configure_vscode_settings(ide_folder=ide_folder) - env_file = CONTENT_PATH / ".env" + env_file = ContentPaths.CONTENT_PATH / ".env" env_vars = dotenv.dotenv_values(env_file) - env_vars["PYTHONPATH"] = PYTHONPATH_STR + env_vars["PYTHONPATH"] = ContentPaths.PYTHONPATH_STR update_dotenv(DOTENV_PATH, env_vars) if ide_type == IDEType.PYCHARM: - python_discovery_paths = PYTHONPATH.copy() + python_discovery_paths = ContentPaths.PYTHONPATH.copy() # Remove 'CONTENT_PATH' from the python discovery paths as it is already configured by default, # and all the configured paths are relative to the project root (which is 'CONTENT_PATH'). - if CONTENT_PATH in python_discovery_paths: - python_discovery_paths.remove(CONTENT_PATH) + if ContentPaths.CONTENT_PATH in python_discovery_paths: + python_discovery_paths.remove(ContentPaths.CONTENT_PATH) - config_file_path = CONTENT_PATH / ".idea" / f"{CONTENT_PATH.name.lower()}.iml" + config_file_path = ( + ContentPaths.CONTENT_PATH + / ".idea" + / f"{ContentPaths.CONTENT_PATH.name.lower()}.iml" + ) update_pycharm_config_file( file_path=config_file_path, python_discovery_paths=python_discovery_paths, @@ -317,12 +319,12 @@ def build_tasks(): "image": integration_script.docker_image, "volumes": [ { - "localPath": str(CONTENT_PATH), - "containerPath": str(CONTENT_PATH), + "localPath": str(ContentPaths.CONTENT_PATH), + "containerPath": str(ContentPaths.CONTENT_PATH), } ], "env": { - "PYTHONPATH": PYTHONPATH_STR, + "PYTHONPATH": ContentPaths.PYTHONPATH_STR, }, }, }, @@ -346,12 +348,12 @@ def build_tasks(): "image": test_docker_image, "volumes": [ { - "localPath": str(CONTENT_PATH), - "containerPath": str(CONTENT_PATH), + "localPath": str(ContentPaths.CONTENT_PATH), + "containerPath": str(ContentPaths.CONTENT_PATH), } ], "customOptions": f"-w {integration_script.path.parent}", - "env": {"PYTHONPATH": PYTHONPATH_STR}, + "env": {"PYTHONPATH": ContentPaths.PYTHONPATH_STR}, }, }, ], @@ -374,7 +376,7 @@ def build_launch(): "type": "PowerShell", "request": "launch", "script": ( - f"/workspaces/content/{integration_script.path.relative_to(CONTENT_PATH)}" + f"/workspaces/content/{integration_script.path.relative_to(ContentPaths.CONTENT_PATH)}" if devcontainer else str(integration_script.path.with_suffix(".ps1")) ), @@ -394,8 +396,8 @@ def build_launch(): "python": { "pathMappings": [ { - "localRoot": str(CONTENT_PATH), - "remoteRoot": str(CONTENT_PATH), + "localRoot": str(ContentPaths.CONTENT_PATH), + "remoteRoot": str(ContentPaths.CONTENT_PATH), } ], "projectType": "general", @@ -410,8 +412,8 @@ def build_launch(): "python": { "pathMappings": [ { - "localRoot": str(CONTENT_PATH), - "remoteRoot": str(CONTENT_PATH), + "localRoot": str(ContentPaths.CONTENT_PATH), + "remoteRoot": str(ContentPaths.CONTENT_PATH), }, ], "projectType": "general", @@ -423,14 +425,14 @@ def build_launch(): "type": "debugpy", "request": "launch", "program": ( - f"/workspaces/content/{integration_script.path.relative_to(CONTENT_PATH)}" + f"/workspaces/content/{integration_script.path.relative_to(ContentPaths.CONTENT_PATH)}" if devcontainer else str(integration_script.path.with_suffix(".py")) ), "console": "integratedTerminal", "cwd": "${workspaceFolder}", "justMyCode": False, - "env": {"PYTHONPATH": PYTHONPATH_STR}, + "env": {"PYTHONPATH": ContentPaths.PYTHONPATH_STR}, }, { "name": "Python: Debug Tests", @@ -440,7 +442,7 @@ def build_launch(): "purpose": ["debug-test"], "console": "integratedTerminal", "justMyCode": False, - "env": {"PYTHONPATH": PYTHONPATH_STR}, + "env": {"PYTHONPATH": ContentPaths.PYTHONPATH_STR}, }, ], } @@ -694,7 +696,9 @@ def upload_and_create_instance( def add_demistomock_and_commonserveruser(integration_script: IntegrationScript): - source_demisto_mock_path = CONTENT_PATH / "Tests" / "demistomock" / "demistomock.py" + source_demisto_mock_path = ( + ContentPaths.CONTENT_PATH / "Tests" / "demistomock" / "demistomock.py" + ) target_demisto_mock_path = integration_script.path.parent / "demistomock.py" if source_demisto_mock_path.exists(): shutil.copy( @@ -738,7 +742,7 @@ def configure_integration( RuntimeError: If using auto-detection for IDE and it failed, or if the configuration failed (for instance, Docker is turned off) """ - base_path = CONTENT_PATH + base_path = ContentPaths.CONTENT_PATH integration_script = BaseContent.from_path(Path(file_path)) assert isinstance( @@ -746,7 +750,7 @@ def configure_integration( ), "Expected Integration Script" add_demistomock_and_commonserveruser(integration_script) docker_image: Union[str, DockerImage] = integration_script.docker_image - interpreter_path = CONTENT_PATH / ".venv" / "bin" / "python" + interpreter_path = ContentPaths.CONTENT_PATH / ".venv" / "bin" / "python" configure_params(integration_script, secret_id, instance_name, test_module) if not docker_image: docker_image = DEF_DOCKER @@ -796,13 +800,13 @@ def clean_repo(): """ Clean the repository from temporary files like 'CommonServerPython' and API modules created by the 'lint' command. """ - for path in PYTHONPATH: - for temp_file in CONTENT_PATH.rglob(f"{path.name}.py"): + for path in ContentPaths.PYTHONPATH: + for temp_file in ContentPaths.CONTENT_PATH.rglob(f"{path.name}.py"): if temp_file.parent != path and ".venv" not in temp_file.parts: temp_file.unlink(missing_ok=True) - for path in CONTENT_PATH.rglob("*.pyc"): + for path in ContentPaths.CONTENT_PATH.rglob("*.pyc"): path.unlink(missing_ok=True) - for path in CONTENT_PATH.rglob("test_data/__init__.py"): + for path in ContentPaths.CONTENT_PATH.rglob("test_data/__init__.py"): path.unlink(missing_ok=True) @@ -837,9 +841,9 @@ def setup_env( configure_module_discovery(ide_type=ide_type) if not file_paths: - (CONTENT_PATH / "CommonServerUserPython.py").touch() + (ContentPaths.CONTENT_PATH / "CommonServerUserPython.py").touch() if ide_type == IDEType.VSCODE: - ide_folder = CONTENT_PATH / ".vscode" + ide_folder = ContentPaths.CONTENT_PATH / ".vscode" ide_folder.mkdir(exist_ok=True) configure_vscode_settings(ide_folder=ide_folder) for file_path in file_paths: diff --git a/demisto_sdk/commands/setup_env/tests/setup_environment_test.py b/demisto_sdk/commands/setup_env/tests/setup_environment_test.py index 1e318e80c3e..0371ffcd073 100644 --- a/demisto_sdk/commands/setup_env/tests/setup_environment_test.py +++ b/demisto_sdk/commands/setup_env/tests/setup_environment_test.py @@ -35,18 +35,18 @@ def test_setup_env_vscode(mocker, monkeypatch, pack, create_virtualenv): test_image = "test/python3-sha" params = {"username": "user", "password": "pass"} repo_path = Path(pack.repo_path) - mocker.patch.object(setup_environment, "CONTENT_PATH", repo_path) + mocker.patch.object(setup_environment.ContentPaths, "CONTENT_PATH", repo_path) mocker.patch.object(setup_environment, "DOTENV_PATH", repo_path / ".env") mocker.patch.object( - setup_environment, + setup_environment.ContentPaths, "PYTHONPATH", [repo_path / "Packs/Base/Scripts/CommonServerPython"], ) mocker.patch.object(setup_environment, "add_demistomock_and_commonserveruser") - mocker.patch.object(content_item, "CONTENT_PATH", repo_path) + mocker.patch.object(content_item.ContentPaths, "CONTENT_PATH", repo_path) mocker.patch.object( docker_helper.DockerBase, "get_or_create_test_image", diff --git a/demisto_sdk/commands/test_content/test_modeling_rule/test_modeling_rule.py b/demisto_sdk/commands/test_content/test_modeling_rule/test_modeling_rule.py index 1e4dc215ada..a7c8fc6f556 100644 --- a/demisto_sdk/commands/test_content/test_modeling_rule/test_modeling_rule.py +++ b/demisto_sdk/commands/test_content/test_modeling_rule/test_modeling_rule.py @@ -34,7 +34,7 @@ ModelingRule, SingleModelingRule, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.logger import ( handle_deprecated_args, @@ -142,8 +142,12 @@ def get_relative_path_to_content(path: Path) -> str: Returns: Path: The relative path to the content directory. """ - if path.is_absolute() and path.as_posix().startswith(CONTENT_PATH.as_posix()): - return path.as_posix().replace(f"{CONTENT_PATH.as_posix()}{os.path.sep}", "") + if path.is_absolute() and path.as_posix().startswith( + ContentPaths.CONTENT_PATH.as_posix() + ): + return path.as_posix().replace( + f"{ContentPaths.CONTENT_PATH.as_posix()}{os.path.sep}", "" + ) return path.as_posix() diff --git a/demisto_sdk/commands/test_content/test_modeling_rule/tests/test_modeling_rule_test.py b/demisto_sdk/commands/test_content/test_modeling_rule/tests/test_modeling_rule_test.py index 780aba20fac..82b8f87df56 100644 --- a/demisto_sdk/commands/test_content/test_modeling_rule/tests/test_modeling_rule_test.py +++ b/demisto_sdk/commands/test_content/test_modeling_rule/tests/test_modeling_rule_test.py @@ -6,13 +6,13 @@ import typer from freezegun import freeze_time -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths DEFAULT_TEST_EVENT_ID = UUID("00000000-0000-0000-0000-000000000000") class ModelingRuleMock: - path = Path(CONTENT_PATH) + path = Path(ContentPaths.CONTENT_PATH) def normalize_file_name(self): return "test_modeling_rule.yml" diff --git a/demisto_sdk/commands/upload/tests/uploader_test.py b/demisto_sdk/commands/upload/tests/uploader_test.py index 4c066388ef6..12aeccd8857 100644 --- a/demisto_sdk/commands/upload/tests/uploader_test.py +++ b/demisto_sdk/commands/upload/tests/uploader_test.py @@ -133,7 +133,7 @@ def test_upload_folder( "upload", ) content_path = f"{git_path()}/demisto_sdk/tests/test_files/" - mocker.patch.object(content_item, "CONTENT_PATH", Path(content_path)) + mocker.patch.object(content_item.ContentPaths, "CONTENT_PATH", Path(content_path)) mocker.patch.object(PackParser, "parse_ignored_errors", return_value={}) path = Path(content_path, path_end) @@ -609,7 +609,7 @@ def test_print_summary_successfully_uploaded_files( import demisto_sdk.commands.content_graph.objects.content_item as content_item mocker.patch.object( - content_item, + content_item.ContentPaths, "CONTENT_PATH", Path(f"{git_path()}/demisto_sdk/tests/test_files"), ) @@ -621,20 +621,18 @@ def test_print_summary_successfully_uploaded_files( uploader.print_summary() assert "UPLOAD SUMMARY:\n" in caplog.text - assert ( - "\n".join( - ( - "SUCCESSFUL UPLOADS:", - "╒═════════════════╤════════╤═════════════╤════════════════╕", - "│ NAME │ TYPE │ PACK NAME │ PACK VERSION │", - "╞═════════════════╪════════╪═════════════╪════════════════╡", - "│ DummyScript.yml │ Script │ DummyPack │ 1.0.0 │", - "╘═════════════════╧════════╧═════════════╧════════════════╛", - "", - ) + res = "\n".join( + ( + "SUCCESSFUL UPLOADS:", + "╒═════════════════╤════════╤═════════════╤════════════════╕", + "│ NAME │ TYPE │ PACK NAME │ PACK VERSION │", + "╞═════════════════╪════════╪═════════════╪════════════════╡", + "│ DummyScript.yml │ Script │ DummyPack │ 1.0.0 │", + "╘═════════════════╧════════╧═════════════╧════════════════╛", + "", ) - in caplog.text ) + assert res in caplog.text def test_print_summary_failed_uploaded(self, caplog, mocker): """ diff --git a/demisto_sdk/commands/validate/old_validate_manager.py b/demisto_sdk/commands/validate/old_validate_manager.py index b117b216914..2807a02d972 100644 --- a/demisto_sdk/commands/validate/old_validate_manager.py +++ b/demisto_sdk/commands/validate/old_validate_manager.py @@ -34,10 +34,7 @@ PathLevel, ) from demisto_sdk.commands.common.content import Content -from demisto_sdk.commands.common.content_constant_paths import ( - CONTENT_PATH, - DEFAULT_ID_SET_PATH, -) +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.errors import ( FOUND_FILES_AND_ERRORS, @@ -255,7 +252,7 @@ def __init__( specific_validations=specific_validations ).should_run_validation self.file_path = file_path - self.id_set_path = id_set_path or DEFAULT_ID_SET_PATH + self.id_set_path = id_set_path or ContentPaths.DEFAULT_ID_SET_PATH # create the id_set only once per run. self.id_set_file = self.get_id_set_file( self.skip_id_set_creation, self.id_set_path @@ -359,7 +356,7 @@ def is_node_exist(self) -> bool: bool: True if node exist, else False """ # Check node exist - content_path = CONTENT_PATH + content_path = ContentPaths.CONTENT_PATH stdout, stderr, exit_code = run_command_os("node -v", cwd=content_path) # type: ignore if exit_code: return False @@ -911,7 +908,7 @@ def run_validations_on_file( ReadMeValidator.add_node_env_vars() if ( not ReadMeValidator.are_modules_installed_for_verify( - CONTENT_PATH # type: ignore + ContentPaths.CONTENT_PATH # type: ignore ) and not ReadMeValidator.is_docker_available() ): # shows warning message diff --git a/demisto_sdk/commands/validate/tests/BA_validators_test.py b/demisto_sdk/commands/validate/tests/BA_validators_test.py index e3d0244c035..84646a7f9e5 100644 --- a/demisto_sdk/commands/validate/tests/BA_validators_test.py +++ b/demisto_sdk/commands/validate/tests/BA_validators_test.py @@ -10,7 +10,6 @@ XSOAR_SUPPORT, ) from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_assets_modeling_rule_object, create_classifier_object, create_correlation_rule_object, @@ -43,6 +42,7 @@ create_xdrc_template_object, create_xsiam_dashboard_object, create_xsiam_report_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.BA_validators.BA100_is_valid_version import ( IsValidVersionValidator, @@ -1275,14 +1275,14 @@ def test_IsEntityNameContainExcludedWordValidator( [create_pack_object(), create_pack_object()], 1, [ - "Pack for content item '/newPackName' and all related files were changed from 'pack_171' to 'newPackName', please undo." + "Pack for content item '/newPackName' and all related files were changed from 'pack_1' to 'newPackName', please undo." ], ), ( [create_integration_object(), create_integration_object()], 1, [ - "Pack for content item '/newPackName/Integrations/integration_0/integration_0.yml' and all related files were changed from 'pack_173' to 'newPackName', please undo." + "Pack for content item '/newPackName/Integrations/integration_0/integration_0.yml' and all related files were changed from 'pack_25' to 'newPackName', please undo." ], ), ( @@ -1292,7 +1292,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/ParsingRules/TestParsingRule/TestParsingRule.yml' and all related files were changed from 'pack_175' to 'newPackName', please undo." + "Pack for content item '/newPackName/ParsingRules/TestParsingRule/TestParsingRule.yml' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1302,7 +1302,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/CorrelationRules/correlation_rule.yml' and all related files were changed from 'pack_177' to 'newPackName', please undo." + "Pack for content item '/newPackName/CorrelationRules/correlation_rule.yml' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1312,7 +1312,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Playbooks/playbook-0.yml' and all related files were changed from 'pack_179' to 'newPackName', please undo." + "Pack for content item '/newPackName/Playbooks/playbook-0.yml' and all related files were changed from 'pack_12' to 'newPackName', please undo." ], ), ( @@ -1322,7 +1322,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/ModelingRules/modelingrule_0/modelingrule_0.yml' and all related files were changed from 'pack_181' to 'newPackName', please undo." + "Pack for content item '/newPackName/ModelingRules/modelingrule_0/modelingrule_0.yml' and all related files were changed from 'pack_1' to 'newPackName', please undo." ], ), ( @@ -1332,7 +1332,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Integrations/integration_0/integration_0.yml' and all related files were changed from 'pack_183' to 'newPackName', please undo." + "Pack for content item '/newPackName/Integrations/integration_0/integration_0.yml' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1342,7 +1342,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Scripts/script0/script0.yml' and all related files were changed from 'pack_185' to 'newPackName', please undo." + "Pack for content item '/newPackName/Scripts/script0/script0.yml' and all related files were changed from 'pack_10' to 'newPackName', please undo." ], ), ( @@ -1352,7 +1352,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Classifiers/classifier-test_classifier.json' and all related files were changed from 'pack_187' to 'newPackName', please undo." + "Pack for content item '/newPackName/Classifiers/classifier-test_classifier.json' and all related files were changed from 'pack_9' to 'newPackName', please undo." ], ), ( @@ -1362,7 +1362,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Lists/list-list.json' and all related files were changed from 'pack_189' to 'newPackName', please undo." + "Pack for content item '/newPackName/Lists/list-list.json' and all related files were changed from 'pack_8' to 'newPackName', please undo." ], ), ( @@ -1372,7 +1372,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Jobs/job-job.json' and all related files were changed from 'pack_191' to 'newPackName', please undo." + "Pack for content item '/newPackName/Jobs/job-job.json' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1382,7 +1382,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Dashboards/dashboard-dashboard.json' and all related files were changed from 'pack_193' to 'newPackName', please undo." + "Pack for content item '/newPackName/Dashboards/dashboard-dashboard.json' and all related files were changed from 'pack_6' to 'newPackName', please undo." ], ), ( @@ -1392,7 +1392,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/IncidentTypes/incidenttype-incident_type.json' and all related files were changed from 'pack_195' to 'newPackName', please undo." + "Pack for content item '/newPackName/IncidentTypes/incidenttype-incident_type.json' and all related files were changed from 'pack_9' to 'newPackName', please undo." ], ), ( @@ -1402,7 +1402,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/IncidentFields/incidentfield-incident_field.json' and all related files were changed from 'pack_197' to 'newPackName', please undo." + "Pack for content item '/newPackName/IncidentFields/incidentfield-incident_field.json' and all related files were changed from 'pack_12' to 'newPackName', please undo." ], ), ( @@ -1412,7 +1412,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Reports/report-report.json' and all related files were changed from 'pack_199' to 'newPackName', please undo." + "Pack for content item '/newPackName/Reports/report-report.json' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1422,7 +1422,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/XSIAMReports/xsiam_report.json' and all related files were changed from 'pack_201' to 'newPackName', please undo." + "Pack for content item '/newPackName/XSIAMReports/xsiam_report.json' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1432,7 +1432,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/XSIAMDashboards/xsiam_dashboard.json' and all related files were changed from 'pack_203' to 'newPackName', please undo." + "Pack for content item '/newPackName/XSIAMDashboards/xsiam_dashboard.json' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1442,7 +1442,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/XDRCTemplates/pack_205_xdrc_template/xdrc_template.json' and all related files were changed from 'pack_205' to 'newPackName', please undo." + "Pack for content item '/newPackName/XDRCTemplates/pack_1_xdrc_template/xdrc_template.json' and all related files were changed from 'pack_1' to 'newPackName', please undo." ], ), ( @@ -1452,7 +1452,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/AssetsModelingRules/assets_modeling_rule/assets_modeling_rule.yml' and all related files were changed from 'pack_207' to 'newPackName', please undo." + "Pack for content item '/newPackName/AssetsModelingRules/assets_modeling_rule/assets_modeling_rule.yml' and all related files were changed from 'pack_4' to 'newPackName', please undo." ], ), ( @@ -1462,7 +1462,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Triggers/trigger.json' and all related files were changed from 'pack_209' to 'newPackName', please undo." + "Pack for content item '/newPackName/Triggers/trigger.json' and all related files were changed from 'pack_1' to 'newPackName', please undo." ], ), ( @@ -1472,7 +1472,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Layouts/layout-layout.json' and all related files were changed from 'pack_211' to 'newPackName', please undo." + "Pack for content item '/newPackName/Layouts/layout-layout.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1482,7 +1482,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Widgets/widget-widget.json' and all related files were changed from 'pack_213' to 'newPackName', please undo." + "Pack for content item '/newPackName/Widgets/widget-widget.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1492,7 +1492,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/IndicatorFields/indicatorfield-indicator_field.json' and all related files were changed from 'pack_215' to 'newPackName', please undo." + "Pack for content item '/newPackName/IndicatorFields/indicatorfield-indicator_field.json' and all related files were changed from 'pack_21' to 'newPackName', please undo." ], ), ( @@ -1502,7 +1502,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Wizards/wizard-test_wizard.json' and all related files were changed from 'pack_217' to 'newPackName', please undo." + "Pack for content item '/newPackName/Wizards/wizard-test_wizard.json' and all related files were changed from 'pack_10' to 'newPackName', please undo." ], ), ( @@ -1512,7 +1512,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/GenericDefinitions/genericdefinition-generic_definition.json' and all related files were changed from 'pack_219' to 'newPackName', please undo." + "Pack for content item '/newPackName/GenericDefinitions/genericdefinition-generic_definition.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1522,7 +1522,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/GenericFields/generic_field/genericfield-generic_field.json' and all related files were changed from 'pack_221' to 'newPackName', please undo." + "Pack for content item '/newPackName/GenericFields/generic_field/genericfield-generic_field.json' and all related files were changed from 'pack_7' to 'newPackName', please undo." ], ), ( @@ -1532,7 +1532,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/GenericTypes/generic_type/generictype-generic_type.json' and all related files were changed from 'pack_223' to 'newPackName', please undo." + "Pack for content item '/newPackName/GenericTypes/generic_type/generictype-generic_type.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1542,7 +1542,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/GenericModules/genericmodule-generic_module.json' and all related files were changed from 'pack_225' to 'newPackName', please undo." + "Pack for content item '/newPackName/GenericModules/genericmodule-generic_module.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1552,7 +1552,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Classifiers/classifier-mapper-incoming_mapper.json' and all related files were changed from 'pack_227' to 'newPackName', please undo." + "Pack for content item '/newPackName/Classifiers/classifier-mapper-incoming_mapper.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1562,7 +1562,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/Classifiers/classifier-mapper-outgoing_mapper.json' and all related files were changed from 'pack_229' to 'newPackName', please undo." + "Pack for content item '/newPackName/Classifiers/classifier-mapper-outgoing_mapper.json' and all related files were changed from 'pack_5' to 'newPackName', please undo." ], ), ( @@ -1572,7 +1572,7 @@ def test_IsEntityNameContainExcludedWordValidator( ], 1, [ - "Pack for content item '/newPackName/IndicatorTypes/reputation-indicator_type.json' and all related files were changed from 'pack_231' to 'newPackName', please undo." + "Pack for content item '/newPackName/IndicatorTypes/reputation-indicator_type.json' and all related files were changed from 'pack_1' to 'newPackName', please undo." ], ), ], @@ -1602,12 +1602,8 @@ def test_ValidPackNameValidator_obtain_invalid_content_items( content_items[1].path = new_path results = PackNameValidator().obtain_invalid_content_items(content_items) assert len(results) == expected_number_of_failures - assert all( - [ - result.message == expected_msg - for result, expected_msg in zip(results, expected_msgs) - ] - ) + for result, expected_msg in zip(results, expected_msgs): + assert result.message == expected_msg @pytest.mark.parametrize( @@ -1617,34 +1613,34 @@ def test_ValidPackNameValidator_obtain_invalid_content_items( pytest.param( [create_integration_object(readme_content="test-module")], 1, - "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_233/Integrations/integration_0/README.md", + "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_27/Integrations/integration_0/README.md", id="invalid: integration readme", ), pytest.param( [create_integration_object(description_content="test-module")], 1, - "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_234/Integrations/integration_0/integration_0_description.md", + "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_28/Integrations/integration_0/integration_0_description.md", id="invalid: integration description", ), pytest.param([create_script_object()], 0, "", id="valid: script"), pytest.param( [create_script_object(readme_content="test-module ")], 1, - "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_236/Scripts/script0/README.md", + "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_12/Scripts/script0/README.md", id="invalid: script readme", ), pytest.param([create_playbook_object()], 0, "", id="valid: playbook"), pytest.param( [create_playbook_object(readme_content="test-module ")], 1, - "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_238/Playbooks/playbook-0_README.md", + "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_14/Playbooks/playbook-0_README.md", id="invalid: playbook readme", ), pytest.param([create_pack_object()], 0, "", id="valid: pack"), pytest.param( [create_pack_object(readme_text="test-module ")], 1, - "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_240/README.md", + "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_3/README.md", id="invalid: pack readme", ), pytest.param( @@ -1654,7 +1650,7 @@ def test_ValidPackNameValidator_obtain_invalid_content_items( ) ], 1, - "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_241/ReleaseNotes/1_0_1.md", + "Found internal terms in a customer-facing documentation: found test-module in Packs/pack_4/ReleaseNotes/1_0_1.md", id="invalid: pack release note", ), ], @@ -2096,17 +2092,20 @@ def test_IsHaveUnitTestFileValidator_obtain_invalid_content_items__all_valid(): Then - Make sure no failures are returned. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( name="MyIntegration0", unit_test_name="MyIntegration0", pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), create_integration_object( name="MyIntegration0", unit_test_name="MyIntegration0", pack_info={"support": PARTNER_SUPPORT}, + repo=repo, ), ] @@ -2127,12 +2126,14 @@ def test_IsHaveUnitTestFileValidator_obtain_invalid_content_items__invalid_item( Then - Make sure one failure is returned and the error message is correct. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( name="MyIntegration0", unit_test_name="myintegration0", pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] @@ -2163,7 +2164,8 @@ def test_is_valid_context_path_depth_command(): Then - Make sure one failure is returned and the error message is correct. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( paths=["script.commands"], @@ -2190,6 +2192,7 @@ def test_is_valid_context_path_depth_command(): ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] expected_msg = "The level of depth for context output path for command: ip In the yml should be less or equal to 5 check the following outputs:\npath_1.2.3.4.5.6\n" @@ -2212,7 +2215,8 @@ def test_is_valid_context_path_depth_script(): Then - Make sure one failure is returned and the error message is correct. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_script_object( paths=["outputs"], @@ -2226,6 +2230,7 @@ def test_is_valid_context_path_depth_script(): ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] expected_msg = "The level of depth for context output path for script: myScript In the yml should be less or equal to 5 check the following outputs:\ntest.test.1.2.3.4.5.6" @@ -2248,7 +2253,8 @@ def test_is_valid_context_path_depth_command_multiple_invalid_outputs(): Then - Make sure the paths exist in the error message that is returned """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( paths=["script.commands"], @@ -2280,6 +2286,7 @@ def test_is_valid_context_path_depth_command_multiple_invalid_outputs(): ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] @@ -2305,7 +2312,8 @@ def test_is_valid_context_path_depth_command_multiple_commands_with_invalid_outp Then - Make sure the paths exist in the error message that is returned """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( paths=["script.commands"], @@ -2360,6 +2368,7 @@ def test_is_valid_context_path_depth_command_multiple_commands_with_invalid_outp ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] invalid_path_ip_1 = "path_1.2.3.4.5.6" diff --git a/demisto_sdk/commands/validate/tests/BC_validators_test.py b/demisto_sdk/commands/validate/tests/BC_validators_test.py index 9b219a44732..610065305a4 100644 --- a/demisto_sdk/commands/validate/tests/BC_validators_test.py +++ b/demisto_sdk/commands/validate/tests/BC_validators_test.py @@ -9,7 +9,6 @@ from demisto_sdk.commands.content_graph.objects.mapper import Mapper from demisto_sdk.commands.content_graph.objects.script import Script from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_incident_field_object, create_incident_type_object, create_incoming_mapper_object, @@ -17,6 +16,7 @@ create_old_file_pointers, create_pack_object, create_script_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.BC_validators.BC100_breaking_backwards_subtype import ( BreakingBackwardsSubtypeValidator, @@ -385,11 +385,17 @@ def test_WasMarketplaceModifiedValidator__modified_item_has_only_one_marketplace - Case 2: Should pass the validation since the user did not remove any marketplace. - Case 3: Should pass the validation since the user did not remove any marketplace. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): modified_content_items = [ - create_integration_object(pack_info={"marketplaces": in_pack_marketplaces}), - create_script_object(pack_info={"marketplaces": in_pack_marketplaces}), + create_integration_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + create_script_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), ] + # old_content_items = [create_integration_object(repo=repo), create_script_object(repo=repo)] old_content_items = [create_integration_object(), create_script_object()] modified_content_items[0].marketplaces = modified_content_items[ @@ -433,12 +439,20 @@ def test_WasMarketplaceModifiedValidator__modified_item_has_only_one_marketplace - Case 1: Should fail the validation since the user removed marketplaces. - Case 2: Should fail the validation since the user replaced one marketplace with a different one. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): modified_content_items = [ - create_integration_object(pack_info={"marketplaces": in_pack_marketplaces}), - create_script_object(pack_info={"marketplaces": in_pack_marketplaces}), + create_integration_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + create_script_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + ] + old_content_items = [ + create_integration_object(repo=repo), + create_script_object(repo=repo), ] - old_content_items = [create_integration_object(), create_script_object()] modified_content_items[0].marketplaces = modified_content_items[ 1 @@ -483,12 +497,20 @@ def test_WasMarketplaceModifiedValidator__old_item_has_only_one_marketplace__pas - Case 1: Should pass the validation since the user added marketplaces or removed all marketplaces which is equal to adding all marketplaces. - Case 2: Should pass the validation since the user added marketplaces or removed all marketplaces which is equal to adding all marketplaces. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): modified_content_items = [ - create_integration_object(pack_info={"marketplaces": in_pack_marketplaces}), - create_script_object(pack_info={"marketplaces": in_pack_marketplaces}), + create_integration_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + create_script_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + ] + old_content_items = [ + create_integration_object(repo=repo), + create_script_object(repo=repo), ] - old_content_items = [create_integration_object(), create_script_object()] modified_content_items[0].marketplaces = modified_content_items[ 1 @@ -521,15 +543,23 @@ def test_WasMarketplaceModifiedValidator__old_item_has_only_one_marketplace__fai - Should fail the validation since the user replaced one marketplace with a different one. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): modified_marketplaces = XSOAR_MARKETPLACE in_pack_marketplaces = ALL_MARKETPLACES_FOR_IN_PACK modified_content_items = [ - create_integration_object(pack_info={"marketplaces": in_pack_marketplaces}), - create_script_object(pack_info={"marketplaces": in_pack_marketplaces}), + create_integration_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + create_script_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + ] + old_content_items = [ + create_integration_object(repo=repo), + create_script_object(repo=repo), ] - old_content_items = [create_integration_object(), create_script_object()] modified_content_items[0].marketplaces = modified_content_items[ 1 @@ -575,12 +605,20 @@ def test_WasMarketplaceModifiedValidator__old_and_modified_items_have_all_market - Case 1: Should pass the validation since the user added marketplaces or removed all marketplaces which is equal to adding all marketplaces. - Case 2: Should pass the validation since the user didn't change anything or removed all marketplaces which is equal to adding all marketplaces. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): modified_content_items = [ - create_integration_object(pack_info={"marketplaces": in_pack_marketplaces}), - create_script_object(pack_info={"marketplaces": in_pack_marketplaces}), + create_integration_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + create_script_object( + pack_info={"marketplaces": in_pack_marketplaces}, repo=repo + ), + ] + old_content_items = [ + create_integration_object(repo=repo), + create_script_object(repo=repo), ] - old_content_items = [create_integration_object(), create_script_object()] create_old_file_pointers(modified_content_items, old_content_items) @@ -684,15 +722,23 @@ def test_WasMarketplaceModifiedValidator__renamed__fails(): - Should fail the validation since moving to a different pack with less marketplaces is not allowed. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): renamed_content_items = [ - create_integration_object(pack_info={"marketplaces": XSOAR_MARKETPLACE}), - create_script_object(pack_info={"marketplaces": XSOAR_MARKETPLACE}), + create_integration_object( + pack_info={"marketplaces": XSOAR_MARKETPLACE}, repo=repo + ), + create_script_object( + pack_info={"marketplaces": XSOAR_MARKETPLACE}, repo=repo + ), ] renamed_content_items[0].git_status = renamed_content_items[1].git_status = ( GitStatuses.RENAMED ) - old_content_items = [create_integration_object(), create_script_object()] + old_content_items = [ + create_integration_object(repo=repo), + create_script_object(repo=repo), + ] old_content_items[0].marketplaces = old_content_items[1].marketplaces = ( ALL_MARKETPLACES_FOR_IN_PACK @@ -724,19 +770,23 @@ def test_WasMarketplaceModifiedValidator__renamed__passes(): - Should pass the validation since the new host has all marketplaces in pack level. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): renamed_content_items = [ create_integration_object( - pack_info={"marketplaces": ALL_MARKETPLACES_FOR_IN_PACK} + pack_info={"marketplaces": ALL_MARKETPLACES_FOR_IN_PACK}, repo=repo ), create_script_object( - pack_info={"marketplaces": ALL_MARKETPLACES_FOR_IN_PACK} + pack_info={"marketplaces": ALL_MARKETPLACES_FOR_IN_PACK}, repo=repo ), ] renamed_content_items[0].git_status = renamed_content_items[1].git_status = ( GitStatuses.RENAMED ) - old_content_items = [create_integration_object(), create_script_object()] + old_content_items = [ + create_integration_object(repo=repo), + create_script_object(repo=repo), + ] old_content_items[0].marketplaces = old_content_items[1].marketplaces = ( XSOAR_MARKETPLACE diff --git a/demisto_sdk/commands/validate/tests/GR_validators_test.py b/demisto_sdk/commands/validate/tests/GR_validators_test.py index d384e0f4f15..07d3f66bdd8 100644 --- a/demisto_sdk/commands/validate/tests/GR_validators_test.py +++ b/demisto_sdk/commands/validate/tests/GR_validators_test.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from pytest_mock import MockerFixture @@ -83,9 +85,9 @@ def test_IsPackDisplayNameAlreadyExistsValidatorListFiles_obtain_invalid_content - Validate that we got the error messages for the duplicate name. """ mocker.patch.object( - GR104_is_pack_display_name_already_exists, + GR104_is_pack_display_name_already_exists.ContentPaths, "CONTENT_PATH", - new=graph_repo.path, + new=Path(graph_repo.path), ) graph_repo.create_pack(name="pack1") @@ -122,9 +124,9 @@ def test_IsPackDisplayNameAlreadyExistsValidatorAllFiles_obtain_invalid_content_ - Validate that we got the error messages for the duplicate name. """ mocker.patch.object( - GR104_is_pack_display_name_already_exists, + GR104_is_pack_display_name_already_exists.ContentPaths, "CONTENT_PATH", - new=graph_repo.path, + new=Path(graph_repo.path), ) graph_repo.create_pack(name="pack1") diff --git a/demisto_sdk/commands/validate/tests/IF_validators_test.py b/demisto_sdk/commands/validate/tests/IF_validators_test.py index 55e346dab7b..b167a900b43 100644 --- a/demisto_sdk/commands/validate/tests/IF_validators_test.py +++ b/demisto_sdk/commands/validate/tests/IF_validators_test.py @@ -4,9 +4,9 @@ from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_incident_field_object, create_old_file_pointers, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.IF_validators.IF100_is_valid_name_and_cli_name import ( IsValidNameAndCliNameValidator, @@ -281,9 +281,12 @@ def test_NameFieldPrefixValidator_obtain_invalid_content_items_without_item_pref """ # not valid pack_name = "Foo" - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): # Create an Incident field so that there is no prefix name of the pack in the name field - content_item = create_incident_field_object(pack_info={"name": pack_name}) + content_item = create_incident_field_object( + pack_info={"name": pack_name}, repo=repo + ) assert not content_item.name.startswith(pack_name) results = NameFieldPrefixValidator().obtain_invalid_content_items( [content_item] @@ -334,10 +337,11 @@ def test_NameFieldPrefixValidator_obtain_invalid_content_items_with_item_prefix( - Ensure that no ValidationResult returned when prefix name is in itemPrefix which is in pack_metadata """ + repo = get_temp_repo() # not valid - with ChangeCWD(REPO.path): + with ChangeCWD(repo.path): content_item = create_incident_field_object( - pack_info={"name": "Foo", "itemPrefix": item_prefix} + pack_info={"name": "Foo", "itemPrefix": item_prefix}, repo=repo ) results = NameFieldPrefixValidator().obtain_invalid_content_items( [content_item] @@ -364,8 +368,11 @@ def test_NameFieldPrefixValidator_obtain_invalid_content_items_with_special_pack Then: - Ensure that no ValidationResult returned """ - with ChangeCWD(REPO.path): - content_item = create_incident_field_object(pack_info={"name": special_pack}) + repo = get_temp_repo() + with ChangeCWD(repo.path): + content_item = create_incident_field_object( + pack_info={"name": special_pack}, repo=repo + ) assert not NameFieldPrefixValidator().obtain_invalid_content_items( [content_item] ) diff --git a/demisto_sdk/commands/validate/tests/IN_validators_test.py b/demisto_sdk/commands/validate/tests/IN_validators_test.py index 4ff896d7302..ad01916d7af 100644 --- a/demisto_sdk/commands/validate/tests/IN_validators_test.py +++ b/demisto_sdk/commands/validate/tests/IN_validators_test.py @@ -23,10 +23,10 @@ ) from demisto_sdk.commands.content_graph.objects.integration import Integration from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_integration_object, create_old_file_pointers, create_script_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.IN_validators.IN100_is_valid_proxy_and_insecure import ( IsValidProxyAndInsecureValidator, @@ -4951,12 +4951,19 @@ def test_IsContainingFromLicenseInParamsValidator_obtain_invalid_content_items__ Then - Make sure the validation pass for all. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ - create_integration_object(pack_info={"support": XSOAR_SUPPORT}), - create_integration_object(pack_info={"support": PARTNER_SUPPORT}), - create_integration_object(pack_info={"support": DEVELOPER_SUPPORT}), - create_integration_object(pack_info={"support": COMMUNITY_SUPPORT}), + create_integration_object(pack_info={"support": XSOAR_SUPPORT}, repo=repo), + create_integration_object( + pack_info={"support": PARTNER_SUPPORT}, repo=repo + ), + create_integration_object( + pack_info={"support": DEVELOPER_SUPPORT}, repo=repo + ), + create_integration_object( + pack_info={"support": COMMUNITY_SUPPORT}, repo=repo + ), create_integration_object( paths=["configuration"], values=[ @@ -4971,6 +4978,7 @@ def test_IsContainingFromLicenseInParamsValidator_obtain_invalid_content_items__ ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] @@ -4995,7 +5003,8 @@ def test_IsContainingFromLicenseInParamsValidator_obtain_invalid_content_items__ Then - Make sure the validation fail and the right error message is returned. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( paths=["configuration"], @@ -5011,6 +5020,7 @@ def test_IsContainingFromLicenseInParamsValidator_obtain_invalid_content_items__ ] ], pack_info={"support": PARTNER_SUPPORT}, + repo=repo, ), create_integration_object( paths=["configuration"], @@ -5026,6 +5036,7 @@ def test_IsContainingFromLicenseInParamsValidator_obtain_invalid_content_items__ ] ], pack_info={"support": DEVELOPER_SUPPORT}, + repo=repo, ), create_integration_object( paths=["configuration"], @@ -5041,6 +5052,7 @@ def test_IsContainingFromLicenseInParamsValidator_obtain_invalid_content_items__ ] ], pack_info={"support": COMMUNITY_SUPPORT}, + repo=repo, ), ] @@ -5118,7 +5130,8 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_v Then - Make sure the validation pass for all. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( paths=["configuration"], @@ -5141,6 +5154,7 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_v ] ], pack_info={"support": PARTNER_SUPPORT}, + repo=repo, ), create_integration_object( paths=["configuration"], @@ -5163,6 +5177,7 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_v ] ], pack_info={"support": DEVELOPER_SUPPORT}, + repo=repo, ), create_integration_object( paths=["configuration"], @@ -5185,6 +5200,7 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_v ] ], pack_info={"support": COMMUNITY_SUPPORT}, + repo=repo, ), create_integration_object( paths=["configuration"], @@ -5206,6 +5222,7 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_v ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] @@ -5225,7 +5242,8 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_i Then - Make sure the validation fail and the right error message is returned. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( paths=["configuration"], @@ -5241,6 +5259,7 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_i ] ], pack_info={"support": XSOAR_SUPPORT}, + repo=repo, ), ] @@ -5261,113 +5280,125 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_i ) -@pytest.mark.parametrize( - "content_items, expected_number_of_failures, expected_msgs", - [ - ( - [ - create_integration_object( - paths=["script.commands"], - values=[[]], - pack_info={"name": "pack_no_1"}, - ), - create_integration_object( - paths=["script.commands"], - values=[ - [ +def create_content_items_1(repo): + return [ + create_integration_object( + paths=["script.commands"], + values=[[]], + pack_info={"name": "pack_no_1"}, + repo=repo, + ), + create_integration_object( + paths=["script.commands"], + values=[ + [ + { + "name": "ip", + "description": "ip command", + "deprecated": False, + "arguments": [ { - "name": "ip", - "description": "ip command", - "deprecated": False, - "arguments": [ - { - "name": "ip_1", - "default": True, - "isArray": True, - "required": True, - "description": "ip_1_description", - }, - { - "name": "ip_2", - "default": True, - "isArray": True, - "required": True, - "description": "ip_2_description", - }, - ], - "outputs": [], + "name": "ip_1", + "default": True, + "isArray": True, + "required": True, + "description": "ip_1_description", }, - ] - ], - pack_info={"name": "pack_no_2"}, - ), - create_integration_object( - paths=["script.commands"], - values=[ - [ { - "name": "incident_command", - "description": "ip command", - "deprecated": False, - "arguments": [ - { - "name": "incident_arg_no_1", - "default": True, - "isArray": True, - "required": True, - "description": "ip_1_description", - }, - { - "name": "ip_2", - "default": True, - "isArray": True, - "required": True, - "description": "ip_2_description", - }, - ], - "outputs": [], + "name": "ip_2", + "default": True, + "isArray": True, + "required": True, + "description": "ip_2_description", }, - ] - ], - pack_info={"name": "pack_no_3"}, - ), + ], + "outputs": [], + }, + ] ], - 0, - [], + pack_info={"name": "pack_no_2"}, + repo=repo, ), - ( - [ - create_integration_object( - paths=["script.commands"], - values=[ - [ + create_integration_object( + paths=["script.commands"], + values=[ + [ + { + "name": "incident_command", + "description": "ip command", + "deprecated": False, + "arguments": [ + { + "name": "incident_arg_no_1", + "default": True, + "isArray": True, + "required": True, + "description": "ip_1_description", + }, { - "name": "incident_command", - "description": "ip command", - "deprecated": False, - "arguments": [ - { - "name": "incident_arg_no_1", - "default": True, - "isArray": True, - "required": True, - "description": "ip_1_description", - }, - { - "name": "ip_2", - "default": True, - "isArray": True, - "required": True, - "description": "ip_2_description", - }, - ], - "outputs": [], + "name": "ip_2", + "default": True, + "isArray": True, + "required": True, + "description": "ip_2_description", }, - ] - ], - pack_info={"name": "pack_no_4"}, - ) + ], + "outputs": [], + }, + ] + ], + pack_info={"name": "pack_no_3"}, + repo=repo, + ), + ] + + +def create_content_items_2(repo): + return [ + create_integration_object( + paths=["script.commands"], + values=[ + [ + { + "name": "incident_command", + "description": "ip command", + "deprecated": False, + "arguments": [ + { + "name": "incident_arg_no_1", + "default": True, + "isArray": True, + "required": True, + "description": "ip_1_description", + }, + { + "name": "ip_2", + "default": True, + "isArray": True, + "required": True, + "description": "ip_2_description", + }, + ], + "outputs": [], + }, + ] ], + pack_info={"name": "pack_no_4"}, + repo=repo, + ) + ] + + +@pytest.mark.parametrize( + "create_content_items_func, expected_number_of_failures, expected_msgs", + [ + ( + create_content_items_1, + 0, + [], + ), + ( + create_content_items_2, 1, [ "The following commands contain the word 'incident' in one or more of their fields, please remove:\nThe command incident_command contains the word 'incident' in its name and in the following arguments: incident_arg_no_1." @@ -5376,7 +5407,7 @@ def test_IsAPITokenInCredentialTypeValidator_obtain_invalid_content_items__all_i ], ) def test_IsNameContainIncidentInCorePackValidator_obtain_invalid_content_items( - mocker, content_items, expected_number_of_failures, expected_msgs + create_content_items_func, mocker, expected_number_of_failures, expected_msgs ): """ Given @@ -5397,7 +5428,9 @@ def test_IsNameContainIncidentInCorePackValidator_obtain_invalid_content_items( "demisto_sdk.commands.validate.validators.IN_validators.IN139_is_name_contain_incident_in_core_pack.get_core_pack_list", return_value=["pack_no_1", "pack_no_2", "pack_no_4"], ) - with ChangeCWD(REPO.path): + repo = get_temp_repo() + content_items = create_content_items_func(repo) + with ChangeCWD(repo.path): results = ( IsNameContainIncidentInCorePackValidator().obtain_invalid_content_items( content_items @@ -5426,29 +5459,36 @@ def test_IsPartnerCollectorHasXsoarSupportLevelValidator_obtain_invalid_content_ Then - Make sure the validation pass for all. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( pack_info={"support": XSOAR_SUPPORT}, paths=["script.isfetchevents"], values=[True], + repo=repo, ), create_integration_object( pack_info={"support": PARTNER_SUPPORT}, paths=["supportlevelheader", "script.isfetchevents"], values=[XSOAR_SUPPORT, True], + repo=repo, ), create_integration_object( pack_info={"support": XSOAR_SUPPORT}, paths=["script.isfetcheventsandassets"], values=[True], + repo=repo, ), create_integration_object( pack_info={"support": PARTNER_SUPPORT}, paths=["supportlevelheader", "script.isfetcheventsandassets"], values=[XSOAR_SUPPORT, True], + repo=repo, + ), + create_integration_object( + pack_info={"support": PARTNER_SUPPORT}, repo=repo ), - create_integration_object(pack_info={"support": PARTNER_SUPPORT}), ] results = IsPartnerCollectorHasXsoarSupportLevelValidator().obtain_invalid_content_items( @@ -5469,17 +5509,20 @@ def test_IsPartnerCollectorHasXsoarSupportLevelValidator_obtain_invalid_content_ Then - Make sure the validation fail and the right error message is returned. """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): content_items = [ create_integration_object( pack_info={"support": PARTNER_SUPPORT}, paths=["script.isfetchevents"], values=[True], + repo=repo, ), create_integration_object( pack_info={"support": PARTNER_SUPPORT}, paths=["script.isfetcheventsandassets"], values=[True], + repo=repo, ), ] diff --git a/demisto_sdk/commands/validate/tests/PA_validators_test.py b/demisto_sdk/commands/validate/tests/PA_validators_test.py index 75c191bb86c..718da0540e4 100644 --- a/demisto_sdk/commands/validate/tests/PA_validators_test.py +++ b/demisto_sdk/commands/validate/tests/PA_validators_test.py @@ -21,13 +21,13 @@ from demisto_sdk.commands.content_graph.objects.base_content import BaseNode from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFile from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_integration_object, create_modeling_rule_object, create_old_file_pointers, create_pack_object, create_playbook_object, create_script_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.base_validator import BaseValidator from demisto_sdk.commands.validate.validators.PA_validators.PA100_valid_tags_prefixes import ( @@ -1702,9 +1702,10 @@ def test_PackMetadataVersionShouldBeRaisedValidator( "`demisto-sdk update-release-notes -i Packs/{pack} -u " "(major|minor|revision|documentation)` for a specific pack and version." ) - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): integration = create_integration_object( - pack_info={"currentVersion": current_version} + pack_info={"currentVersion": current_version}, repo=repo ) pack = integration.in_pack integration.git_status = GitStatuses.MODIFIED @@ -1744,8 +1745,11 @@ def test_PackMetadataVersionShouldBeRaisedValidator_metadata_change(mocker): ) old_version = "1.0.0" current_version = "1.0.0" - with ChangeCWD(REPO.path): - pack = create_pack_object(["currentVersion", "price"], [current_version, 5]) + repo = get_temp_repo() + with ChangeCWD(repo.path): + pack = create_pack_object( + ["currentVersion", "price"], [current_version, 5], repo=repo + ) old_pack = pack.copy(deep=True) old_pack.current_version = old_version diff --git a/demisto_sdk/commands/validate/tests/RM_validators_test.py b/demisto_sdk/commands/validate/tests/RM_validators_test.py index e5e7a8a3d0e..937072a63c0 100644 --- a/demisto_sdk/commands/validate/tests/RM_validators_test.py +++ b/demisto_sdk/commands/validate/tests/RM_validators_test.py @@ -5,12 +5,12 @@ from demisto_sdk.commands.common.tools import find_pack_folder from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_doc_file_object, create_integration_object, create_pack_object, create_playbook_object, create_script_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.base_validator import ValidationResult from demisto_sdk.commands.validate.validators.RM_validators.RM100_no_empty_sections import ( @@ -255,81 +255,101 @@ def test_is_image_path_validator(content_items, expected_number_of_failures): ) +def create_content_items_1(temp_repo): + return [ + create_playbook_object( + readme_content="This is a valid readme without any images.", + pack_info={"name": "test1"}, + repo=temp_repo, + ), + create_playbook_object( + readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", + pack_info={"name": "test2"}, + repo=temp_repo, + ), + create_playbook_object(readme_content="", pack_info={"name": "test1"}), + create_integration_object( + readme_content="This is a valid readme without any images.", + pack_info={"name": "test3"}, + repo=temp_repo, + ), + create_integration_object( + readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", + pack_info={"name": "test4"}, + repo=temp_repo, + ), + create_integration_object( + readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.jpg)", + pack_info={"name": "test5"}, + repo=temp_repo, + ), + create_integration_object( + readme_content="", pack_info={"name": "test6"}, repo=temp_repo + ), + ] + + +def create_content_item_2(temp_repo): + return [ + create_playbook_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png), ", + pack_info={"name": "pack_0"}, + repo=temp_repo, + ), + create_integration_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png)", + pack_info={"name": "pack_1"}, + repo=temp_repo, + ), + create_playbook_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png ), ", + pack_info={"name": "pack_2"}, + repo=temp_repo, + ), + create_integration_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image]( ../doc_files/example.png)", + pack_info={"name": "pack_3"}, + repo=temp_repo, + ), + create_integration_object( + readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.jpg)", + pack_info={"name": "pack_4"}, + repo=temp_repo, + ), + ] + + @pytest.mark.parametrize( - "content_items, doc_files_name, expected_number_of_failures, expected_msgs", + "create_content_items_func, doc_files_name, expected_number_of_failures, expected_msgs", [ ( - [ - create_playbook_object( - readme_content="This is a valid readme without any images.", - pack_info={"name": "test1"}, - ), - create_playbook_object( - readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", - pack_info={"name": "test1"}, - ), - create_playbook_object(readme_content="", pack_info={"name": "test1"}), - create_integration_object( - readme_content="This is a valid readme without any images.", - pack_info={"name": "test2"}, - ), - create_integration_object( - readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.png)", - pack_info={"name": "test2"}, - ), - create_integration_object( - readme_content="This is a valid readme if this file exists ![example image](../doc_files/example.jpg)", - pack_info={"name": "test2"}, - ), - create_integration_object( - readme_content="", pack_info={"name": "test2"} - ), - ], + create_content_items_1, [None, "example.png", None, None, "example.png", "example.jpg", None], 0, [], ), ( - [ - create_playbook_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png), ", - pack_info={"name": "test1"}, - ), - create_integration_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png)", - pack_info={"name": "test2"}, - ), - create_playbook_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.png ), ", - pack_info={"name": "test3"}, - ), - create_integration_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image]( ../doc_files/example.png)", - pack_info={"name": "test4"}, - ), - create_integration_object( - readme_content="This is not a valid readme if this file doesn't exists ![example image](../doc_files/example.jpg)", - pack_info={"name": "test5"}, - ), - ], + create_content_item_2, [None, None, "example.png", "example.png", None], 5, [ - "The following images do not exist or have additional characters present in their declaration within the README: Packs/test1/doc_files/example.png", - "The following images do not exist or have additional characters present in their declaration within the README: Packs/test2/doc_files/example.png", - "The following images do not exist or have additional characters present in their declaration within the README: Packs/test3/doc_files/example.png", - "The following images do not exist or have additional characters present in their declaration within the README: Packs/test5/doc_files/example.jpg", + "The following images do not exist or have additional characters present in their declaration within the README: Packs/pack_0/doc_files/example.png", + "The following images do not exist or have additional characters present in their declaration within the README: Packs/pack_1/doc_files/example.png", + "The following images do not exist or have additional characters present in their declaration within the README: Packs/pack_2/doc_files/example.png ", + "The following images do not exist or have additional characters present in their declaration within the README: Packs/pack_3/ doc_files/example.png", ], ), ], ) def test_IsImageExistsInReadmeValidator_obtain_invalid_content_items( - content_items, + create_content_items_func, doc_files_name, expected_number_of_failures, expected_msgs, ): - with ChangeCWD(REPO.path): + repo = get_temp_repo() + content_items = create_content_items_func(repo) + with ChangeCWD(repo.path): for content_item, file_name in zip(content_items, doc_files_name): if file_name: create_doc_file_object(find_pack_folder(content_item.path), file_name) @@ -338,12 +358,8 @@ def test_IsImageExistsInReadmeValidator_obtain_invalid_content_items( content_items ) assert len(results) == expected_number_of_failures - assert all( - [ - (result.message, expected_msg) - for result, expected_msg in zip(results, expected_msgs) - ] - ) + for result, expected_msg in zip(results, expected_msgs): + assert result.message == expected_msg def test_IsPackReadmeNotEqualPackDescriptionValidator_not_valid(): diff --git a/demisto_sdk/commands/validate/tests/SC_validators_test.py b/demisto_sdk/commands/validate/tests/SC_validators_test.py index c6026977e6c..855dc6039b6 100644 --- a/demisto_sdk/commands/validate/tests/SC_validators_test.py +++ b/demisto_sdk/commands/validate/tests/SC_validators_test.py @@ -1,10 +1,12 @@ +from pathlib import Path + from demisto_sdk.commands.common.constants import ( SKIP_PREPARE_SCRIPT_NAME, MarketplaceVersions, ) from demisto_sdk.commands.validate.tests.test_tools import ( - REPO, create_script_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validators.base_validator import BaseValidator from demisto_sdk.commands.validate.validators.SC_validators import ( @@ -80,7 +82,8 @@ def test_IsScriptArgumentsContainIncidentWordValidatorCorePacks_obtain_invalid_c Then: - make sure the script with the argument that has "incident" fails the validation """ - with ChangeCWD(REPO.path): + repo = get_temp_repo() + with ChangeCWD(repo.path): mocker.patch( "demisto_sdk.commands.validate.validators.SC_validators.SC105_incident_not_in_args_validator_core_packs.get_core_pack_list", return_value=["PackWithInvalidScript"], @@ -94,6 +97,7 @@ def test_IsScriptArgumentsContainIncidentWordValidatorCorePacks_obtain_invalid_c [{"name": "incident-id", "description": "test"}], ], pack_info={"name": "PackWithInvalidScript"}, + repo=repo, ), create_script_object( paths=["args"], @@ -107,8 +111,9 @@ def test_IsScriptArgumentsContainIncidentWordValidatorCorePacks_obtain_invalid_c ], ], pack_info={"name": "PackWithValidScript"}, + repo=repo, ), - create_script_object(), + create_script_object(repo=repo), ) results = IsScriptArgumentsContainIncidentWordValidatorCorePacks().obtain_invalid_content_items( @@ -161,9 +166,9 @@ def test_DuplicatedScriptNameValidatorListFiles_obtain_invalid_content_items( - Validate that only the first pair of scripts appear in the results, and the rest of the scripts is valid. """ mocker.patch.object( - SC109_script_name_is_not_unique_validator, + SC109_script_name_is_not_unique_validator.ContentPaths, "CONTENT_PATH", - new=graph_repo.path, + new=Path(graph_repo.path), ) pack = graph_repo.create_pack() @@ -209,9 +214,9 @@ def test_DuplicatedScriptNameValidatorAllFiles_obtain_invalid_content_items( - Validate that only the first pair of scripts appear in the results, and the rest of the scripts is valid. """ mocker.patch.object( - SC109_script_name_is_not_unique_validator, + SC109_script_name_is_not_unique_validator.ContentPaths, "CONTENT_PATH", - new=graph_repo.path, + new=Path(graph_repo.path), ) pack = graph_repo.create_pack() diff --git a/demisto_sdk/commands/validate/tests/old_validators_test.py b/demisto_sdk/commands/validate/tests/old_validators_test.py index 53825b958c3..10b9c0e3633 100644 --- a/demisto_sdk/commands/validate/tests/old_validators_test.py +++ b/demisto_sdk/commands/validate/tests/old_validators_test.py @@ -19,7 +19,7 @@ FileType, ) from demisto_sdk.commands.common.content.content import Content -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.errors import Errors from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.common.hook_validations.base_validator import BaseValidator @@ -184,11 +184,11 @@ def setup_class(cls): if not Path(dir_to_create).exists(): cls.CREATED_DIRS.append(dir_to_create) os.makedirs(dir_to_create) - copyfile(CONF_JSON_MOCK_PATH, CONF_PATH) + copyfile(CONF_JSON_MOCK_PATH, ContentPaths.CONF_PATH) @classmethod def teardown_class(cls): - Path(CONF_PATH).unlink() + Path(ContentPaths.CONF_PATH).unlink() for dir_to_delete in cls.CREATED_DIRS: if Path(dir_to_delete).exists(): os.rmdir(dir_to_delete) diff --git a/demisto_sdk/commands/validate/tests/test_tools.py b/demisto_sdk/commands/validate/tests/test_tools.py index f52fb5045ea..20684c0a6dd 100644 --- a/demisto_sdk/commands/validate/tests/test_tools.py +++ b/demisto_sdk/commands/validate/tests/test_tools.py @@ -1,3 +1,4 @@ +import os import tempfile from pathlib import Path from typing import Any, Dict, List, Optional, cast @@ -57,7 +58,12 @@ from TestSuite.file import File from TestSuite.repo import Repo -REPO = Repo(tmpdir=Path(tempfile.mkdtemp()), init_git=True) + +def get_temp_repo() -> Repo: + content_temp_dir = Path(os.path.join(tempfile.mkdtemp(), "content")) + content_temp_dir.mkdir() + + return Repo(tmpdir=content_temp_dir, init_git=True) def create_integration_object( @@ -69,12 +75,14 @@ def create_integration_object( name: Optional[str] = None, code: Optional[str] = None, unit_test_name: Optional[str] = None, + repo=get_temp_repo(), ) -> Integration: """Creating an integration object with altered fields from a default integration yml structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The integration object. @@ -82,7 +90,7 @@ def create_integration_object( yml_content = load_yaml("integration.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() if pack_info: pack.set_data(**pack_info) @@ -109,19 +117,21 @@ def create_integration_object( def create_parsing_rule_object( paths: Optional[List[str]] = None, values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> ParsingRule: - """Creating an parsing_rule object with altered fields from a default parsing_rule yml structure. + """Creating a parsing_rule object with altered fields from a default parsing_rule yml structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The parsing_rule object. """ yml_content = load_yaml("parsing_rule.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() parsing_rule = pack.create_parsing_rule("TestParsingRule", yml_content) parser = ParsingRuleParser(Path(parsing_rule.path), list(MarketplaceVersions)) return ParsingRule.from_orm(parser) @@ -130,19 +140,21 @@ def create_parsing_rule_object( def create_correlation_rule_object( paths: Optional[List[str]] = None, values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> CorrelationRule: - """Creating an correlation_rule object with altered fields from a default correlation_rule yml structure. + """Creating a correlation_rule object with altered fields from a default correlation_rule yml structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The correlation_rule object. """ yml_content = load_yaml("correlation_rule_test.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_correlation_rule(name="correlation_rule", content=yml_content) return cast( CorrelationRule, BaseContent.from_path(Path(pack.correlation_rules[0].path)) @@ -154,6 +166,7 @@ def create_playbook_object( values: Optional[List[Any]] = None, pack_info: Optional[Dict[str, Any]] = None, readme_content: Optional[str] = None, + repo=get_temp_repo(), ) -> Playbook: """Creating a playbook object with altered fields from a default playbook yml structure. @@ -162,14 +175,17 @@ def create_playbook_object( values (Optional[List[Any]]): The values to update. pack_info (Optional[List[str]]): The playbook's pack name. readme_content (Optional[List[Any]]): The playbook's readme. + repo: Containing repository object. New temporary repo is created by default. Returns: The playbook object. """ yml_content = load_yaml("playbook.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() if pack_info: - pack.set_data(**pack_info) + pack = repo.create_pack(**pack_info) + else: + pack = repo.create_pack() + additional_params = {} if readme_content is not None: @@ -187,6 +203,7 @@ def create_test_playbook_object( values: Optional[List[Any]] = None, pack_info: Optional[Dict[str, Any]] = None, readme_content: Optional[str] = None, + repo=get_temp_repo(), ) -> TestPlaybook: """Creating a test playbook object with altered fields from a default test playbook yml structure. @@ -195,14 +212,17 @@ def create_test_playbook_object( values (Optional[List[Any]]): The values to update. pack_info (Optional[List[str]]): The playbook's pack name. readme_content (Optional[List[Any]]): The playbook's readme. + repo: Containing repository object. New temporary repo is created by default. + Returns: The test playbook object. """ yml_content = load_yaml("playbook.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() if pack_info: - pack.set_data(**pack_info) + pack = repo.create_pack(**pack_info) + else: + pack = repo.create_pack() additional_params = {} if readme_content is not None: @@ -238,19 +258,20 @@ def create_modeling_rule_object( values: Optional[List[Any]] = None, rules: Optional[str] = None, schema: Optional[dict] = None, + repo=get_temp_repo(), ) -> ModelingRule: """Creating an modeling_rule object with altered fields from a default modeling_rule yml structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. - + repo: Containing repository object. New temporary repo is created by default. Returns: The modeling_rule object. """ yml_content = load_yaml("modeling_rule.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_modeling_rule(yml=yml_content, rules=rules, schema=schema) return cast(ModelingRule, BaseContent.from_path(Path(pack.modeling_rules[0].path))) @@ -258,19 +279,20 @@ def create_modeling_rule_object( def create_ps_integration_object( paths: Optional[List[str]] = None, values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Integration: """Creating an integration object with altered fields from a default integration yml structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. - + repo: Containing repository object. New temporary repo is created by default. Returns: The integration object. """ yml_content = load_yaml("ps_integration.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() integration = pack.create_integration(yml=yml_content) integration.code = File( Path(f"{integration.path}/integration_0.ps1"), integration.repo_path @@ -287,13 +309,15 @@ def create_script_object( name: Optional[str] = None, code: Optional[str] = None, test_code: Optional[str] = None, + repo=get_temp_repo(), ) -> Script: - """Creating an script object with altered fields from a default script yml structure. + """Creating a script object with altered fields from a default script yml structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. - pack_name (str): The name of the pack that the script will be inside of + pack_name (str): The name of the pack that the script will be inside of. + repo: Containing repository object. New temporary repo is created by default. Returns: The script object. @@ -304,7 +328,7 @@ def create_script_object( yml_content = load_yaml("script.yml") update_keys(yml_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() if pack_info: pack.set_data(**pack_info) if readme_content is not None: @@ -328,12 +352,14 @@ def create_pack_object( name: Optional[str] = None, release_note_content: Optional[str] = None, bc_release_note_content: Optional[List[Dict[str, str]]] = None, + repo=get_temp_repo(), ) -> Pack: """Creating an pack object with altered fields from a default pack_metadata json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The pack_metadata object. @@ -341,7 +367,7 @@ def create_pack_object( json_content = load_json("pack_metadata.json") update_keys(json_content, paths, values) remove_fields_from_dict(json_content, fields_to_delete) - pack = REPO.create_pack(name) + pack = repo.create_pack(name) pack_path = Path(pack.path) if release_note_content is not None: @@ -385,7 +411,8 @@ def create_pack_object( def remove_fields_from_dict( - json_content: dict, fields_to_delete: Optional[List[str]] = None + json_content: dict, + fields_to_delete: Optional[List[str]] = None, ): if fields_to_delete: for field in fields_to_delete: @@ -393,57 +420,66 @@ def remove_fields_from_dict( def create_classifier_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Classifier: """Creating an classifier object with altered fields from a default classifier json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The classifier object. """ json_content = load_json("classifier.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_classifier(name="test_classifier", content=json_content) return cast(Classifier, BaseContent.from_path(Path(pack.classifiers[0].path))) def create_list_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> ListObject: """Creating an list object with altered fields from a default list json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The list object. """ json_content = load_json("list.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_list(name="list", content=json_content) return cast(ListObject, BaseContent.from_path(Path(pack.lists[0].path))) def create_job_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Job: """Creating an job object with altered fields from a default job json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The job object. """ json_content = {} - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_job(name="job", is_feed=True) if paths and values: with open(pack.jobs[0].path) as f: @@ -455,39 +491,45 @@ def create_job_object( def create_dashboard_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Dashboard: """Creating an dashboard object with altered fields from a default dashboard json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The dashboard object. """ json_content = load_json("dashboard.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_dashboard(name="dashboard", content=json_content) return cast(Dashboard, BaseContent.from_path(Path(pack.dashboards[0].path))) def create_incident_type_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> IncidentType: """Creating an incident_type object with altered fields from a default incident_type json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The incident_type object. """ json_content = load_json("incident_type.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_incident_type(name="incident_type", content=json_content) return cast(IncidentType, BaseContent.from_path(Path(pack.incident_types[0].path))) @@ -496,19 +538,21 @@ def create_incident_field_object( paths: Optional[List[str]] = None, values: Optional[List[Any]] = None, pack_info: Optional[Dict[str, Any]] = None, + repo=get_temp_repo(), ) -> IncidentField: """Creating an incident_field object with altered fields from a default incident_field json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The incident_field object. """ json_content = load_json("incident_field.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() if pack_info: pack.set_data(**pack_info) pack.create_incident_field(name="incident_field", content=json_content) @@ -518,58 +562,67 @@ def create_incident_field_object( def create_report_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Report: """Creating an report object with altered fields from a default report json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The report object. """ json_content = load_json("report.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_report(name="report", content=json_content) return cast(Report, BaseContent.from_path(Path(pack.reports[0].path))) def create_xsiam_report_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> XSIAMReport: """Creating an xsiam_report object with altered fields from a default xsiam_report json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The xsiam_report object. """ json_content = load_json("xsiam_report.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_xsiam_report(name="xsiam_report", content=json_content) return cast(XSIAMReport, BaseContent.from_path(Path(pack.xsiam_reports[0].path))) def create_xsiam_dashboard_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> XSIAMDashboard: """Creating an xsiam_dashboard object with altered fields from a default xsiam_dashboard json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The xsiam_dashboard object. """ json_content = load_json("xsiam_dashboard.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_xsiam_dashboard(name="xsiam_dashboard", content=json_content) return cast( XSIAMDashboard, BaseContent.from_path(Path(pack.xsiam_dashboards[0].path)) @@ -581,6 +634,7 @@ def create_xdrc_template_object( json_values: Optional[List[Any]] = None, yml_paths: Optional[List[str]] = None, yml_values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> XDRCTemplate: """Creating an xdrc_template object with altered fields from a default xdrc_template json and yml structures. @@ -589,6 +643,7 @@ def create_xdrc_template_object( json_values (Optional[List[Any]]): The values to update for the json file. yml_paths (Optional[List[str]]): The keys to update for the yml file. yml_values (Optional[List[Any]]): The values to update for the yml file. + repo: Containing repository object. New temporary repo is created by default. Returns: The xdrc_template object. @@ -597,7 +652,7 @@ def create_xdrc_template_object( update_keys(json_content, json_paths, json_values) yml_content = load_yaml("xdrc_template.yml") update_keys(json_content, yml_paths, yml_values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_xdrc_template( name="xdrc_template", json_content=json_content, yaml_content=yml_content ) @@ -609,6 +664,7 @@ def create_assets_modeling_rule_object( json_values: Optional[List[Any]] = None, yml_paths: Optional[List[str]] = None, yml_values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> AssetsModelingRule: """Creating an assets_modeling_rule object with altered fields from a default assets_modeling_rule json and yml structures. @@ -617,6 +673,7 @@ def create_assets_modeling_rule_object( json_values (Optional[List[Any]]): The values to update for the json file. yml_paths (Optional[List[str]]): The keys to update for the yml file. yml_values (Optional[List[Any]]): The values to update for the yml file. + repo: Containing repository object. New temporary repo is created by default. Returns: The assets_modeling_rule object. @@ -625,7 +682,7 @@ def create_assets_modeling_rule_object( update_keys(json_content, json_paths, json_values) yml_content = load_yaml("assets_modeling_rule.yml") update_keys(json_content, yml_paths, yml_values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_assets_modeling_rule( name="assets_modeling_rule", schema=json_content, yml=yml_content ) @@ -636,93 +693,108 @@ def create_assets_modeling_rule_object( def create_trigger_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Trigger: """Creating an trigger object with altered fields from a default trigger json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The trigger object. """ json_content = load_json("trigger.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_trigger(name="trigger", content=json_content) return cast(Trigger, BaseContent.from_path(Path(pack.triggers[0].path))) def create_layout_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Layout: """Creating an layout object with altered fields from a default layout json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The layout object. """ json_content = load_json("layoutscontainer.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_layout(name="layout", content=json_content) return cast(Layout, BaseContent.from_path(Path(pack.layouts[0].path))) def create_widget_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Widget: """Creating an widget object with altered fields from a default widget json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The widget object. """ json_content = load_json("widget.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_widget(name="widget", content=json_content) return cast(Widget, BaseContent.from_path(Path(pack.widgets[0].path))) def create_indicator_field_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> IndicatorField: """Creating an indicator_field object with altered fields from a default indicator_field json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The indicator_field object. """ json_content = load_json("indicator_field.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_indicator_field(name="indicator_field", content=json_content) return cast( IndicatorField, BaseContent.from_path(Path(pack.indicator_fields[0].path)) ) -def create_wizard_object(dict_to_update: Optional[Any] = None) -> Wizard: +def create_wizard_object( + dict_to_update: Optional[Any] = None, repo: Repo = get_temp_repo() +) -> Wizard: """Creating a wizard object with altered fields from a default wizard json structure. Args: dict_to_update (Optional[Any], optional): The dict to update into the wizards dict. + repo: Containing repository object. New temporary repo is created by default. Returns: The wizard object. """ - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_wizard(name="test_wizard") if dict_to_update: pack.wizards[0].update(dict_to_update) @@ -730,20 +802,23 @@ def create_wizard_object(dict_to_update: Optional[Any] = None) -> Wizard: def create_generic_definition_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> GenericDefinition: """Creating an generic_definition object with altered fields from a default generic_definition json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The generic_definition object. """ json_content = load_json("generic_definition.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_generic_definition(name="generic_definition", content=json_content) return cast( GenericDefinition, BaseContent.from_path(Path(pack.generic_definitions[0].path)) @@ -751,58 +826,67 @@ def create_generic_definition_object( def create_generic_field_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> GenericField: """Creating an generic_field object with altered fields from a default generic_field json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The generic_field object. """ json_content = load_json("generic_field.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_generic_field(name="generic_field", content=json_content) return cast(GenericField, BaseContent.from_path(Path(pack.generic_fields[0].path))) def create_generic_type_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> GenericType: """Creating an generic_type object with altered fields from a default generic_type json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The generic_type object. """ json_content = load_json("generic_type.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_generic_type(name="generic_type", content=json_content) return cast(GenericType, BaseContent.from_path(Path(pack.generic_types[0].path))) def create_generic_module_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> GenericModule: """Creating an generic_module object with altered fields from a default generic_module json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The generic_module object. """ json_content = load_json("generic_module.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_generic_module(name="generic_module", content=json_content) return cast( GenericModule, BaseContent.from_path(Path(pack.generic_modules[0].path)) @@ -810,58 +894,67 @@ def create_generic_module_object( def create_incoming_mapper_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> Mapper: """Creating an incoming_mapper object with altered fields from a default incoming_mapper json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The incoming_mapper object. """ json_content = load_json("incoming_mapper.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_mapper(name="incoming_mapper", content=json_content) return cast(Mapper, BaseContent.from_path(Path(pack.mappers[0].path))) def create_outgoing_mapper_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ): """Creating an outgoing_mapper object with altered fields from a default outgoing_mapper json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The outgoing_mapper object. """ json_content = load_json("outgoing_mapper.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_mapper(name="outgoing_mapper", content=json_content) return BaseContent.from_path(Path(pack.mappers[0].path)) def create_indicator_type_object( - paths: Optional[List[str]] = None, values: Optional[List[Any]] = None + paths: Optional[List[str]] = None, + values: Optional[List[Any]] = None, + repo=get_temp_repo(), ) -> IndicatorType: """Creating an indicator_type object with altered fields from a default indicator_type json structure. Args: paths (Optional[List[str]]): The keys to update. values (Optional[List[Any]]): The values to update. + repo: Containing repository object. New temporary repo is created by default. Returns: The indicator_type object. """ json_content = load_json("indicator_type.json") update_keys(json_content, paths, values) - pack = REPO.create_pack() + pack = repo.create_pack() pack.create_indicator_type(name="indicator_type", content=json_content) return cast( IndicatorType, BaseContent.from_path(Path(pack.indicator_types[0].path)) diff --git a/demisto_sdk/commands/validate/tests/validators_test.py b/demisto_sdk/commands/validate/tests/validators_test.py index 3913c3d8570..b1ba08e0755 100644 --- a/demisto_sdk/commands/validate/tests/validators_test.py +++ b/demisto_sdk/commands/validate/tests/validators_test.py @@ -14,7 +14,7 @@ ExecutionMode, GitStatuses, ) -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.content_graph.common import ContentType @@ -31,6 +31,7 @@ create_integration_object, create_pack_object, create_script_object, + get_temp_repo, ) from demisto_sdk.commands.validate.validate_manager import ValidateManager from demisto_sdk.commands.validate.validation_results import ResultWriter @@ -65,7 +66,7 @@ INTEGRATION = create_integration_object() INTEGRATION.path = Path( - f"{CONTENT_PATH}/Packs/pack_0/Integrations/integration_0/integration_0.yml" + f"{ContentPaths.CONTENT_PATH}/Packs/pack_0/Integrations/integration_0/integration_0.yml" ) @@ -283,91 +284,67 @@ def test_gather_validations_from_conf( assert results.support_level_dict == expected_results.support_level_dict -@pytest.mark.parametrize( - "results, fixing_results, expected_results", - [ - ( - [ - ValidationResult( - validator=IDNameValidator(), - message="", - content_object=INTEGRATION, - ) - ], - [], - { - "validations": [ - { - "file path": str(INTEGRATION.path), - "error code": "BA101", - "message": "", - } - ], - "fixed validations": [], - "invalid content items": [], - "Validations that caught exceptions": [], - }, - ), - ( - [], - [], - { - "validations": [], - "fixed validations": [], - "invalid content items": [], - "Validations that caught exceptions": [], - }, - ), - ( - [ - ValidationResult( - validator=IDNameValidator(), - message="", - content_object=INTEGRATION, - ) - ], - [ - FixResult( - validator=IDNameValidator(), - message="Fixed this issue", - content_object=INTEGRATION, - ) - ], +@pytest.fixture +def temp_integration(): + """Fixture to create a temporary integration object for testing.""" + temp_repo = get_temp_repo() + return create_integration_object(repo=temp_repo) + + +def test_write_results_case_1(temp_integration): + """ + Test case 1: One validation result, no fixes. + """ + results = [ + ValidationResult( + validator=IDNameValidator(), + message="", + content_object=temp_integration, + ) + ] + fixing_results = [] + expected_results = { + "validations": [ { - "validations": [ - { - "file path": str(INTEGRATION.path), - "error code": "BA101", - "message": "", - } - ], - "fixed validations": [ - { - "file path": str(INTEGRATION.path), - "error code": "BA101", - "message": "Fixed this issue", - } - ], - "invalid content items": [], - "Validations that caught exceptions": [], - }, - ), - ], -) -def test_write_results_to_json_file(results, fixing_results, expected_results): + "file path": str( + temp_integration.path.relative_to(ContentPaths.CONTENT_PATH) + ), + "error code": "BA101", + "message": "", + } + ], + "fixed validations": [], + "invalid content items": [], + "Validations that caught exceptions": [], + } + + with tempfile.NamedTemporaryFile( + mode="w", delete=False, suffix=".json" + ) as temp_file: + temp_file_path = temp_file.name + validation_results = ResultWriter(json_file_path=temp_file_path) + validation_results.validation_results = results + validation_results.fixing_results = fixing_results + validation_results.write_results_to_json_file() + + with open(temp_file_path, "r") as file: + loaded_data = json.load(file) + assert loaded_data == expected_results + + +def test_write_results_case_2(temp_integration): """ - Given - results and fixing_results lists. - - Case 1: One validation result. - - Case 2: Both lists are empty. - - Case 3: Both lists has one item. - When - - Calling the write_results_to_json_file function. - Then - - Case 1: Make sure the results hold both list where the fixing results is empty. - - Case 2: Make sure the results hold both list where both are empty. - - Case 3: Make sure the results hold both list where both hold 1 result each. + Test case 2: Empty results and fixing results. """ + results = [] + fixing_results = [] + expected_results = { + "validations": [], + "fixed validations": [], + "invalid content items": [], + "Validations that caught exceptions": [], + } + with tempfile.NamedTemporaryFile( mode="w", delete=False, suffix=".json" ) as temp_file: @@ -376,94 +353,167 @@ def test_write_results_to_json_file(results, fixing_results, expected_results): validation_results.validation_results = results validation_results.fixing_results = fixing_results validation_results.write_results_to_json_file() + with open(temp_file_path, "r") as file: loaded_data = json.load(file) assert loaded_data == expected_results -@pytest.mark.parametrize( - "only_throw_warnings, results, expected_exit_code, expected_warnings_call_count, expected_error_call_count, expected_error_code_in_warnings, expected_error_code_in_errors", - [ - ( - ["BA101"], - [ - ValidationResult( - validator=IDNameValidator(), - message="", - content_object=INTEGRATION, - ) - ], - 0, - 1, - 0, - ["BA101"], - [], - ), - ( - [], - [ - ValidationResult( - validator=IDNameValidator(), - message="", - content_object=INTEGRATION, - ) - ], - 1, - 0, - 1, - [], - ["BA101"], - ), - ( - ["BC100"], - [ - ValidationResult( - validator=IDNameValidator(), - message="", - content_object=INTEGRATION, +def test_write_results_case_3(temp_integration): + """ + Test case 3: One validation result and one fix result. + """ + results = [ + ValidationResult( + validator=IDNameValidator(), + message="", + content_object=temp_integration, + ) + ] + fixing_results = [ + FixResult( + validator=IDNameValidator(), + message="Fixed this issue", + content_object=temp_integration, + ) + ] + expected_results = { + "validations": [ + { + "file path": str( + temp_integration.path.relative_to(ContentPaths.CONTENT_PATH) ), - ValidationResult( - validator=BreakingBackwardsSubtypeValidator(), - message="", - content_object=INTEGRATION, + "error code": "BA101", + "message": "", + } + ], + "fixed validations": [ + { + "file path": str( + temp_integration.path.relative_to(ContentPaths.CONTENT_PATH) ), - ], - 1, - 1, - 1, - ["BC100"], - ["BA101"], - ), - ], -) -def test_post_results( - mocker, - only_throw_warnings, - results, - expected_exit_code, - expected_warnings_call_count, - expected_error_call_count, - expected_error_code_in_warnings, - expected_error_code_in_errors, - caplog, -): + "error code": "BA101", + "message": "Fixed this issue", + } + ], + "invalid content items": [], + "Validations that caught exceptions": [], + } + + with tempfile.NamedTemporaryFile( + mode="w", delete=False, suffix=".json" + ) as temp_file: + temp_file_path = temp_file.name + validation_results = ResultWriter(json_file_path=temp_file_path) + validation_results.validation_results = results + validation_results.fixing_results = fixing_results + validation_results.write_results_to_json_file() + + with open(temp_file_path, "r") as file: + loaded_data = json.load(file) + assert loaded_data == expected_results + + +def test_post_results_case_1(temp_integration, caplog): """ - Given - an only_throw_warnings list, and a list of results. - - Case 1: One failed validation with its error_code in the only_throw_warnings list. - - Case 2: One failed validation with its error_code not in the only_throw_warnings list. - - Case 3: One failed validation with its error_code in the only_throw_warnings list and one failed validation with its error_code not in the only_throw_warnings list. - When - - Calling the post_results function. - Then - - Make sure the error and warning loggers was called the correct number of times with the right error codes, and that the exit code was calculated correctly. - - Case 1: Make sure the exit_code is 0 (success), and that the warning logger was called once with 'BA101' and the error logger wasn't called. - - Case 2: Make sure the exit_code is 1 (failure), and that the error logger was called once with 'BA101' and the warning logger wasn't called. - - Case 3: Make sure the exit_code is 1 (failure), and that the error logger was called once with 'BA101' and the warning logger was called once with 'BC100' + Case 1: One failed validation with its error_code in the only_throw_warnings list. """ + only_throw_warnings = ["BA101"] + results = [ + ValidationResult( + validator=IDNameValidator(), + message="", + content_object=temp_integration, + ) + ] + expected_exit_code = 0 + expected_warnings_call_count = 1 + expected_error_call_count = 0 + expected_error_code_in_warnings = ["BA101"] + expected_error_code_in_errors = [] + validation_results = ResultWriter() validation_results.validation_results = results exit_code = validation_results.post_results(only_throw_warning=only_throw_warnings) + + assert exit_code == expected_exit_code + + log_by_level = map_reduce(caplog.records, lambda log: log.levelno) + warnings = log_by_level.get(30, ()) + assert len(warnings) == expected_warnings_call_count + for code in expected_error_code_in_warnings: + assert code in " ".join({log.message for log in warnings}) + + errors = log_by_level.get(40, ()) + assert len(errors) == expected_error_call_count + for code in expected_error_code_in_errors: + assert code in " ".join({log.message for log in errors}) + + +def test_post_results_case_2(temp_integration, caplog): + """ + Case 2: One failed validation with its error_code not in the only_throw_warnings list. + """ + only_throw_warnings = [] + results = [ + ValidationResult( + validator=IDNameValidator(), + message="", + content_object=temp_integration, + ) + ] + expected_exit_code = 1 + expected_warnings_call_count = 0 + expected_error_call_count = 1 + expected_error_code_in_warnings = [] + expected_error_code_in_errors = ["BA101"] + + validation_results = ResultWriter() + validation_results.validation_results = results + exit_code = validation_results.post_results(only_throw_warning=only_throw_warnings) + + assert exit_code == expected_exit_code + + log_by_level = map_reduce(caplog.records, lambda log: log.levelno) + warnings = log_by_level.get(30, ()) + assert len(warnings) == expected_warnings_call_count + for code in expected_error_code_in_warnings: + assert code in " ".join({log.message for log in warnings}) + + errors = log_by_level.get(40, ()) + assert len(errors) == expected_error_call_count + for code in expected_error_code_in_errors: + assert code in " ".join({log.message for log in errors}) + + +def test_post_results_case_3(temp_integration, caplog): + """ + Case 3: One failed validation with its error_code in the only_throw_warnings list + and one failed validation with its error_code not in the only_throw_warnings list. + """ + only_throw_warnings = ["BC100"] + results = [ + ValidationResult( + validator=IDNameValidator(), + message="", + content_object=temp_integration, + ), + ValidationResult( + validator=BreakingBackwardsSubtypeValidator(), + message="", + content_object=temp_integration, + ), + ] + expected_exit_code = 1 + expected_warnings_call_count = 1 + expected_error_call_count = 1 + expected_error_code_in_warnings = ["BC100"] + expected_error_code_in_errors = ["BA101"] + + validation_results = ResultWriter() + validation_results.validation_results = results + exit_code = validation_results.post_results(only_throw_warning=only_throw_warnings) + assert exit_code == expected_exit_code log_by_level = map_reduce(caplog.records, lambda log: log.levelno) @@ -502,7 +552,10 @@ def test_should_run(validator, expected_results): - Case 3: Should return False. """ assert expected_results == validator.should_run( - INTEGRATION, [], {}, running_execution_mode=ExecutionMode.USE_GIT + create_integration_object(), + [], + {}, + running_execution_mode=ExecutionMode.USE_GIT, ) diff --git a/demisto_sdk/commands/validate/validators/GR_validators/GR101_is_using_invalid_from_version.py b/demisto_sdk/commands/validate/validators/GR_validators/GR101_is_using_invalid_from_version.py index a85f7ecb7a3..93c542a2f0a 100644 --- a/demisto_sdk/commands/validate/validators/GR_validators/GR101_is_using_invalid_from_version.py +++ b/demisto_sdk/commands/validate/validators/GR_validators/GR101_is_using_invalid_from_version.py @@ -3,7 +3,7 @@ from abc import ABC from typing import Iterable, List, Union -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.objects import ( CaseField, CaseLayout, @@ -98,7 +98,7 @@ def obtain_invalid_content_items_using_graph( [] if validate_all_files else [ - str(content_item.path.relative_to(CONTENT_PATH)) + str(content_item.path.relative_to(ContentPaths.CONTENT_PATH)) for content_item in content_items ] ) diff --git a/demisto_sdk/commands/validate/validators/GR_validators/GR103_is_using_unknown_content.py b/demisto_sdk/commands/validate/validators/GR_validators/GR103_is_using_unknown_content.py index 98b4d9d2bd2..2393c662cb1 100644 --- a/demisto_sdk/commands/validate/validators/GR_validators/GR103_is_using_unknown_content.py +++ b/demisto_sdk/commands/validate/validators/GR_validators/GR103_is_using_unknown_content.py @@ -3,7 +3,7 @@ from abc import ABC from typing import Iterable, List, Union -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule @@ -91,7 +91,7 @@ def obtain_invalid_content_items_using_graph( results: List[ValidationResult] = [] file_paths_to_validate = ( [ - str(content_item.path.relative_to(CONTENT_PATH)) + str(content_item.path.relative_to(ContentPaths.CONTENT_PATH)) for content_item in content_items ] if not validate_all_files diff --git a/demisto_sdk/commands/validate/validators/GR_validators/GR104_is_pack_display_name_already_exists.py b/demisto_sdk/commands/validate/validators/GR_validators/GR104_is_pack_display_name_already_exists.py index 941a5bfa465..ca277e47369 100644 --- a/demisto_sdk/commands/validate/validators/GR_validators/GR104_is_pack_display_name_already_exists.py +++ b/demisto_sdk/commands/validate/validators/GR_validators/GR104_is_pack_display_name_already_exists.py @@ -3,7 +3,7 @@ from abc import ABC from typing import Iterable, List -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.objects.pack import Pack from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType from demisto_sdk.commands.validate.validators.base_validator import ( @@ -31,7 +31,7 @@ def obtain_invalid_content_items_using_graph( self, content_items: Iterable[ContentTypes], validate_all_files: bool ) -> List[ValidationResult]: file_paths_to_objects = { - str(content_item.path.relative_to(CONTENT_PATH)): content_item + str(content_item.path.relative_to(ContentPaths.CONTENT_PATH)): content_item for content_item in content_items } content_id_to_objects = {item.object_id: item for item in content_items} # type: ignore[attr-defined] diff --git a/demisto_sdk/commands/validate/validators/GR_validators/GR105_duplicate_content_id.py b/demisto_sdk/commands/validate/validators/GR_validators/GR105_duplicate_content_id.py index 9776e29e45d..cc6d10336eb 100644 --- a/demisto_sdk/commands/validate/validators/GR_validators/GR105_duplicate_content_id.py +++ b/demisto_sdk/commands/validate/validators/GR_validators/GR105_duplicate_content_id.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import Iterable, List, Union -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule @@ -93,7 +93,7 @@ def obtain_invalid_content_items_using_graph( [] if validate_all_files else [ - str(content_item.path.relative_to(CONTENT_PATH)) + str(content_item.path.relative_to(ContentPaths.CONTENT_PATH)) for content_item in content_items ] ) @@ -102,7 +102,7 @@ def obtain_invalid_content_items_using_graph( validator=self, message=self.error_message.format( content_item.object_id, - Path(duplicate.path).relative_to(CONTENT_PATH), # type: ignore[attr-defined] + Path(duplicate.path).relative_to(ContentPaths.CONTENT_PATH), # type: ignore[attr-defined] ), content_object=content_item, # type: ignore[arg-type] ) diff --git a/demisto_sdk/commands/validate/validators/GR_validators/GR106_is_testplaybook_in_use.py b/demisto_sdk/commands/validate/validators/GR_validators/GR106_is_testplaybook_in_use.py index 93874ce72ff..0cd4dd93cf3 100644 --- a/demisto_sdk/commands/validate/validators/GR_validators/GR106_is_testplaybook_in_use.py +++ b/demisto_sdk/commands/validate/validators/GR_validators/GR106_is_testplaybook_in_use.py @@ -3,7 +3,7 @@ from abc import ABC from typing import Iterable, List -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.objects.conf_json import ConfJSON from demisto_sdk.commands.content_graph.objects.test_playbook import TestPlaybook from demisto_sdk.commands.validate.validators.base_validator import ( @@ -31,7 +31,7 @@ class IsTestPlaybookInUseValidator(BaseValidator[ContentTypes], ABC): def obtain_invalid_content_items_using_graph( self, content_items: Iterable[ContentTypes], validate_all_files: bool ) -> List[ValidationResult]: - conf_data = ConfJSON.from_path(CONTENT_PATH / "Tests/conf.json") + conf_data = ConfJSON.from_path(ContentPaths.CONTENT_PATH / "Tests/conf.json") test_playbooks_ids_to_skip = list( set(conf_data.skipped_tests.keys()) | set(conf_data.reputation_tests) ) diff --git a/demisto_sdk/commands/validate/validators/GR_validators/GR107_is_deprecated_content_item_in_usage.py b/demisto_sdk/commands/validate/validators/GR_validators/GR107_is_deprecated_content_item_in_usage.py index 4ead651f044..6210957d1e3 100644 --- a/demisto_sdk/commands/validate/validators/GR_validators/GR107_is_deprecated_content_item_in_usage.py +++ b/demisto_sdk/commands/validate/validators/GR_validators/GR107_is_deprecated_content_item_in_usage.py @@ -3,7 +3,7 @@ from abc import ABC from typing import Iterable, List, Union -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule @@ -95,7 +95,7 @@ def obtain_invalid_content_items_using_graph( [] if validate_all_files else [ - str(content_item.path.relative_to(CONTENT_PATH)) + str(content_item.path.relative_to(ContentPaths.CONTENT_PATH)) for content_item in content_items ] ) @@ -106,7 +106,9 @@ def obtain_invalid_content_items_using_graph( deprecated_item_type=item.deprecated_item_type, deprecated_item=item.deprecated_item_id, using_deprecated_item=str( - item_using_deprecated.path.relative_to(CONTENT_PATH) + item_using_deprecated.path.relative_to( + ContentPaths.CONTENT_PATH + ) ), ), content_object=item_using_deprecated, diff --git a/demisto_sdk/commands/validate/validators/RM_validators/RM114_is_image_exists_in_readme.py b/demisto_sdk/commands/validate/validators/RM_validators/RM114_is_image_exists_in_readme.py index 9610840b862..27d65ecd577 100644 --- a/demisto_sdk/commands/validate/validators/RM_validators/RM114_is_image_exists_in_readme.py +++ b/demisto_sdk/commands/validate/validators/RM_validators/RM114_is_image_exists_in_readme.py @@ -27,7 +27,10 @@ class IsImageExistsInReadmeValidator(BaseValidator[ContentTypes]): description = ( "Validate that images placed under doc_files folder and used in README exist." ) - error_message = "The following images do not exist or have additional characters present in their declaration within the README: {0}" + error_message = ( + "The following images do not exist or have additional characters present in their declaration " + "within the README: {0}" + ) rationale = "Missing images are not shown in rendered markdown" related_field = "" is_auto_fixable = False diff --git a/demisto_sdk/commands/validate/validators/SC_validators/SC109_script_name_is_not_unique_validator.py b/demisto_sdk/commands/validate/validators/SC_validators/SC109_script_name_is_not_unique_validator.py index 24bad9b77a1..57baa11d53e 100644 --- a/demisto_sdk/commands/validate/validators/SC_validators/SC109_script_name_is_not_unique_validator.py +++ b/demisto_sdk/commands/validate/validators/SC_validators/SC109_script_name_is_not_unique_validator.py @@ -3,7 +3,7 @@ from abc import ABC from typing import Iterable, List -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.tools import replace_incident_to_alert from demisto_sdk.commands.content_graph.objects.script import Script from demisto_sdk.commands.validate.validators.base_validator import ( @@ -38,7 +38,7 @@ def obtain_invalid_content_items_using_graph( when the script name included `alert`. """ file_paths_to_objects = { - str(content_item.path.relative_to(CONTENT_PATH)): content_item + str(content_item.path.relative_to(ContentPaths.CONTENT_PATH)): content_item for content_item in content_items } query_list = list(file_paths_to_objects) if not validate_all_files else [] diff --git a/demisto_sdk/commands/validate/validators/base_validator.py b/demisto_sdk/commands/validate/validators/base_validator.py index 4bf68b6bc52..c1c3dd86f72 100644 --- a/demisto_sdk/commands/validate/validators/base_validator.py +++ b/demisto_sdk/commands/validate/validators/base_validator.py @@ -15,7 +15,7 @@ from pydantic import BaseModel from demisto_sdk.commands.common.constants import ExecutionMode, GitStatuses -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.common.tools import is_abstract_class from demisto_sdk.commands.content_graph.commands.update import update_content_graph @@ -226,13 +226,15 @@ class BaseResult(BaseModel): def format_readable_message(self): path: Path = self.content_object.path if path.is_absolute(): - path = path.relative_to(CONTENT_PATH) + path = path.relative_to(ContentPaths.CONTENT_PATH) return f"{str(path)}: [{self.validator.error_code}] - {self.message}" @property def format_json_message(self): return { - "file path": str(self.content_object.path.relative_to(CONTENT_PATH)), + "file path": str( + self.content_object.path.relative_to(ContentPaths.CONTENT_PATH) + ), "error code": self.validator.error_code, "message": self.message, } @@ -293,13 +295,13 @@ class InvalidContentItemResult(BaseResult, BaseModel): def format_readable_message(self): path: Path = self.path if path.is_absolute(): - path = path.relative_to(CONTENT_PATH) + path = path.relative_to(ContentPaths.CONTENT_PATH) return f"{path}: [{self.error_code}] - {self.message}" @property def format_json_message(self): return { - "file path": str(self.path.relative_to(CONTENT_PATH)), + "file path": str(self.path.relative_to(ContentPaths.CONTENT_PATH)), "error code": self.error_code, "message": self.message, } diff --git a/demisto_sdk/commands/xsoar_linter/xsoar_linter.py b/demisto_sdk/commands/xsoar_linter/xsoar_linter.py index eebd5c98598..6711bfca084 100644 --- a/demisto_sdk/commands/xsoar_linter/xsoar_linter.py +++ b/demisto_sdk/commands/xsoar_linter/xsoar_linter.py @@ -9,7 +9,7 @@ from packaging.version import Version -from demisto_sdk.commands.common.content_constant_paths import PYTHONPATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.cpu_count import cpu_count from demisto_sdk.commands.common.logger import logger from demisto_sdk.commands.content_graph.objects import Integration, Script @@ -122,7 +122,9 @@ def build_xsoar_linter_env_var(integration_script: IntegrationScript) -> dict: xsoar_linter_env["commands"] = ",".join( [command.name for command in integration_script.commands] ) - xsoar_linter_env["PYTHONPATH"] = ":".join(str(path) for path in PYTHONPATH) + xsoar_linter_env["PYTHONPATH"] = ":".join( + str(path) for path in ContentPaths.PYTHONPATH + ) return xsoar_linter_env diff --git a/demisto_sdk/scripts/merge_pytest_reports.py b/demisto_sdk/scripts/merge_pytest_reports.py index 3aee643ffea..bebb49d71f2 100644 --- a/demisto_sdk/scripts/merge_pytest_reports.py +++ b/demisto_sdk/scripts/merge_pytest_reports.py @@ -6,10 +6,10 @@ import coverage from junitparser import JUnitXml -from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger, logging_setup -PRECOMMIT_FOLDER = CONTENT_PATH / ".pre-commit" +PRECOMMIT_FOLDER = ContentPaths.CONTENT_PATH / ".pre-commit" def fix_coverage_report_path(coverage_file: Path) -> bool: @@ -41,7 +41,7 @@ def fix_coverage_report_path(coverage_file: Path) -> bool: continue file = Path(file).relative_to("/src") if ( - not (CONTENT_PATH / file).exists() + not (ContentPaths.CONTENT_PATH / file).exists() or file.parent.name != file.stem # For example, in `QRadar_v3` directory we only care for `QRadar_v3.py` ): @@ -52,7 +52,7 @@ def fix_coverage_report_path(coverage_file: Path) -> bool: else: cursor.execute( "UPDATE file SET path = ? WHERE id = ?", - (str(CONTENT_PATH / file), id_), + (str(ContentPaths.CONTENT_PATH / file), id_), ) sql_connection.commit() logger.debug("Done editing coverage report") @@ -66,7 +66,7 @@ def fix_coverage_report_path(coverage_file: Path) -> bool: def merge_coverage_report(): - coverage_path = CONTENT_PATH / ".coverage" + coverage_path = ContentPaths.CONTENT_PATH / ".coverage" coverage_path.unlink(missing_ok=True) cov = coverage.Coverage(data_file=coverage_path) # this is the path where the pre-commit created the coverage files @@ -93,7 +93,7 @@ def merge_junit_reports(): report = reports[0] for rep in reports[1:]: report += rep - report.write(str(CONTENT_PATH / ".report_pytest.xml")) + report.write(str(ContentPaths.CONTENT_PATH / ".report_pytest.xml")) for file in report_files: Path(file).unlink(missing_ok=True) logger.info("Junit report was successfully merged.") diff --git a/demisto_sdk/scripts/validate_conf_json.py b/demisto_sdk/scripts/validate_conf_json.py index f7022001f30..e1b545ec5b3 100644 --- a/demisto_sdk/scripts/validate_conf_json.py +++ b/demisto_sdk/scripts/validate_conf_json.py @@ -2,7 +2,7 @@ from pathlib import Path from typing import List, Optional, cast -from demisto_sdk.commands.common.content_constant_paths import CONF_PATH +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.logger import logger, logging_setup from demisto_sdk.commands.common.tools import string_to_bool from demisto_sdk.commands.content_graph.commands.update import update_content_graph @@ -15,7 +15,7 @@ class ConfJsonValidator: def __init__( self, - conf_json_path: Path = CONF_PATH, + conf_json_path: Path = ContentPaths.CONF_PATH, graph: Optional[ContentGraphInterface] = None, # Pass None to generate ) -> None: self._conf_path = conf_json_path diff --git a/demisto_sdk/tests/integration_tests/format_integration_test.py b/demisto_sdk/tests/integration_tests/format_integration_test.py index 5d6d3ea258a..97bf33d6eb9 100644 --- a/demisto_sdk/tests/integration_tests/format_integration_test.py +++ b/demisto_sdk/tests/integration_tests/format_integration_test.py @@ -10,6 +10,7 @@ GENERAL_DEFAULT_FROMVERSION, ) from demisto_sdk.commands.common.content.content import Content +from demisto_sdk.commands.common.content_constant_paths import ContentPaths from demisto_sdk.commands.common.git_util import GitUtil from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.handlers import DEFAULT_YAML_HANDLER as yaml @@ -260,7 +261,8 @@ def test_integration_format_configuring_conf_json_no_interactive_positive( # Setting up conf.json conf_json_path = tmp_path / "conf.json" mocker.patch( - "demisto_sdk.commands.format.update_generic_yml.CONF_PATH", conf_json_path + "demisto_sdk.commands.format.update_generic_yml.ContentPaths.CONF_PATH", + conf_json_path, ) with open(conf_json_path, "w") as file: json.dump(CONF_JSON_ORIGINAL_CONTENT, file, indent=4) @@ -313,7 +315,8 @@ def test_integration_format_configuring_conf_json_positive( # Setting up conf.json conf_json_path = tmp_path / "conf.json" mocker.patch( - "demisto_sdk.commands.format.update_generic_yml.CONF_PATH", conf_json_path + "demisto_sdk.commands.format.update_generic_yml.ContentPaths.CONF_PATH", + conf_json_path, ) with open(conf_json_path, "w") as file: json.dump(CONF_JSON_ORIGINAL_CONTENT, file, indent=4) @@ -390,7 +393,8 @@ def test_integration_format_configuring_conf_json_negative( # Setting up conf.json conf_json_path = tmp_path / "conf.json" mocker.patch( - "demisto_sdk.commands.format.update_generic_yml.CONF_PATH", conf_json_path + "demisto_sdk.commands.format.update_generic_yml.ContentPaths.CONF_PATH", + conf_json_path, ) with open(conf_json_path, "w") as file: @@ -1936,16 +1940,17 @@ def test_verify_deletion_from_conf_pack_format_with_deprecate_flag( Then - Ensure deletion from test.conf """ + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + repo_path = repo.path + ContentPaths.update_content_path(repo_path) - # Prepare mockers - - # Prepare content # Create pack with integration and with test playbook in the yml. pack = repo.create_pack("TestPack") integration = pack.create_integration("TestIntegration") integration.yml.update({"tests": ["test_playbook"]}) pack_path = pack.path - repo_path = repo.path + # We don't need to format empty readme files Path(f"{repo_path}/Packs/TestPack/Integrations/TestIntegration/README.md").unlink( missing_ok=True @@ -1995,6 +2000,9 @@ def test_verify_deletion_from_conf_pack_format_with_deprecate_flag( } ] + # Restore the original content path. + ContentPaths.update_content_path(old_content_path) + def test_verify_deletion_from_conf_script_format_with_deprecate_flag( mocker, monkeypatch, repo @@ -2010,7 +2018,10 @@ def test_verify_deletion_from_conf_script_format_with_deprecate_flag( - Ensure deletion from test.conf """ - # Prepare mockers + # Save the current content path and update it for the lifetime of the test. + old_content_path = ContentPaths.CONTENT_PATH + repo_path = repo.path + ContentPaths.update_content_path(repo_path) # Prepare content # Create pack with script and with test playbook in the yml. @@ -2018,7 +2029,6 @@ def test_verify_deletion_from_conf_script_format_with_deprecate_flag( script = pack.create_script("TestScript") script.yml.update({"tests": ["test_playbook_for_script"]}) script_path = script.path - repo_path = repo.path # We don't need to format empty readme files Path(f"{repo_path}/Packs/TestPack/Scripts/TestScript/README.md").unlink( @@ -2056,6 +2066,9 @@ def test_verify_deletion_from_conf_script_format_with_deprecate_flag( {"scripts": ["AnotherTestScript"], "playbookID": "test_playbook_for_script"}, ] + # Restore the original content path. + ContentPaths.update_content_path(old_content_path) + def test_format_incident_field_with_no_graph(mocker, monkeypatch, repo): """ diff --git a/demisto_sdk/tests/integration_tests/modeling_rules_integration_test.py b/demisto_sdk/tests/integration_tests/modeling_rules_integration_test.py index bbcf721bbf3..8727e915764 100644 --- a/demisto_sdk/tests/integration_tests/modeling_rules_integration_test.py +++ b/demisto_sdk/tests/integration_tests/modeling_rules_integration_test.py @@ -992,13 +992,15 @@ def test_the_test_modeling_rule_command_results_with_ignored_validations( "0", ], ) + schema_path = Path(pack.modeling_rules[0].schema.path).relative_to( + pack.repo_path + ) # Assert assert result.exit_code == 0 assert "All mappings validated successfully" in result.output # make sure the schema validation was skipped. - schema_path = pack.modeling_rules[0].schema.path assert ( - f"Skipping the validation to check that the schema {schema_path} is aligned with TestData file" + f"Skipping the validation to check that the schema {str(schema_path)} is aligned with TestData file" in result.output ) except typer.Exit: diff --git a/demisto_sdk/tests/integration_tests/unify_integration_test.py b/demisto_sdk/tests/integration_tests/unify_integration_test.py index de14bc0a6d0..145aa507e9d 100644 --- a/demisto_sdk/tests/integration_tests/unify_integration_test.py +++ b/demisto_sdk/tests/integration_tests/unify_integration_test.py @@ -10,7 +10,7 @@ from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json from demisto_sdk.commands.common.handlers import DEFAULT_YAML_HANDLER as yaml from demisto_sdk.commands.common.legacy_git_tools import git_path -from demisto_sdk.commands.validate.tests.test_tools import REPO +from demisto_sdk.commands.validate.tests.test_tools import get_temp_repo from demisto_sdk.tests.test_files.validate_integration_test_valid_types import ( DASHBOARD, GENERIC_MODULE, @@ -326,7 +326,8 @@ def test_layout_unify(self, mocker, monkeypatch): - make sure the 'fromServerVersion' and 'fromVersion' are the same. - make sure the 'toVersion' and 'toServerVersion' are the same. """ - pack = REPO.create_pack("test") + repo = get_temp_repo() + pack = repo.create_pack("test") layout = pack.create_layoutcontainer( name="test", content=json.load( @@ -338,7 +339,7 @@ def test_layout_unify(self, mocker, monkeypatch): output = "test.json" - with ChangeCWD(REPO.path): + with ChangeCWD(repo.path): runner = CliRunner(mix_stderr=False) result = runner.invoke( main, [UNIFY_CMD, "-i", f"{layout.path}", "-o", output] diff --git a/demisto_sdk/tests/integration_tests/upload_integration_test.py b/demisto_sdk/tests/integration_tests/upload_integration_test.py index fbb0915bf62..5c0554a3562 100644 --- a/demisto_sdk/tests/integration_tests/upload_integration_test.py +++ b/demisto_sdk/tests/integration_tests/upload_integration_test.py @@ -62,7 +62,7 @@ def test_integration_upload_pack_positive(demisto_client_mock, mocker): DEMISTO_SDK_PATH, "tests/test_files/content_repo_example/Packs/FeedAzure" ) mocker.patch.object( - content_item, + content_item.ContentPaths, "CONTENT_PATH", Path(DEMISTO_SDK_PATH, "tests/test_files/content_repo_example"), ) @@ -121,7 +121,7 @@ def test_integration_upload_pack_with_specific_marketplace(demisto_client_mock, "tests/test_files/content_repo_example/Packs/ExamplePack/Integrations", ) mocker.patch.object( - content_item, + content_item.ContentPaths, "CONTENT_PATH", Path(DEMISTO_SDK_PATH, "tests/test_files/content_repo_example"), )