import json
from io import BytesIO
from unittest import mock
import demistomock as demisto
from ValidateContent import (
HOOK_ID_TO_PATTERN,
ValidationResult,
extract_hook_id,
get_pack_name,
parse_pre_commit_output,
read_validate_results,
resolve_entity_type,
strip_ansi_codes,
)
def create_mock_zip_file_with_metadata(metadata_content):
"""
Helper function to create a mock zip file with metadata.json
Args:
metadata_content:
Returns:
mock_zip: a mock zip file with metadata.json containing metadata_content.
"""
mock_zip = mock.MagicMock()
mock_metadata_file = BytesIO(json.dumps(metadata_content).encode("utf-8"))
def mock_open(name, *args, **kwargs):
if name == "metadata.json":
return mock_metadata_file
raise KeyError(f"No such file: {name}")
mock_zip.open = mock_open
return mock_zip
def test_strip_ansi_codes():
ansi_text = "\033[31mRed text\033[0m"
assert strip_ansi_codes(ansi_text) == "Red text"
def test_extract_hook_id():
output = "Running hook: check-ast\n- hook id: check-ast\nAn error occurred"
assert extract_hook_id(output) == "check-ast"
assert extract_hook_id("No hook id") == ""
def test_parse_pre_commit_output_check_ast():
output = """check python ast.........................................................Failed
- hook id: check-ast
- exit code: 1
Packs/TmpPack/Integrations/HelloWorldTest/HelloWorldTest.py: failed parsing with CPython 3.11.10:
Traceback (most recent call last):
File "/root/.cache/pre-commit/repopc0svvoh/py_env-python3.11/lib/python3.11/site-packages/pre_commit_hooks/check_ast.py",
line 21, in main
ast.parse(f.read(), filename=filename)
File "/usr/local/lib/python3.11/ast.py", line 50, in parse
return compile(source, filename, mode, flags,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "Packs/TmpPack/Integrations/HelloWorldTest/HelloWorldTest.py", line 1413
elif command == 'hello
^
SyntaxError: unterminated string literal (detected at line 1413)"""
pattern_obj = HOOK_ID_TO_PATTERN["check-ast"]
result = parse_pre_commit_output(output, pattern_obj)
assert result == [{"file": "Packs/TmpPack/Integrations/HelloWorldTest/HelloWorldTest.py", "line": "1413"}]
def test_parse_pre_commit_output_mypy():
output = """mypy-py3.11..............................................................Failed
- hook id: mypy
- exit code: 1
Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py:791: error: Name
"greet" is not defined [name-defined]
greet(inp)
^
Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py:791: error: Name
"inp" is not defined [name-defined]
greet(inp)
^
Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py:794: error: Name
"by" is not defined [name-defined]
by({'arrrr': 'rrrrra', 'rrrraa': 'rapapapu'})
^
Found 3 errors in 1 file (checked 1 source file)"""
pattern_obj = HOOK_ID_TO_PATTERN["mypy"]
result = parse_pre_commit_output(output, pattern_obj)
assert result == [
{
"file": "Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py",
"line": "791",
"details": """Name
"greet" is not defined [name-defined]
greet(inp)
^""",
},
{
"file": "Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py",
"line": "791",
"details": """Name
"inp" is not defined [name-defined]
greet(inp)
^""",
},
{
"file": "Packs/TAXIIServer/Integrations/TAXII2Server/TAXII2Server.py",
"line": "794",
"details": """Name
"by" is not defined [name-defined]
by({'arrrr': 'rrrrra', 'rrrraa': 'rapapapu'})
^""",
},
]
def test_resolve_entity_type():
assert resolve_entity_type("Packs/SomePack/Integrations/SomeIntegration") == "integration"
assert resolve_entity_type("Packs/SomePack/Scripts/SomeScript") == "script"
assert resolve_entity_type("Packs/SomePack/Playbooks/SomePlaybook") == "playbook"
assert resolve_entity_type("Packs/SomePack/TestPlaybooks/SomeTestPlaybook") == "testplaybook"
assert resolve_entity_type("Packs/SomePack/") == "contentpack"
def test_get_pack_name_success(mocker):
"""
Given:
A valid zip file path with a metadata.json file containing a pack name.
When:
Calling get_pack_name with the zip file path.
Then:
The function should return the correct pack name from the metadata.json file.
"""
mock_metadata = {"name": "TestPack"}
mock_metadata_json = json.dumps(mock_metadata)
mock_zipfile = mocker.MagicMock()
mock_metadata_file = mocker.MagicMock()
mock_metadata_file.read.return_value = mock_metadata_json
# Simulate behaviour of nested context managers.
mock_zipfile.__enter__.return_value.open.return_value.__enter__.return_value = mock_metadata_file
mocker.patch("zipfile.ZipFile", return_value=mock_zipfile)
result = get_pack_name("test_pack.zip")
assert result == "TestPack"
def test_get_pack_name_no_name(mocker):
"""
Given:
A valid zip file path with a metadata.json file that doesn't contain a pack name.
When:
Calling get_pack_name with the zip file path.
Then:
The function should return 'TmpPack' as the default pack name.
"""
mock_metadata = {}
mock_metadata_json = json.dumps(mock_metadata)
mock_zipfile = mocker.MagicMock()
mock_metadata_file = mocker.MagicMock()
mock_metadata_file.read.return_value = mock_metadata_json
mock_zipfile.__enter__.return_value.open.return_value.__enter__.return_value = mock_metadata_file
mocker.patch("zipfile.ZipFile", return_value=mock_zipfile)
mock_error = mocker.patch.object(demisto, "error")
result = get_pack_name("test_pack.zip")
assert result == "TmpPack"
mock_error.assert_called_with("Could not find pack name in metadata.json")
def test_read_validate_results(tmp_path):
"""
Given:
A temporary JSON file with validation results.
When:
Calling read_validate_results with the path to this file.
Then:
The function should return a list of ValidationResult objects.
"""
json_file = tmp_path / "validation_results.json"
json_file.write_text(
json.dumps(
[
{
"validations": [
{
"file path": "Packs/TestPack/Scripts/TestScript/TestScript.yml",
"error code": "ST001",
"message": "Test error message",
}
]
}
]
)
)
results = read_validate_results(json_file)
assert len(results) == 1
assert isinstance(results[0], ValidationResult)
assert results[0].filePath.endswith("Packs/TestPack/Scripts/TestScript/TestScript.yml")
assert results[0].errorCode == "ST001"
assert results[0].message == "Test error message"