diff --git a/api/tests/performance/scenarios/resources.py b/api/tests/performance/scenarios/resources.py new file mode 100644 index 0000000000..dabf99c3f2 --- /dev/null +++ b/api/tests/performance/scenarios/resources.py @@ -0,0 +1,234 @@ +from locust import events, task +from utils.config import ( + L_PROVIDER_NAME, + M_PROVIDER_NAME, + RESOURCES_UI_FIELDS, + S_PROVIDER_NAME, + TARGET_INSERTED_AT, +) +from utils.helpers import ( + APIUserBase, + get_api_token, + get_auth_headers, + get_dynamic_filters_pairs, + get_next_resource_filter, + get_scan_id_from_provider_name, +) + +GLOBAL = { + "token": None, + "scan_ids": {}, + "resource_filters": None, + "large_resource_filters": None, +} + + +@events.test_start.add_listener +def on_test_start(environment, **kwargs): + GLOBAL["token"] = get_api_token(environment.host) + + GLOBAL["scan_ids"]["small"] = get_scan_id_from_provider_name( + environment.host, GLOBAL["token"], S_PROVIDER_NAME + ) + GLOBAL["scan_ids"]["medium"] = get_scan_id_from_provider_name( + environment.host, GLOBAL["token"], M_PROVIDER_NAME + ) + GLOBAL["scan_ids"]["large"] = get_scan_id_from_provider_name( + environment.host, GLOBAL["token"], L_PROVIDER_NAME + ) + + GLOBAL["resource_filters"] = get_dynamic_filters_pairs( + environment.host, GLOBAL["token"], "resources" + ) + GLOBAL["large_resource_filters"] = get_dynamic_filters_pairs( + environment.host, GLOBAL["token"], "resources", GLOBAL["scan_ids"]["large"] + ) + + +class APIUser(APIUserBase): + def on_start(self): + self.token = GLOBAL["token"] + self.s_scan_id = GLOBAL["scan_ids"]["small"] + self.m_scan_id = GLOBAL["scan_ids"]["medium"] + self.l_scan_id = GLOBAL["scan_ids"]["large"] + self.available_resource_filters = GLOBAL["resource_filters"] + self.available_resource_filters_large_scan = GLOBAL["large_resource_filters"] + + @task + def resources_default(self): + name = "/resources" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" + f"&filter[updated_at]={TARGET_INSERTED_AT}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_default_ui_fields(self): + name = "/resources?fields" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" + f"&fields[resources]={','.join(RESOURCES_UI_FIELDS)}" + f"&filter[updated_at]={TARGET_INSERTED_AT}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_default_include(self): + name = "/resources?include" + page = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page}" + f"&filter[updated_at]={TARGET_INSERTED_AT}" + f"&include=provider" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_metadata(self): + name = "/resources/metadata" + endpoint = f"/resources/metadata?filter[updated_at]={TARGET_INSERTED_AT}" + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task + def resources_scan_small(self): + name = "/resources?filter[scan_id] - 50k" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" f"&filter[scan]={self.s_scan_id}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task + def resources_metadata_scan_small(self): + name = "/resources/metadata?filter[scan_id] - 50k" + endpoint = f"/resources/metadata?&filter[scan]={self.s_scan_id}" + self.client.get( + endpoint, + headers=get_auth_headers(self.token), + name=name, + ) + + @task(2) + def resources_scan_medium(self): + name = "/resources?filter[scan_id] - 250k" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" f"&filter[scan]={self.m_scan_id}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task + def resources_metadata_scan_medium(self): + name = "/resources/metadata?filter[scan_id] - 250k" + endpoint = f"/resources/metadata?&filter[scan]={self.m_scan_id}" + self.client.get( + endpoint, + headers=get_auth_headers(self.token), + name=name, + ) + + @task + def resources_scan_large(self): + name = "/resources?filter[scan_id] - 500k" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" f"&filter[scan]={self.l_scan_id}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task + def resources_scan_large_include(self): + name = "/resources?filter[scan_id]&include - 500k" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" + f"&filter[scan]={self.l_scan_id}" + f"&include=provider" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task + def resources_metadata_scan_large(self): + endpoint = f"/resources/metadata?&filter[scan]={self.l_scan_id}" + self.client.get( + endpoint, + headers=get_auth_headers(self.token), + name="/resources/metadata?filter[scan_id] - 500k", + ) + + @task(2) + def resources_filters(self): + name = "/resources?filter[resource_filter]&include" + filter_name, filter_value = get_next_resource_filter( + self.available_resource_filters + ) + + endpoint = ( + f"/resources?filter[{filter_name}]={filter_value}" + f"&filter[updated_at]={TARGET_INSERTED_AT}" + f"&include=provider" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_metadata_filters(self): + name = "/resources/metadata?filter[resource_filter]" + filter_name, filter_value = get_next_resource_filter( + self.available_resource_filters + ) + + endpoint = ( + f"/resources/metadata?filter[{filter_name}]={filter_value}" + f"&filter[updated_at]={TARGET_INSERTED_AT}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_metadata_filters_scan_large(self): + name = "/resources/metadata?filter[resource_filter]&filter[scan_id] - 500k" + filter_name, filter_value = get_next_resource_filter( + self.available_resource_filters + ) + + endpoint = ( + f"/resources/metadata?filter[{filter_name}]={filter_value}" + f"&filter[scan]={self.l_scan_id}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(2) + def resourcess_filter_large_scan_include(self): + name = "/resources?filter[resource_filter][scan]&include - 500k" + filter_name, filter_value = get_next_resource_filter( + self.available_resource_filters + ) + + endpoint = ( + f"/resources?filter[{filter_name}]={filter_value}" + f"&filter[scan]={self.l_scan_id}" + f"&include=provider" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_latest_default_ui_fields(self): + name = "/resources/latest?fields" + page_number = self._next_page(name) + endpoint = ( + f"/resources/latest?page[number]={page_number}" + f"&fields[resources]={','.join(RESOURCES_UI_FIELDS)}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(3) + def resources_latest_metadata_filters(self): + name = "/resources/metadata/latest?filter[resource_filter]" + filter_name, filter_value = get_next_resource_filter( + self.available_resource_filters + ) + + endpoint = f"/resources/metadata/latest?filter[{filter_name}]={filter_value}" + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) diff --git a/api/tests/performance/utils/config.py b/api/tests/performance/utils/config.py index febc5e0a24..8cbb2c0646 100644 --- a/api/tests/performance/utils/config.py +++ b/api/tests/performance/utils/config.py @@ -13,6 +13,23 @@ FINDINGS_RESOURCE_METADATA = { "resource_types": "resource_type", "services": "service", } +RESOURCE_METADATA = { + "regions": "region", + "types": "type", + "services": "service", +} + +RESOURCES_UI_FIELDS = [ + "name", + "failed_findings_count", + "region", + "service", + "type", + "provider", + "inserted_at", + "updated_at", + "uid", +] S_PROVIDER_NAME = "provider-50k" M_PROVIDER_NAME = "provider-250k" diff --git a/api/tests/performance/utils/helpers.py b/api/tests/performance/utils/helpers.py index 999a2d55c8..08144a5c4b 100644 --- a/api/tests/performance/utils/helpers.py +++ b/api/tests/performance/utils/helpers.py @@ -7,6 +7,7 @@ from locust import HttpUser, between from utils.config import ( BASE_HEADERS, FINDINGS_RESOURCE_METADATA, + RESOURCE_METADATA, TARGET_INSERTED_AT, USER_EMAIL, USER_PASSWORD, @@ -121,13 +122,16 @@ def get_scan_id_from_provider_name(host: str, token: str, provider_name: str) -> return response.json()["data"][0]["id"] -def get_resource_filters_pairs(host: str, token: str, scan_id: str = "") -> dict: +def get_dynamic_filters_pairs( + host: str, token: str, endpoint: str, scan_id: str = "" +) -> dict: """ - Retrieves and maps resource metadata filter values from the findings endpoint. + Retrieves and maps metadata filter values from a given endpoint. Args: host (str): The host URL of the API. token (str): Bearer token for authentication. + endpoint (str): The API endpoint to query for metadata. scan_id (str, optional): Optional scan ID to filter metadata. Defaults to using inserted_at timestamp. Returns: @@ -136,22 +140,28 @@ def get_resource_filters_pairs(host: str, token: str, scan_id: str = "") -> dict Raises: AssertionError: If the request fails or does not return a 200 status code. """ + metadata_mapping = ( + FINDINGS_RESOURCE_METADATA if endpoint == "findings" else RESOURCE_METADATA + ) + date_filter = "inserted_at" if endpoint == "findings" else "updated_at" metadata_filters = ( f"filter[scan]={scan_id}" if scan_id - else f"filter[inserted_at]={TARGET_INSERTED_AT}" + else f"filter[{date_filter}]={TARGET_INSERTED_AT}" ) response = requests.get( - f"{host}/findings/metadata?{metadata_filters}", headers=get_auth_headers(token) + f"{host}/{endpoint}/metadata?{metadata_filters}", + headers=get_auth_headers(token), ) assert ( response.status_code == 200 ), f"Failed to get resource filters values: {response.text}" attributes = response.json()["data"]["attributes"] + return { - FINDINGS_RESOURCE_METADATA[key]: values + metadata_mapping[key]: values for key, values in attributes.items() - if key in FINDINGS_RESOURCE_METADATA.keys() + if key in metadata_mapping.keys() }