import json import os from unittest.mock import Mock, patch import pytest from GoogleCloudSCC import ( ERROR_MESSAGES, BaseGoogleClient, GoogleCloudAssetClient, GoogleNameParser, GooglePubSubClient, GoogleSccClient, ) with open("test_data/service_account_json.txt") as f: TEST_JSON = f.read() @pytest.fixture def client(): with patch.object(GoogleSccClient, "__init__", lambda x: None): mocked_client = GoogleSccClient() mocked_client.organization_id = "organization_id" mocked_client.service = Mock() mocked_client.execute_request = Mock() return mocked_client @pytest.fixture def pubsub_client(): with patch.object(GooglePubSubClient, "__init__", lambda x: None): mocked_client = GooglePubSubClient() mocked_client.project_id = "project_id" mocked_client.subscription_id = "subscription_id" mocked_client.service = Mock() mocked_client.execute_request = Mock() return mocked_client @pytest.fixture def cloud_asset_client(): with patch.object(GoogleCloudAssetClient, "__init__", lambda x: None): mocked_client = GoogleCloudAssetClient() mocked_client.organization_id = "organization_id" mocked_client.service = Mock() mocked_client.execute_request = Mock() return mocked_client @pytest.fixture def base_client(): with patch.object(BaseGoogleClient, "__init__", lambda x: None): mocked_client = BaseGoogleClient() mocked_client.service = "service" return mocked_client def test_safe_load_non_strict_json(): """ Scenario: Dictionary should be prepared from json string. Given: - json as string. When: - Preparing dictionary from string. Then: - Ensure valid json should be loaded successfully. """ from GoogleCloudSCC import safe_load_non_strict_json excepted_json = json.loads(TEST_JSON, strict=False) assert safe_load_non_strict_json(TEST_JSON) == excepted_json def test_safe_load_non_strict_json_parse_error(): """ Scenario: Failed to load json when invalid json string is given. Given: - Empty json string. When: - Preparing dictionary from string. Then: - Ensure Exception is raised with proper error message. """ from GoogleCloudSCC import safe_load_non_strict_json with pytest.raises(ValueError, match=ERROR_MESSAGES["JSON_PARSE_ERROR"].format("Service Account JSON")): safe_load_non_strict_json("Invalid json") def test_safe_load_non_strict_json_empty(): """ Scenario: Returns {}(blank) dictionary when empty json string is given. Given: - Invalid json as string. When: - Preparing dictionary from string. Then: - Ensure {}(blank) dictionary should be returned. """ from GoogleCloudSCC import safe_load_non_strict_json assert safe_load_non_strict_json("") == {} def test_get_source_path(mocker): """ Scenario: Return a fully-qualified source string. Given: - valid string parameters. When: - Preparing source string. Then: - Ensure a fully-qualified source string should be returned. """ from GoogleCloudSCC import GoogleNameParser, demisto mocker.patch.object(demisto, "params", return_value={"organization_id": "organization_id"}) assert GoogleNameParser.get_source_path("source_id") == "organizations/organization_id/sources/source_id" @patch("GoogleCloudSCC.init_google_scc_client") def test_validate_service_account_and_organization_name(mock1, client): """ Scenario:Validate organization by making "Organization settings" API call. Given: - Empty Dictionary. When: - Validating parameters. Then: - Ensure {}(blank) dictionary returns error. """ from GoogleCloudSCC import validate_service_account_and_organization_name mock1.return_value = client client.get_findings = Mock(return_value={}) validate_service_account_and_organization_name({"service_account_json": '{"test": "test"}'}) assert client.get_findings.call_count == 1 param = {"service_account_json": "123"} with pytest.raises(ValueError, match=ERROR_MESSAGES["INVALID_SERVICE_ACCOUNT"].format("Service Account JSON")): validate_service_account_and_organization_name(param) def test_prepare_markdown_fields_for_fetch_incidents(): """ Scenario:Prepares markdown fields for incident. Given: - Dictionary of fields received in response of fetch incident. When: - Validating parameters. Then: - Proper table format of given parameters. """ from GoogleCloudSCC import prepare_markdown_fields_for_fetch_incidents, tableToMarkdown data = {"finding": {"securityMarks": {"marks": {"A": 1, "B": 2}}, "sourceProperties": {"MfaDetails": {"C": 3, "D": 4}}}} actual_output = prepare_markdown_fields_for_fetch_incidents(data) expected_output = { "MfaDetails": tableToMarkdown("", {"C": 3, "D": 4}), "securityMarks": tableToMarkdown("", {"A": 1, "B": 2}), } assert actual_output == expected_output def test_create_filter_list_findings(): """ Scenario : Creating common filter query string for "list findings" API based on various filter parameter. Given: - List of filter parameters. When: - Preparing a filter query based on convention of API. Then: - A filter query with all parameters in proper format. """ from GoogleCloudSCC import create_filter_list_findings output = create_filter_list_findings("A, B ,C", "ABC = X", ["HIGH", "LOW"], ["ACTIVE"]) assert ( output == 'ABC = X AND (Severity="HIGH" OR Severity="LOW") AND (State="ACTIVE") AND ' '(Category="A" OR Category="B" OR Category="C")' ) output = create_filter_list_findings("A, B ,C", "", ["HIGH", "LOW"], ["ACTIVE"]) assert output == '(Severity="HIGH" OR Severity="LOW") AND (State="ACTIVE") AND (Category="A" OR Category="B" OR Category="C")' def test_fetch_incidents(pubsub_client): """ Scenario : Prepares incidents from past activity in Google Drive. Given: - List of parameters. When: - Preparing a incident according to given parameters. Then: - An incident made according to parameters and last_run is returned. """ from GoogleCloudSCC import fetch_incidents with open("test_data/fetch_incidents_data.json") as file: mock_data = json.load(file) pubsub_client.pull_messages = Mock(return_value=mock_data) pubsub_client.acknowledge_messages = Mock(return_value={}) param = {"organization_id": "123"} incidents = fetch_incidents(pubsub_client, param) assert len(incidents) == 4 def test_execute_request(base_client): """ Scenario : Execute the request and handle error scenario. Given: - Request object. When: - Handling error scenarios. Then: - Checks if correct Error message is there or not. """ from GoogleCloudSCC import HttpError, httplib2 from httplib2 import Response, socks mock_request = Mock() mock_request.execute = Mock( side_effect=[ HttpError(Response({"status": 404}), b"{}"), HttpError(Response({"status": 501}), b"{}"), socks.HTTPError("Proxy Error"), httplib2.ServerNotFoundError("Not Found."), ] ) errors = [ ERROR_MESSAGES["NOT_FOUND_ERROR"].format("Ok"), ERROR_MESSAGES["UNKNOWN_ERROR"].format(501, "Ok"), ERROR_MESSAGES["PROXY_ERROR"], ERROR_MESSAGES["TIMEOUT_ERROR"].format("Not Found."), ] for error in errors: with pytest.raises(ValueError, match=error): base_client.execute_request(mock_request) def test_google_name_parser(): """ Scenario: Validates static method of GoogleNameParser Given: - argument given Then: - Ensure static method should return proper outputs. """ from GoogleCloudSCC import GoogleNameParser assert GoogleNameParser.get_finding_path("-", "123") == "organizations//sources/-/findings/123" assert GoogleNameParser.get_project_path("123") == "projects/123" assert GoogleNameParser.get_subscription_path("123", "456") == "projects/123/subscriptions/456" def test_main(mocker, client): """ Scenario : Parse and validate integration params and commands. """ import GoogleCloudSCC from GoogleCloudSCC import demisto params = {"service_account_json": TEST_JSON, "organization_id": "organization_id"} mocker.patch.object(demisto, "params", return_value=params) mocker.patch.object(demisto, "command", return_value="test-module") mocker.patch.object(GoogleCloudSCC, "init_google_scc_client") mocker.patch.object(GoogleCloudSCC, "init_google_pubsub_client") mocker.patch.object(GoogleCloudSCC, "test_module", return_value="ok") GoogleCloudSCC.main() assert GoogleCloudSCC.test_module.called mocker.patch.object(demisto, "command", return_value="fetch-incidents") mocker.patch.object(GoogleCloudSCC, "fetch_incidents", return_value=([], [])) GoogleCloudSCC.main() assert GoogleCloudSCC.fetch_incidents.called mocker.patch.object(GoogleCloudSCC, "fetch_incidents", side_effect=[Exception("test")]) with pytest.raises(Exception, match="Failed to execute {} command. Error: {}".format("fetch-incidents", "test")): GoogleCloudSCC.main() @patch("GoogleCloudSCC.init_google_scc_client") def test_test_module(mock1, client, mocker): """ Scenario : Test authentication using service json. """ from GoogleCloudSCC import demisto, test_module mocker.patch.object(demisto, "results") mock1.return_value = client client.get_findings = Mock(return_value={}) test_module({"organization_id": "organization_id", "service_account_json": '{"test": "test"}'}) assert demisto.results.called demisto.results.assert_called_with("ok") def test_validate_get_int_success(): """ Scenario: Validate and convert string max_results to integer. Given: - valid page size. When: - Validating max_results. Then: - Ensure if max_results is valid. """ from GoogleCloudSCC import validate_get_int max_results = "9" return_value = validate_get_int(max_results, ERROR_MESSAGES["MAX_INCIDENT_ERROR"]) assert return_value == 9 @pytest.mark.parametrize("string_input", ["invalid_int", "10000000", "-700"]) def test_validate_get_int_max_incident_error(string_input): """ Scenario: Validate and convert string max_results to integer. Given: - Invalid page size. When: - Validating max_results at a time of fetched incident Then: - Ensure if page_size is not in range it returns error. """ from GoogleCloudSCC import MAX_PAGE_SIZE, validate_get_int with pytest.raises(ValueError, match=ERROR_MESSAGES["MAX_INCIDENT_ERROR"]): validate_get_int(string_input, ERROR_MESSAGES["MAX_INCIDENT_ERROR"], MAX_PAGE_SIZE) def test_prepare_hr_and_ec_for_list_findings(): """ Scenario: Validates human readable and entry context for list findings Given: - finding response given Then: - Ensure finding HR and EC. """ from GoogleCloudSCC import prepare_hr_and_ec_for_list_findings with open("./test_data/list_finding_response.json") as f: finding_response = json.load(f) with open("./test_data/list_finding_ec.json") as f: finding_ec = json.load(f) _, context = prepare_hr_and_ec_for_list_findings(finding_response) assert context == finding_ec def test_prepare_hr_and_ec_for_list_findings_no_record(): """ Scenario: Validates human readable and entry context for list findings Given: - finding response given When: - Zero records found Then: - Ensure finding HR and EC. """ from GoogleCloudSCC import prepare_hr_and_ec_for_list_findings finding_response = {"listFindingsResults": []} hr, context = prepare_hr_and_ec_for_list_findings(finding_response) assert context == {} assert hr == ERROR_MESSAGES["NO_RECORDS_FOUND"].format("finding") def test_findings_list_command(client): """ Scenario: Validates command result for list-finding command. Given: - command arguments given for list finding command Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import finding_list_command with open("test_data/list_finding_response.json") as file: mock_data = json.load(file) with open("./test_data/list_finding_ec.json") as f: finding_ec = json.load(f) with open("test_data/list_finding_hr.md") as f: hr_output = f.read() GoogleNameParser.get_organization_id = Mock(return_value="123") client.get_findings = Mock(return_value=mock_data) arguments = {"category": "A, B", "status": "ACTIVE"} command_output = finding_list_command(client, arguments) assert command_output.outputs == finding_ec assert command_output.raw_response == mock_data assert command_output.readable_output == hr_output def test_create_filter_list_assets(): """ Scenario : Creating common filter query string for "list assets" API based on various filter parameter. Given: - List of filter parameters. When: - Preparing a filter query based on convention of API. Then: - A filter query with all parameters in proper format. """ from GoogleCloudSCC import create_filter_list_assets output = create_filter_list_assets("X, Y ,Z", "A, B, C", "ABC = XYZ", "true") assert ( output == 'ABC = XYZ AND (resourceProperties.name="A" OR resourceProperties.name="B" OR' ' resourceProperties.name="C") AND (securityCenterProperties.resourceType="X" OR ' 'securityCenterProperties.resourceType="Y" OR securityCenterProperties.resourceType="Z") AND ' '(resourceProperties.lifecycleState="ACTIVE")' ) output = create_filter_list_assets("X, Y ,Z", "A, B, C", "ABC = XYZ", "") assert ( output == 'ABC = XYZ AND (resourceProperties.name="A" OR resourceProperties.name="B" OR' ' resourceProperties.name="C") AND (securityCenterProperties.resourceType="X" OR ' 'securityCenterProperties.resourceType="Y" OR securityCenterProperties.resourceType="Z")' ) def test_prepare_hr_and_ec_for_list_assets(): """ Scenario: Validates human readable and entry context for list assets Given: - assets response given Then: - Ensure finding HR and EC. """ from GoogleCloudSCC import prepare_outputs_for_list_assets with open("./test_data/list_asset_response.json") as f: asset_response = json.load(f) with open("./test_data/list_asset_ec.json") as f: asset_ec = json.load(f) context, _ = prepare_outputs_for_list_assets(asset_response) assert context == asset_ec def test_prepare_hr_and_ec_for_list_assets_no_record(): """ Scenario: Validates human readable and entry context for list assets Given: - asset response given When: - Zero records found Then: - Ensure finding HR and EC. """ from GoogleCloudSCC import prepare_outputs_for_list_assets response = {"listAssetsResults": []} context, hr = prepare_outputs_for_list_assets(response) assert context == {} assert hr == ERROR_MESSAGES["NO_RECORDS_FOUND"].format("asset") def test_asset_list_command(client): """ Scenario: Validates command result for list-asset command. Given: - command arguments given for list asset command Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import asset_list_command with open("test_data/list_asset_response.json") as file: mock_data = json.load(file) with open("./test_data/list_asset_ec.json") as f: asset_ec = json.load(f) with open("test_data/list_asset_hr.md") as f: hr_output = f.read() GoogleNameParser.get_organization_id = Mock(return_value="123") client.get_assets = Mock(return_value=mock_data) arguments = {"resourceType": "A", "filter": "test"} command_output = asset_list_command(client, arguments) assert command_output.outputs == asset_ec assert command_output.raw_response == mock_data assert command_output.readable_output == hr_output def test_split(): """ Scenario: Validates that string should split properly. Given: - string which contains delimiter Then: - Ensure result should split by delimiter and not split by escaped delimeter """ from GoogleCloudSCC import split_and_escape assert split_and_escape(r"abc,ss,ssa,sc\,aa", ",") == ["abc", "ss", "ssa", "sc,aa"] assert split_and_escape(r"ab;cd ;xy\;ad", ";") == ["ab", "cd", "xy;ad"] def test_get_and_validate_args_finding_update(): """ Scenario: Get and validates argument of update finding command Given: - raw argument of update finding command Then: - Ensure all argument should be in valid format. """ from GoogleCloudSCC import get_and_validate_args_finding_update # Invalid severity args = {"severity": "INVALID"} with pytest.raises(ValueError, match=ERROR_MESSAGES["INVALID_SEVERITY_ERROR"]): get_and_validate_args_finding_update(args) args = {"sourceProperties": "A=B, C=D\\,X\\=Y"} assert get_and_validate_args_finding_update(args)[4] == {"A": "B", "C": "D,X=Y"} args = {"sourceProperties": "INVALID"} with pytest.raises(ValueError): get_and_validate_args_finding_update(args) args = {"severity": "HIGH", "updateMask": "A,B,C"} _, _, severity, _, _, update_mask = get_and_validate_args_finding_update(args) assert severity == "HIGH" assert update_mask == ["A", "B", "C"] def test_get_update_mask_for_update_finding(): """ Scenario: Validates updateMask field construct Properly Given: - update finding command arguments Then: - Ensure updateMask field construct properly """ from GoogleCloudSCC import get_update_mask_for_update_finding body = {"severity": "HIGH", "state": "ACTIVE", "eventTime": "REQUIRED", "sourceProperties": {"A": "1"}} update_mask = ["state", "severity"] assert get_update_mask_for_update_finding(body, update_mask).split(",") == [ "state", "severity", "eventTime", "sourceProperties.A", ] def test_prepare_hr_and_ec_for_update_finding(): """ Scenario: Validates human readable and entry context for update finding Given: - update finding response given Then: - Ensure finding HR is correct """ from GoogleCloudSCC import prepare_hr_and_ec_for_update_finding with open("./test_data/update_finding_response.json") as f: finding_response = json.load(f) hr, _ = prepare_hr_and_ec_for_update_finding(finding_response) assert "Name" in hr assert "State" in hr assert "Category" in hr assert "Severity" not in hr def test_prepare_hr_and_ec_for_cloud_assets_list(): """ Scenario: Validates human readable and entry context for cloud assets list Given: - asset list response given Then: - Ensure HR and entry context is correct """ from GoogleCloudSCC import prepare_hr_and_ec_for_cloud_asset_list with open("./test_data/cloud_assets_list_response.json") as f: cloud_assets_response = json.load(f) with open("./test_data/cloud_assets_list_ec.json") as f: cloud_assets_ec = json.load(f) hr, ec = prepare_hr_and_ec_for_cloud_asset_list(cloud_assets_response) assert cloud_assets_ec == ec assert "Asset Name" in hr assert "Asset Type" in hr assert "Parent" in hr assert "Discovery Name" in hr assert "Ancestors" in hr assert "Update Time (In UTC)" in hr def test_prepare_hr_and_ec_for_cloud_assets_list_no_record(): """ Scenario: Validates human readable and entry context for cloud assets list Given: - cloud asset response given When: - Zero records found Then: - Ensure finding HR and EC. """ from GoogleCloudSCC import prepare_hr_and_ec_for_cloud_asset_list response = {"assets": []} hr, context = prepare_hr_and_ec_for_cloud_asset_list(response) assert context == {} assert hr == ERROR_MESSAGES["NO_RECORDS_FOUND"].format("resource") def test_cloud_asset_list_command(cloud_asset_client): """ Scenario: Validates command result for cloud asset-resource-list command. Given: - command arguments given for cloud asset resource list command Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import cloud_asset_list_command with open("test_data/cloud_assets_list_response.json") as file: mock_data = json.load(file) with open("./test_data/cloud_assets_list_ec.json") as f: asset_ec = json.load(f) with open("test_data/cloud_assets_list_hr.md") as f: hr_output = f.read() GoogleNameParser.get_organization_id = Mock(return_value="123") cloud_asset_client.get_assets = Mock(return_value=mock_data) arguments = {"parent": "project/123456789000"} command_output = cloud_asset_list_command(cloud_asset_client, arguments) assert command_output.outputs == asset_ec assert command_output.raw_response == mock_data assert command_output.readable_output == hr_output @pytest.mark.parametrize( "test_case, project_names, max_iterations, call_count", [ ("one-iteration-one-found", "projects/123456789", 2, 1), ("two-iteration-two-found", "projects/123456789,projects/234567890", 2, 2), ("two-iteration-one-only-found-no-token", "projects/123456789,projects/234567890", 2, 1), ("two-iteration-one-only-found-max-iteration-reached", "projects/123456789,projects/234567890", 1, 1), ], ) def test_cloud_asset_owner_get_command(cloud_asset_client, test_case, project_names, max_iterations, call_count): """ Scenario: Validates command result for cloud asset-owner-get command. Given: - command arguments given for cloud asset owner get command Then: - Ensure command should call the API expected times and return appropriate outputs. """ from GoogleCloudSCC import cloud_asset_owner_get_command with open("test_data/cloud_assets_owners_get_response.json") as file: mock_data = json.load(file)["test-cases"][test_case] with open("test_data/cloud_assets_owners_get_hr.json") as file: hr_output = json.load(file)["test-cases"][test_case] GoogleNameParser.get_organization_id = Mock(return_value="123") cloud_asset_client.get_assets = Mock(side_effect=mock_data["responses"]) arguments = {"projectName": project_names, "maxIteration": max_iterations} command_output = cloud_asset_owner_get_command(cloud_asset_client, arguments) assert command_output.outputs == mock_data["outputs"] assert cloud_asset_client.get_assets.call_count == call_count assert command_output.raw_response == mock_data["outputs"] assert command_output.readable_output == hr_output def test_finding_update_command(client, mocker): """ Scenario: Validates command result for update-finding command. Given: - command arguments given for update finding command Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import demisto, finding_update_command with open("test_data/update_finding_response.json") as file: mock_data = json.load(file) with open("./test_data/update_finding_ec.json") as f: finding_ec = json.load(f) with open("./test_data/update_finding_hr.md") as f: hr_output = f.read() client.update_finding = Mock(return_value=mock_data) params = { "organization_id": "123", } mocker.patch.object(demisto, "params", return_value=params) arguments = {"status": "ACTIVE"} command_output = finding_update_command(client, arguments) assert command_output.outputs_key_field == "name" assert command_output.raw_response == mock_data assert command_output.to_context()["EntryContext"] == finding_ec assert command_output.readable_output == hr_output def test_prepare_hr_and_ec_for_cloud_asset_owners_get(): """ Scenario: Validates human readable and entry context for cloud assets owners get Given: - project asset response given Then: - Ensure HR and entry context is correct """ from GoogleCloudSCC import prepare_hr_and_ec_for_cloud_asset_owners_get with open("./test_data/cloud_assets_owners_get_assets.json") as f: cloud_assets_response = json.load(f) with open("./test_data/cloud_assets_owners_get_ec.json") as f: cloud_assets_ec = json.load(f) hr, ec = prepare_hr_and_ec_for_cloud_asset_owners_get(cloud_assets_response["assets"], cloud_assets_response["readTime"]) assert cloud_assets_ec == ec assert "Project Name" in hr assert "Project Owner" in hr assert "Ancestors" in hr assert "Update Time (In UTC)" in hr def test_prepare_hr_and_ec_for_cloud_asset_owners_get_no_record(): """ Scenario: Validates human readable and entry context for cloud assets owners get Given: - project asset response given When: - Zero records found Then: - Ensure finding HR and EC. """ from GoogleCloudSCC import prepare_hr_and_ec_for_cloud_asset_owners_get hr, context = prepare_hr_and_ec_for_cloud_asset_owners_get([], "") assert context == [] assert hr == ERROR_MESSAGES["NO_RECORDS_FOUND"].format("project") @patch("GoogleCloudSCC.init_google_pubsub_client") def test_validate_project_and_subscription_id(mock1, pubsub_client): """ Scenario: Validates project ID and subscription ID Given: - configuration parameter When: - correct argument provided Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import validate_project_and_subscription_id mock1.return_value = pubsub_client params = {"project_id": "project_id", "subscription_id": "subscription_id"} pubsub_client.pull_messages = Mock(return_value={}) validate_project_and_subscription_id(params) assert pubsub_client.pull_messages.call_count == 1 def test_get_http_client_with_proxy(mocker, client): """ Scenario: Validate that proxy is set in http object Given: - proxy insecure path to custom certificate When: - correct proxy, insecure and certificate path arguments provided Then: - Ensure command that proxy, insecure and certificate path should set in Http object """ mocker.patch("GoogleCloudSCC.handle_proxy", return_value={"https": "admin:password@127.0.0.1:3128"}) mocker.patch.dict(os.environ, {"REQUESTS_CA_BUNDLE": "path/to/cert"}) http_obj = client.get_http_client_with_proxy(True, True) assert http_obj.proxy_info.proxy_host == "127.0.0.1" assert http_obj.proxy_info.proxy_port == 3128 assert http_obj.proxy_info.proxy_user == "admin" assert http_obj.proxy_info.proxy_pass == "password" assert http_obj.disable_ssl_certificate_validation assert http_obj.ca_certs == "path/to/cert" def test_google_scc_class_wrapper_methods(client): """ Scenario: Validates helper method of GoogleSccClient Given: - configuration parameter Then: - Ensure wrapper method should return proper outputs. """ client.execute_request = Mock(return_value={"A": 123}) assert client.get_findings(parent="123") == {"A": 123} assert client.get_assets("parent", "duration", "mask", "filter", "order", "size", "token", "readtime") == {"A": 123} assert client.get_source("name") == {"A": 123} assert client.update_finding("name", "time", "severity", "url", None, []) == {"A": 123} def test_google_pubsub_wrapper_methods(pubsub_client): """ Scenario: Validates helper method of GooglePubSubClient Given: - configuration parameter Then: - Ensure wrapper method should return proper outputs. """ pubsub_client.execute_request = Mock(return_value={"B": 123}) assert pubsub_client.pull_messages("123") == {"B": 123} assert pubsub_client.acknowledge_messages(["123"]) == {"B": 123} def test_google_cloud_assets_class_wrapper_methods(cloud_asset_client): """ Scenario: Validates helper method of GoogleCloudAssetClient Given: - configuration parameter Then: - Ensure wrapper method should return proper outputs. """ cloud_asset_client.execute_request = Mock(return_value={"A": 123}) assert cloud_asset_client.get_assets("parent", "asset_types", "content_type", "10", "page_token", "read_time") == {"A": 123} def test_validate_state_and_severity_list(): """ Scenario: Validates state and severity. Given: - state and severity is given When: - incorrect state and severity given Then: - Ensure proper error message should raised. """ from GoogleCloudSCC import validate_state_and_severity_list with pytest.raises(ValueError, match=ERROR_MESSAGES["INVALID_STATE_ERROR"]): validate_state_and_severity_list(["INVALID"], []) with pytest.raises(ValueError, match=ERROR_MESSAGES["INVALID_SEVERITY_ERROR"]): validate_state_and_severity_list(["ACTIVE"], ["INVALID"]) def test_flatten_keys_to_root_negative(): """ Scenario: Validates dictionary Given: - nested dict given Then: - Ensure proper dict should returned. """ from GoogleCloudSCC import flatten_keys_to_root input_dict = {"A": {"AA": 1}, "B": ["C"]} flatten_keys_to_root(input_dict, ["C"], {}) assert input_dict == {"A": {"AA": 1}, "B": ["C"], "C": None} flatten_keys_to_root(input_dict, ["A"], {}) assert input_dict == {"AA": 1, "B": ["C"], "C": None} def test_find_asset_owners(): """ Scenario: Validates find_asset_owners function Given: - minimized cloud asset Then: - Ensure that owners list is returned. """ from GoogleCloudSCC import find_asset_owners input_asset = { "iamPolicy": { "bindings": [ {"members": ["serviceAccount:service-12345"], "role": "roles/cloudfunctions.serviceAgent"}, {"members": ["cloudservices.gserviceaccount.com", "serviceAccount.gserviceaccount.com"], "role": "roles/owner"}, {"members": ["serviceAccount:firebase-dummy-account"], "role": "roles/firebase.managementServiceAgent"}, ] } } expected_owners = ["cloudservices.gserviceaccount.com", "serviceAccount.gserviceaccount.com"] assert expected_owners == find_asset_owners(input_asset) def test_find_asset_owners_no_record(): """ Scenario: Validates find_asset_owners function Given: - minimized asset without owners - empty input Then: - Ensure that empty list is returned. """ from GoogleCloudSCC import find_asset_owners input_asset = { "iamPolicy": { "bindings": [ {"members": ["serviceAccount:service-12345"], "role": "roles/cloudfunctions.serviceAgent"}, {"members": ["serviceAccount:firebase-dummy-account"], "role": "roles/firebase.managementServiceAgent"}, ] } } assert find_asset_owners(input_asset) == [] assert find_asset_owners({}) == [] def test_validate_with_regex(): """ Scenario: Validates validate_with_regex function Given: - pattern - string - validation_message When: - pattern matches - pattern does not match Then: - Ensure ValueError with provided message is raised when pattern does not match. """ from GoogleCloudSCC import validate_with_regex validate_with_regex("validation error", r"^\d{1,4}$", "123") with pytest.raises(ValueError) as e: validate_with_regex("validation error", r"^\d{1,4}$", "12345") assert str(e.value) == "validation error" def test_finding_state_update_command(client): """ Scenario: Validates command result for update-finding command. Given: - command arguments given for update finding command Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import finding_state_update_command with open("test_data/update_finding_response.json") as file: mock_data = json.load(file) with open("./test_data/update_finding_ec.json") as f: finding_ec = json.load(f) with open("test_data/update_finding_hr.md") as f: hr_output = f.read() GoogleNameParser.get_organization_id = Mock(return_value="123") client.update_state = Mock(return_value=mock_data) arguments = {"state": "ACTIVE", "name": "name"} command_output = finding_state_update_command(client, arguments) assert command_output.outputs_key_field == "name" assert command_output.raw_response == mock_data assert command_output.to_context()["EntryContext"] == finding_ec assert command_output.readable_output == hr_output def test_finding_state_update_command_invalid_args(client): """ Scenario: Validates command result for update-finding command. Given: - command arguments given for update finding command Then: - Ensure command should return proper outputs. """ from GoogleCloudSCC import finding_state_update_command arguments = {"state": "dummy", "name": "name"} with pytest.raises(ValueError) as err: finding_state_update_command(client, arguments) assert str(err.value) == ERROR_MESSAGES["INVALID_STATE_ERROR"]