diff --git a/api/tests/performance/scenarios/resources.py b/api/tests/performance/scenarios/resources.py index a8e2a62c20..603ccc6b03 100644 --- a/api/tests/performance/scenarios/resources.py +++ b/api/tests/performance/scenarios/resources.py @@ -1,38 +1,66 @@ -from locust import HttpUser, task, events -from utils.helpers import get_api_token, get_auth_headers, get_random_resource_id +from locust import task, events +from utils.helpers import ( + APIUserBase, + get_api_token, + get_auth_headers, + get_sort_value, + get_available_resource_filters , + get_next_resource_filter +) +from utils.config import ( + RESOURCES_UI_SORT_VALUES, + RESOURCE_INSERTED_AT, +) -GLOBAL = {"token": None, "resource_ids": []} +GLOBAL = {"token": None, "resource_ids": [], "resource_filters": None} @events.test_start.add_listener def on_test_start(environment, **kwargs): GLOBAL["token"] = get_api_token(environment.host) + GLOBAL["resource_filters"] = get_available_resource_filters(environment.host, GLOBAL["token"]) -class ResourceUser(HttpUser): +class ResourceUser(APIUserBase): def on_start(self): self.token = GLOBAL["token"] self.headers = get_auth_headers(self.token) + self.available_resource_filters = GLOBAL["resource_filters"] - with self.client.get( - "/resources", headers=self.headers, name="/resources", catch_response=True - ) as response: - if response.status_code == 200: - json_data = response.json() - GLOBAL["resource_ids"] = [ - item["id"] for item in json_data.get("data", [])[:10] - ] - else: - response.failure("Failed to load /resources") + @task + def resources_default(self): + name = "GET /resources" + page_number = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page_number}" + f"&{get_sort_value(RESOURCES_UI_SORT_VALUES)}" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) @task(3) - def list_resources(self): - self.client.get("/resources", headers=self.headers, name="/resources") + def resource_with_include(self): + name = "GET /resources (with include)" + page = self._next_page(name) + endpoint = ( + f"/resources?page[number]={page}" + f"&{get_sort_value(RESOURCES_UI_SORT_VALUES)}" + f"&include=findings,provider" + ) + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) @task(2) - def get_single_resource(self): - if GLOBAL["resource_ids"]: - resource_id = get_random_resource_id(GLOBAL["resource_ids"]) - self.client.get( - f"/resources/{resource_id}", headers=self.headers, name="/resources/:id" - ) + def resource_filter(self): + name = "GET /resources (random filter)" + filter_type, filter_value = get_next_resource_filter(self.available_resource_filters) + endpoint = f"/resources?filter[{filter_type}]={filter_value}" + self.client.get(endpoint, headers=get_auth_headers(self.token), name=name) + + @task(2) + def resource_filter_with_include(self): + name = "GET /resources (random filter + include)" + filter_type, filter_value = get_next_resource_filter(self.available_resource_filters) + endpoint = ( + f"/resources?filter[{filter_type}]={filter_value}" + f"&include=findings,provider" + ) + 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..c27c529f35 100644 --- a/api/tests/performance/utils/config.py +++ b/api/tests/performance/utils/config.py @@ -17,3 +17,6 @@ FINDINGS_RESOURCE_METADATA = { S_PROVIDER_NAME = "provider-50k" M_PROVIDER_NAME = "provider-250k" L_PROVIDER_NAME = "provider-500k" + +RESOURCES_UI_SORT_VALUES = ["name", "region", "inserted_at"] +RESOURCE_INSERTED_AT = os.environ.get("RESOURCE_INSERTED_AT", "2025-04-14") diff --git a/api/tests/performance/utils/helpers.py b/api/tests/performance/utils/helpers.py index 881875e9cb..e76b21b3e7 100644 --- a/api/tests/performance/utils/helpers.py +++ b/api/tests/performance/utils/helpers.py @@ -167,23 +167,42 @@ def get_sort_value(sort_values: list) -> str: """ return f"sort={','.join(sort_values)}" - -def get_random_resource_id(resource_list): +def get_available_resource_filters(host: str, token: str) -> dict: """ - Selects and returns a random resource ID from the provided list. + Fetches and returns available resource filter values from the API. Args: - resource_list (list): A list of resource IDs. + host (str): The host URL of the API. + token (str): Bearer token for authentication. Returns: - Any: A randomly selected resource ID from the list. + dict: A dictionary containing lists of unique values for each resource filter type. + Example: + { + "service": ["ec2", "s3", "rds"], + "type": ["instance", "bucket"], + "region": ["us-east-1", "us-west-2"] + } Raises: - ValueError: If the provided list is empty. - - Example: - resource_id = get_random_resource_id(GLOBAL["resource_ids"]) + AssertionError: If the API request fails or does not return a 200 status code. """ - if not resource_list: - raise ValueError("The resource list is empty.") - return random.choice(resource_list) + + url = f"{host}/resources" + params = { + "fields[resources]": "type,region,service" + } + + response = requests.get(url, headers=get_auth_headers(token), params=params) + assert response.status_code == 200, f"Failed to fetch filters: {response.text}" + + resources = response.json()["data"] + filters = {"service": set(), "type": set(), "region": set()} + + for res in resources: + attr = res["attributes"] + filters["service"].add(attr["service"]) + filters["type"].add(attr["type"]) + filters["region"].add(attr["region"]) + + return {k: list(v) for k, v in filters.items()}