Skip to content

Commit

Permalink
docs(unit-testing): Update Unit test check for GCP
Browse files Browse the repository at this point in the history
  • Loading branch information
puchy22 committed Apr 15, 2024
1 parent 63adc83 commit fa25aa0
Showing 1 changed file with 72 additions and 49 deletions.
121 changes: 72 additions & 49 deletions docs/developer-guide/unit-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,74 +437,97 @@ Please refer to the [AWS checks tests](./unit-testing.md#checks) for more inform

For the GCP Provider we don't have any library to mock out the API calls we use. So in this scenario we inject the objects in the service client using [MagicMock](https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock).

The following code shows how to use MagicMock to create the service objects for a GCP check test.
The following code shows how to use MagicMock to create the service objects for a GCP check test. It is a real example adapted for informative purposes.

```python
# We need to import the unittest.mock to allow us to patch some objects
# not to use shared ones between test, hence to isolate the test
from re import search
from unittest import mock

# GCP Constants
GCP_PROJECT_ID = "123456789012"
# Import some constans values needed in every check
from tests.providers.gcp.gcp_fixtures import GCP_PROJECT_ID, set_mocked_gcp_provider

# We are going to create a test for the compute_firewall_rdp_access_from_the_internet_allowed check
class Test_compute_firewall_rdp_access_from_the_internet_allowed:
# We are going to create a test for the compute_project_os_login_enabled check
class Test_compute_project_os_login_enabled:

# We name the tests with test_<service>_<check_name>_<test_action>
def test_compute_compute_firewall_rdp_access_from_the_internet_allowed_one_compliant_rule_with_valid_port(self):
def test_one_compliant_project(self):
# Import the service resource model to create the mocked object
from prowler.providers.gcp.services.compute.compute_service import Project
# Create the custom Project object to be tested
project = Project(
id=GCP_PROJECT_ID,
enable_oslogin=True,
)
# Mocked client with MagicMock
compute_client = mock.MagicMock

# Assign GCP client configuration
compute_client.project_ids = [GCP_PROJECT_ID]
compute_client.region = "global"
compute_client.projects = [project]

# Import the service resource model to create the mocked object
from prowler.providers.gcp.services.compute.compute_service import Firewall

# Create the custom Firewall object to be tested
firewall = Firewall(
name="test",
id="1234567890",
source_ranges=["0.0.0.0/0"],
direction="INGRESS",
allowed_rules=[{"IPProtocol": "tcp", "ports": ["443"]}],
project_id=GCP_PROJECT_ID,
# In this scenario we have to mock the app_client from the check to enforce that the compute_client used is the one created above
# And also is mocked the return value of get_global_provider function to return our GCP mocked provider defined in fixtures
with mock.patch(
"prowler.providers.common.common.get_global_provider",
return_value=set_mocked_gcp_provider(),
), mock.patch(
"prowler.providers.gcp.services.compute.compute_project_os_login_enabled.compute_project_os_login_enabled.compute_client",
new=compute_client,
):
# We import the check within the two mocks
from prowler.providers.gcp.services.compute.compute_project_os_login_enabled.compute_project_os_login_enabled import (
compute_project_os_login_enabled,
)
# Once imported, we only need to instantiate the check's class
check = compute_project_os_login_enabled()
# And then, call the execute() function to run the check
# against the Compute client we've set up.
result = check.execute()
# Assert the expected results
assert len(result) == 1
assert result[0].status == "PASS"
assert search(
f"Project {project.id} has OS Login enabled",
result[0].status_extended,
)
assert result[0].resource_id == project.id
assert result[0].location == "global"
assert result[0].project_id == GCP_PROJECT_ID

# Complementary test to make more coverage for different scenarios
def test_one_non_compliant_project(self):
from prowler.providers.gcp.services.compute.compute_service import Project

project = Project(
id=GCP_PROJECT_ID,
enable_oslogin=False,
)
compute_client.firewalls = [firewall]

# In this scenario we have to mock also the Compute service and the compute_client from the check to enforce that the compute_client used is the one created within this check because patch != import, and if you execute tests in parallel some objects can be already initialised hence the check won't be isolated.
# In this case we don't use the Moto decorator, we use the mocked Compute client for both objects
compute_client = mock.MagicMock
compute_client.project_ids = [GCP_PROJECT_ID]
compute_client.projects = [project]

with mock.patch(
"prowler.providers.gcp.services.compute.compute_service.Compute",
new=defender_client,
"prowler.providers.common.common.get_global_provider",
return_value=set_mocked_gcp_provider(),
), mock.patch(
"prowler.providers.gcp.services.compute.compute_client.compute_client",
new=defender_client,
"prowler.providers.gcp.services.compute.compute_project_os_login_enabled.compute_project_os_login_enabled.compute_client",
new=compute_client,
):

# We import the check within the two mocks not to initialise the iam_client with some shared information from
# the current_audit_info or the Compute service.
from prowler.providers.gcp.services.compute.compute_firewall_rdp_access_from_the_internet_allowed.compute_firewall_rdp_access_from_the_internet_allowed import (
compute_firewall_rdp_access_from_the_internet_allowed,
from prowler.providers.gcp.services.compute.compute_project_os_login_enabled.compute_project_os_login_enabled import (
compute_project_os_login_enabled,
)

# Once imported, we only need to instantiate the check's class
check = compute_firewall_rdp_access_from_the_internet_allowed()

# And then, call the execute() function to run the check
# against the IAM client we've set up.
check = compute_project_os_login_enabled()
result = check.execute()

# Last but not least, we need to assert all the fields
# from the check's results
assert len(result) == 1
assert result[0].status == "PASS"
assert result[0].status_extended == f"Firewall {firewall.name} does not expose port 3389 (RDP) to the internet."
assert result[0].resource_name = firewall.name
assert result[0].resource_id == firewall.id
assert result[0].project_id = GCP_PROJECT_ID
assert result[0].location = compute_client.region
assert result[0].status == "FAIL"
assert search(
f"Project {project.id} does not have OS Login enabled",
result[0].status_extended,
)
assert result[0].resource_id == project.id
assert result[0].location == "global"
assert result[0].project_id == GCP_PROJECT_ID

```

### Services
Expand Down Expand Up @@ -535,7 +558,7 @@ from tests.providers.azure.azure_fixtures import (
# We are going to create a test for the app_ensure_http_is_redirected_to_https check
class Test_app_ensure_http_is_redirected_to_https:

# We name the tests with test_<check_name>_<test_action>
# We name the tests with test_<service>_<check_name>_<test_action>
def test_app_http_to_https_disabled(self):
resource_id = f"/subscriptions/{uuid4()}"
# Mocked client with MagicMock
Expand Down

0 comments on commit fa25aa0

Please sign in to comment.