import codecs import json import os import random from dataclasses import dataclass from typing import Dict, List, Any, Optional import requests from locust import HttpUser from secrets_manager.python_ext import PythonSecretsManager PDP_INTEGRATION_TEST_APPLICATION = "pdp-integration-test" PDP_STRESS_TEST_APPLICATION = "ows-pdp-stress-test" # Secrets for ows-pdp-test application in auth0 AUTH0_PDP_TEST_APP_CLIENT_ID = "AUTH0_PDP_TEST_APP_CLIENT_ID" AUTH0_PDP_TEST_APP_CLIENT_SECRET = "AUTH0_PDP_TEST_APP_CLIENT_SECRET" AUTH_URL = "https://qa-orchard.auth0.com/oauth/token" AUTH_AUDIENCE = "https://workstation.qaorch.com/api" @dataclass class LoginInfo: """Holds auth0 login info.""" username: str password_secret: str sm_application: str profile_header_values: Optional[List] = None identity_uuid: str = "" token: str = "" profile_type: str = "FauxLabelProfile" PDP_TEST_USERS = [ LoginInfo(username="pdptest@theorchard.com", password_secret="AUTH0_PDP_TEST_USER_PASSWORD", identity_uuid="4d5f24f5-83f9-4989-9f82-0924a5feaf88", sm_application=PDP_INTEGRATION_TEST_APPLICATION), LoginInfo(username="pdpteststresssimple@theorchard.com", password_secret="SIMPLE_IDENTITY_PASSWORD", identity_uuid="ef7cd70d-4314-49f9-b158-f3b3be66372f", sm_application=PDP_STRESS_TEST_APPLICATION), LoginInfo(username="pdpteststresscomplicated@theorchard.com", identity_uuid="5f0f10e0-9a9a-48e5-9616-a02886837e9", password_secret="COMPLICATED_IDENTITY_PASSWORD", sm_application=PDP_STRESS_TEST_APPLICATION), ] def load_pdp_test_profiles_json() -> Dict[str, Any]: """ Fetch values stored in locust_load_tests/graphql_knowledge_search/pdp_test_profiles.json To regenerate this file: 1. Run this DB PR to add the QA test profiles: https://github.com/theorchard/database/pull/16700 2. Run the Neo4J query below and download the output as a JSON file. 3. Copy the file to locust_load_tests/graphql_knowledge_search/pdp_test_profiles.json Query: ``` MATCH(i:Identity)--(p:Profile)--(l:Label) WHERE i.email in ["pdpteststresscomplicated@theorchard.com", "pdptest@theorchard.com", "pdpteststresssimple@theorchard.com"] RETURN p.profileId as profile_id, p.uuid as profile_uuid, i.email as email, i.id as identity_uuid, l.uuid as tenant_uuid, l.id as vendor_id, p.profileType as profile_type, p.roles[0] as profile_role ORDER BY profile_id; ``` """ # Load files with test data _location = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) # uses codecs.open because neo4j desktop includes byte-order-mark (BOM) in the json export with codecs.open(os.path.join(_location, "pdp_test_profiles.json"), 'r', 'utf-8-sig') as f: PDP_TEST_PROFILES = json.load(f) print(f"loaded PDP_TEST_PROFILES[:5] {PDP_TEST_PROFILES[:5]}") profiles_by_email = dict() for profile_obj in PDP_TEST_PROFILES: email = profile_obj["email"] if email not in profiles_by_email: profiles_by_email[email] = [] profiles_by_email[email].append(profile_obj) return profiles_by_email def substr(lookup_term: str) -> str: """ Returns a random substring :param lookup_term: input string :return: a substring """ n = len(lookup_term) i = random.randint(0, n // 2) j = random.randint(n // 2 + 1, n) s = lookup_term[i:j] # return the original string if substr has only whitespace return s if bool(s.strip()) else lookup_term def generate_pdp_user_credentials() -> List[LoginInfo]: secrets_manager_client = PythonSecretsManager( environment="qa", service_name=PDP_INTEGRATION_TEST_APPLICATION ) auth_client_id = secrets_manager_client.get_cred(AUTH0_PDP_TEST_APP_CLIENT_ID) auth_client_secret = secrets_manager_client.get_cred( AUTH0_PDP_TEST_APP_CLIENT_SECRET ) test_user_list = [] profiles_by_email = load_pdp_test_profiles_json() for user in PDP_TEST_USERS: token = generate_auth_token(user, auth_client_id=auth_client_id, auth_client_secret=auth_client_secret) user.token = f"Bearer {token}" user.profile_header_values = profiles_by_email.get(user.username) test_user_list.append(user) return test_user_list def generate_auth_token(user: LoginInfo, auth_client_id: str, auth_client_secret: str) -> Dict[str, str]: """Generate Auth0 token for pdp test user.""" secrets_manager_client = PythonSecretsManager( environment="qa", service_name=user.sm_application ) password = secrets_manager_client.get_cred(user.password_secret) data = { "grant_type": "password", "username": user.username, "password": password, "audience": AUTH_AUDIENCE, "scope": "", "client_id": auth_client_id, "client_secret": auth_client_secret, } r = requests.post(AUTH_URL, data=data) resp = r.json() if "error" in resp: raise ValueError(f"Auth0 error occurred: {resp}") return resp["access_token"] def graphql_query(user: HttpUser, query: str, variables: Dict[str, str], name: str, headers: Dict[str, str]) -> requests.Response: lookup_term = variables["term"] identity_uuid = headers["Orchard-Identity-Id"] profile_uuid = headers["Orchard-Profile-Id"] resp = user.client.post( url="/graphql", name=name, data=json.dumps({ "query": query, "variables": variables }), headers=headers, ) if "TokenExpiredError" in resp.text: print(f"\n\nSTOPPING USER due to ERROR: {resp.status_code} / ${resp.text}\n\n") user.environment.runner.quit() try: total_records = resp.json().get('data', {}).get(name, {}).get('totalCount') print( f"[graphql_query] {name} identity:{identity_uuid} profile_uuid: '{profile_uuid}' lookup_term: '{lookup_term}' " f"returned {total_records} records") except AttributeError: # resp.json()['data'] can be null if the query is invalid. print(f"[graphql_query]:ERR data is null for {name} identity:{identity_uuid} lookup_term: '{lookup_term}'") pass return resp if __name__ == '__main__': generate_pdp_user_credentials()