Skip to content

Commit

Permalink
testing: first batch of new tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanpulver committed Jul 30, 2024
1 parent 9a53da4 commit 972e028
Show file tree
Hide file tree
Showing 4 changed files with 322 additions and 0 deletions.
56 changes: 56 additions & 0 deletions tests/scan/ecosystems/test_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest
from unittest.mock import MagicMock
from typer import FileTextWrite
from safety_schemas.models import ConfigModel, DependencyResultModel, Ecosystem, FileType
from safety.scan.ecosystems.base import Inspectable, Remediable, InspectableFile

class TestInspectable(unittest.TestCase):
def test_inspect_abstract_method(self):
class TestClass(Inspectable):
pass

with self.assertRaises(TypeError):
TestClass()

def test_inspect_implemented_method(self):
class TestClass(Inspectable):
def inspect(self, config: ConfigModel) -> DependencyResultModel:
return DependencyResultModel(dependencies=[])

instance = TestClass()
result = instance.inspect(MagicMock(spec=ConfigModel))
self.assertIsInstance(result, DependencyResultModel)


class TestRemediable(unittest.TestCase):
def test_remediate_abstract_method(self):
class TestClass(Remediable):
pass

with self.assertRaises(TypeError):
TestClass()

def test_remediate_implemented_method(self):
class TestClass(Remediable):
def remediate(self):
return "Remediation done"

instance = TestClass()
result = instance.remediate()
self.assertEqual(result, "Remediation done")


class TestInspectableFile(unittest.TestCase):
def test_initialization(self):
class ConcreteInspectableFile(InspectableFile):
def inspect(self, config: ConfigModel) -> DependencyResultModel:
return DependencyResultModel(dependencies=[])

file_mock = MagicMock(spec=FileTextWrite)
inspectable_file = ConcreteInspectableFile(file=file_mock)
self.assertEqual(inspectable_file.file, file_mock)
self.assertIsInstance(inspectable_file.dependency_results, DependencyResultModel)
self.assertEqual(inspectable_file.dependency_results.dependencies, [])

if __name__ == '__main__':
unittest.main()
96 changes: 96 additions & 0 deletions tests/scan/test_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import unittest
from unittest.mock import MagicMock, patch
from pathlib import Path
from rich.console import Console
from safety.errors import SafetyException
from safety.scan.models import ScanOutput, SystemScanOutput
from safety_schemas.models import ConfigModel, ProjectModel, PolicySource, ScanType, MetadataModel, ReportSchemaVersion, TelemetryModel

from safety.scan.decorators import (
initialize_scan,
scan_project_command_init,
scan_system_command_init,
inject_metadata
)

class TestInitializeScan(unittest.TestCase):

@patch('safety.scan.decorators.LOG')
def test_initialize_scan(self, mock_log):
ctx = MagicMock()
ctx.obj.auth.client.initialize_scan.return_value = {'platform-enabled': True}
console = MagicMock()
initialize_scan(ctx, console)
self.assertTrue(ctx.obj.platform_enabled)
ctx.obj.auth.client.initialize_scan.assert_called_once()

class TestScanProjectCommandInit(unittest.TestCase):

@patch('safety.scan.decorators.load_unverified_project_from_config')
@patch('safety.scan.decorators.print_header')
@patch('safety.scan.decorators.verify_project')
@patch('safety.scan.decorators.load_policy_file')
@patch('safety.scan.decorators.resolve_policy')
@patch('safety.scan.decorators.print_announcements')
@patch('safety.scan.decorators.initialize_scan')
def test_scan_project_command_init(self, mock_initialize_scan, mock_print_announcements, mock_resolve_policy, mock_load_policy_file, mock_verify_project, mock_print_header, mock_load_unverified_project_from_config):
mock_load_unverified_project_from_config.return_value = MagicMock()
mock_resolve_policy.return_value = MagicMock()
mock_load_policy_file.return_value = MagicMock()
mock_verify_project.return_value = MagicMock()

@scan_project_command_init
def dummy_func(ctx, target, output, *args, **kwargs):
return "scan project"

ctx = MagicMock()
ctx.obj.auth.stage = "development"
ctx.obj.telemetry = TelemetryModel(
safety_options={},
safety_version="1.0.0",
safety_source="CLI",
os_type="Linux",
os_release="5.4.0",
os_description="Linux-5.4.0-42-generic-x86_64-with-Ubuntu-20.04-focal",
python_version="3.8.5",
safety_command="scan"
)
policy_file_path = None
target = Path("/path/to/target")
output = MagicMock(spec=ScanOutput)
output.is_silent = MagicMock(return_value=False)
console = MagicMock(spec=Console)

result = dummy_func(ctx, policy_file_path, target, output, console)
self.assertEqual(result, "scan project")
mock_initialize_scan.assert_called_once()
mock_print_announcements.assert_called_once()
mock_print_header.assert_called_once()


class TestInjectMetadata(unittest.TestCase):

def test_inject_metadata(self):

@inject_metadata
def dummy_func(ctx, *args, **kwargs):
return "metadata injected"

ctx = MagicMock()
ctx.command.name = "scan"
ctx.invoked_subcommand = None
ctx.obj.auth.stage = "development"
ctx.obj.auth.client.get_authentication_type.return_value = "api_key"
ctx.obj.auth.client.is_using_auth_credentials.return_value = True

target = Path("/path/to/target")
kwargs = {"target": target}

result = dummy_func(ctx, **kwargs)
self.assertEqual(result, "metadata injected")
self.assertEqual(ctx.obj.metadata.scan_type, ScanType.scan)
self.assertEqual(ctx.obj.metadata.scan_locations, [target])
self.assertEqual(ctx.obj.metadata.authenticated, True)

if __name__ == '__main__':
unittest.main()
84 changes: 84 additions & 0 deletions tests/scan/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import unittest
from unittest.mock import MagicMock, patch, mock_open
from pathlib import Path
from configparser import ConfigParser
from pydantic import ValidationError
from typing import Any, Dict, Set, Tuple

from safety.auth.utils import SafetyAuthSession
from safety.errors import SafetyError
from safety.scan.ecosystems.target import InspectableFileContext
from safety.scan.models import ScanExport, UnverifiedProjectModel
from safety.scan.main import (
download_policy,
load_unverified_project_from_config,
save_project_info,
load_policy_file,
resolve_policy,
save_report_as,
process_files
)
from safety_schemas.models import FileType, PolicyFileModel, PolicySource, ConfigModel, Stage, ProjectModel, ScanType
import importlib
import time
class TestMainFunctions(unittest.TestCase):

@patch('safety.scan.main.configparser.ConfigParser')
def test_load_unverified_project_from_config(self, MockConfigParser):
mock_config = MockConfigParser.return_value
mock_config.get.side_effect = lambda section, option, fallback=None: {
"id": "test_id",
"url": "test_url",
"name": "test_name"
}.get(option, fallback)

project_root = Path("/path/to/project")
result = load_unverified_project_from_config(project_root)
self.assertIsInstance(result, UnverifiedProjectModel)
self.assertEqual(result.id, "test_id")
self.assertEqual(result.url_path, "test_url")
self.assertEqual(result.name, "test_name")

@patch('builtins.open', new_callable=mock_open)
@patch('safety.scan.main.configparser.ConfigParser')
def test_save_project_info(self, MockConfigParser, mock_open):
mock_config = MockConfigParser.return_value
project = ProjectModel(id="test_id", url_path="test_url", name="test_name")
project_path = Path("/path/to/project/.safety-project.ini")
save_project_info(project, project_path)
mock_config.read.assert_called_once_with(project_path)
mock_open.assert_called_once_with(project_path, 'w')
mock_config.write.assert_called_once()

def test_resolve_policy(self):
local_policy = MagicMock()
cloud_policy = MagicMock()
result = resolve_policy(local_policy, cloud_policy)
self.assertEqual(result, cloud_policy)

result = resolve_policy(local_policy, None)
self.assertEqual(result, local_policy)

result = resolve_policy(None, cloud_policy)
self.assertEqual(result, cloud_policy)

result = resolve_policy(None, None)
self.assertIsNone(result)


@patch('safety.scan.main.InspectableFileContext')
def test_process_files(self, MockInspectableFileContext):
paths = {
"requirements.txt": {Path("/path/to/requirements.txt")},
}
config = MagicMock()
mock_file = MockInspectableFileContext.return_value.__enter__.return_value
mock_file.file_type = FileType.REQUIREMENTS_TXT

result = list(process_files(paths, config))
self.assertEqual(len(result), 1)
self.assertIsInstance(result[0], Tuple)
self.assertEqual(result[0][0], Path("/path/to/requirements.txt"))

if __name__ == '__main__':
unittest.main()
86 changes: 86 additions & 0 deletions tests/scan/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import unittest
from pathlib import Path
from typing import Optional
from enum import Enum
from pydantic.dataclasses import dataclass
from safety.scan.models import FormatMixin, ScanOutput, ScanExport, SystemScanOutput, SystemScanExport, UnverifiedProjectModel

class TestFormatMixin(unittest.TestCase):

def test_is_format(self):
class TestEnum(Enum):
FORMAT_A = "format_a"
FORMAT_B = "[email protected]"

self.assertTrue(FormatMixin.is_format(TestEnum.FORMAT_A, TestEnum.FORMAT_A))
self.assertFalse(FormatMixin.is_format(TestEnum.FORMAT_A, TestEnum.FORMAT_B))
self.assertTrue(FormatMixin.is_format(TestEnum.FORMAT_B, TestEnum.FORMAT_B))
self.assertFalse(FormatMixin.is_format(None, TestEnum.FORMAT_A))
self.assertTrue(FormatMixin.is_format(TestEnum.FORMAT_B, TestEnum("[email protected]")))

def test_version(self):
class TestEnum(FormatMixin, str, Enum):
FORMAT_A = "format_a"
FORMAT_B = "[email protected]"

self.assertIsNone(TestEnum.FORMAT_A.version)
self.assertEqual(TestEnum.FORMAT_B.version, "1.0")


class TestScanOutput(unittest.TestCase):

def test_is_silent(self):
self.assertTrue(ScanOutput.JSON.is_silent())
self.assertTrue(ScanOutput.SPDX.is_silent())
self.assertTrue(ScanOutput.SPDX_2_3.is_silent())
self.assertTrue(ScanOutput.SPDX_2_2.is_silent())
self.assertTrue(ScanOutput.HTML.is_silent())
self.assertFalse(ScanOutput.SCREEN.is_silent())
self.assertFalse(ScanOutput.NONE.is_silent())


class TestScanExport(unittest.TestCase):

def test_get_default_file_name(self):
tag = 123456
self.assertEqual(ScanExport.JSON.get_default_file_name(tag), f"safety-report-{tag}.json")
self.assertEqual(ScanExport.SPDX.get_default_file_name(tag), f"safety-report-spdx-{tag}.json")
self.assertEqual(ScanExport.SPDX_2_3.get_default_file_name(tag), f"safety-report-spdx-{tag}.json")
self.assertEqual(ScanExport.SPDX_2_2.get_default_file_name(tag), f"safety-report-spdx-{tag}.json")
self.assertEqual(ScanExport.HTML.get_default_file_name(tag), f"safety-report-{tag}.html")
with self.assertRaises(ValueError):
ScanExport("unsupported").get_default_file_name(tag)


class TestSystemScanOutput(unittest.TestCase):

def test_is_silent(self):
self.assertTrue(SystemScanOutput.JSON.is_silent())
self.assertFalse(SystemScanOutput.SCREEN.is_silent())


class TestSystemScanExport(unittest.TestCase):

def test_system_scan_export(self):
self.assertEqual(SystemScanExport.JSON.value, "json")


class TestUnverifiedProjectModel(unittest.TestCase):

def test_unverified_project_model(self):
project = UnverifiedProjectModel(
id="test_id",
project_path=Path("/path/to/project"),
created=True,
name="test_name",
url_path="http://test.url"
)
self.assertEqual(project.id, "test_id")
self.assertEqual(project.project_path, Path("/path/to/project"))
self.assertTrue(project.created)
self.assertEqual(project.name, "test_name")
self.assertEqual(project.url_path, "http://test.url")


if __name__ == '__main__':
unittest.main()

0 comments on commit 972e028

Please sign in to comment.