diff --git a/azure-quantum/azure/quantum/_constants.py b/azure-quantum/azure/quantum/_constants.py index 2c7ca17a..231288ad 100644 --- a/azure-quantum/azure/quantum/_constants.py +++ b/azure-quantum/azure/quantum/_constants.py @@ -63,10 +63,14 @@ class ConnectionConstants: # pylint: disable=unnecessary-lambda-assignment GET_QUANTUM_PRODUCTION_ENDPOINT = \ lambda location: f"https://{location}.quantum.azure.com/" + GET_QUANTUM_PRODUCTION_ENDPOINT_v2 = \ + lambda location: f"https://{location}-v2.quantum.azure.com/" GET_QUANTUM_CANARY_ENDPOINT = \ lambda location: f"https://{location or 'eastus2euap'}.quantum.azure.com/" GET_QUANTUM_DOGFOOD_ENDPOINT = \ lambda location: f"https://{location}.quantum-test.azure.com/" + GET_QUANTUM_DOGFOOD_ENDPOINT_v2 = \ + lambda location: f"https://{location}-v2.quantum-test.azure.com/" ARM_PRODUCTION_ENDPOINT = "https://management.azure.com/" ARM_DOGFOOD_ENDPOINT = "https://api-dogfood.resources.windows-int.net/" diff --git a/azure-quantum/azure/quantum/_workspace_connection_params.py b/azure-quantum/azure/quantum/_workspace_connection_params.py index 8411fde2..4519034b 100644 --- a/azure-quantum/azure/quantum/_workspace_connection_params.py +++ b/azure-quantum/azure/quantum/_workspace_connection_params.py @@ -46,9 +46,20 @@ class WorkspaceConnectionParams: ResourceGroupName=(?P[^\s;]+); WorkspaceName=(?P[^\s;]+); ApiKey=(?P[^\s;]+); - QuantumEndpoint=(?Phttps://(?P[^\s\.]+).quantum(?:-test)?.azure.com/); + QuantumEndpoint=(?Phttps://(?P[^\s\.]+?)(?:-v2)?.quantum(?:-test)?.azure.com/); """, re.VERBOSE | re.IGNORECASE) + + WORKSPACE_NOT_FULLY_SPECIFIED_MSG = """ + Azure Quantum workspace not fully specified. + Please specify one of the following: + 1) A valid resource ID. + 2) A valid combination of subscription ID, + resource group name, and workspace name. + 3) A valid combination of workspace name and location. + 4) A valid workspace name. + 5) A valid connection string (via Workspace.from_connection_string()). + """ def __init__( self, @@ -85,6 +96,8 @@ def __init__( self.client_id = None self.tenant_id = None self.api_version = None + # Track if connection string was used + self._used_connection_string = False # callback to create a new client if needed # for example, when changing the user agent self.on_new_client_request = on_new_client_request @@ -235,6 +248,7 @@ def apply_connection_string(self, connection_string: str): if not match: raise ValueError("Invalid connection string") self._merge_re_match(match) + self._used_connection_string = True def merge( self, @@ -450,6 +464,32 @@ def get_full_user_agent(self): full_user_agent = (f"{app_id} {full_user_agent}" if full_user_agent else app_id) return full_user_agent + + def have_enough_for_discovery(self) -> bool: + """ + Returns true if we have enough parameters + to try to find the Azure Quantum Workspace. + """ + return (self.workspace_name + and self.get_credential_or_default()) + + def assert_have_enough_for_discovery(self): + """ + Raises ValueError if we don't have enough parameters + to try to find the Azure Quantum Workspace. + """ + if not self.have_enough_for_discovery(): + raise ValueError(self.WORKSPACE_NOT_FULLY_SPECIFIED_MSG) + + def can_build_resource_id(self) -> bool: + """ + Returns true if we have all necessary parameters + to identify the Azure Quantum Workspace resource. + """ + return (self.subscription_id + and self.resource_group + and self.workspace_name + and self.get_credential_or_default()) def is_complete(self) -> bool: """ @@ -460,6 +500,7 @@ def is_complete(self) -> bool: and self.subscription_id and self.resource_group and self.workspace_name + and self.quantum_endpoint and self.get_credential_or_default()) def assert_complete(self): @@ -468,15 +509,7 @@ def assert_complete(self): to connect to the Azure Quantum Workspace. """ if not self.is_complete(): - raise ValueError( - """ - Azure Quantum workspace not fully specified. - Please specify one of the following: - 1) A valid combination of location and resource ID. - 2) A valid combination of location, subscription ID, - resource group name, and workspace name. - 3) A valid connection string (via Workspace.from_connection_string()). - """) + raise ValueError(self.WORKSPACE_NOT_FULLY_SPECIFIED_MSG) def default_from_env_vars(self) -> WorkspaceConnectionParams: """ @@ -512,10 +545,13 @@ def default_from_env_vars(self) -> WorkspaceConnectionParams: or not self.workspace_name or not self.credential ): - self._merge_connection_params( - connection_params=WorkspaceConnectionParams( - connection_string=os.environ.get(EnvironmentVariables.CONNECTION_STRING)), - merge_default_mode=True) + env_connection_string = os.environ.get(EnvironmentVariables.CONNECTION_STRING) + if env_connection_string: + self._merge_connection_params( + connection_params=WorkspaceConnectionParams( + connection_string=env_connection_string), + merge_default_mode=True) + self._used_connection_string = True return self @classmethod diff --git a/azure-quantum/azure/quantum/workspace.py b/azure-quantum/azure/quantum/workspace.py index 284a9d5e..6e8fc2d3 100644 --- a/azure-quantum/azure/quantum/workspace.py +++ b/azure-quantum/azure/quantum/workspace.py @@ -49,6 +49,10 @@ get_container_uri, ContainerClient ) +from azure.core.exceptions import ResourceNotFoundError +from azure.mgmt.quantum import AzureQuantumMgmtClient +from azure.mgmt.resourcegraph import ResourceGraphClient +from azure.mgmt.resourcegraph.models import QueryRequest if TYPE_CHECKING: from azure.quantum.target import Target @@ -64,8 +68,10 @@ class Workspace: When creating a Workspace object, callers have two options for identifying the Azure Quantum workspace (in order of precedence): - 1. specify a valid location and resource ID; or - 2. specify a valid location, subscription ID, resource group, and workspace name. + 1. specify a valid resource ID; or + 2. specify a valid subscription ID, resource group, and workspace name; or + 3. specify a valid workspace name and location; or + 4. specify a valid workspace name. You can also use a connection string to specify the connection parameters to an Azure Quantum Workspace by calling @@ -110,6 +116,12 @@ class Workspace: Add the specified value as a prefix to the HTTP User-Agent header when communicating to the Azure Quantum service. """ + + # Internal parameter names + _FROM_CONNECTION_STRING_PARAM = '_from_connection_string' + _MGMT_CLIENT_PARAM = '_mgmt_client' + _RG_CLIENT_PARAM = '_rg_client' + def __init__( self, subscription_id: Optional[str] = None, @@ -122,6 +134,13 @@ def __init__( user_agent: Optional[str] = None, **kwargs: Any, ) -> None: + # Extract internal params before passing kwargs to WorkspaceConnectionParams + # Param to track whether the workspace was created from a connection string + from_connection_string = kwargs.pop(Workspace._FROM_CONNECTION_STRING_PARAM, False) + # Params to pass a mock in tests + mgmt_client = kwargs.pop(Workspace._MGMT_CLIENT_PARAM, None) + rg_client = kwargs.pop(Workspace._RG_CLIENT_PARAM, None) + connection_params = WorkspaceConnectionParams( location=location, subscription_id=subscription_id, @@ -135,7 +154,7 @@ def __init__( logger.info("Using %s environment.", connection_params.environment) - connection_params.assert_complete() + connection_params.assert_have_enough_for_discovery() connection_params.on_new_client_request = self._on_new_client_request @@ -145,8 +164,193 @@ def __init__( self._resource_group = connection_params.resource_group self._workspace_name = connection_params.workspace_name + # Populate workspace details from ARG if name is provided but missing subscription and/or resource group + if connection_params.workspace_name \ + and not connection_params.can_build_resource_id(): + arg_client = self._create_arg_client(connection_params, rg_client) + self._load_workspace_from_arg(arg_client) + + # pylint: disable=protected-access + using_connection_string = ( + from_connection_string + or connection_params._used_connection_string + ) + + # Populate workspace from ARM if not using connection string (API key) and not loaded from ARG + if not using_connection_string and not connection_params.is_complete(): + mgmt_client = self._create_mgmt_client(connection_params, mgmt_client) + self._load_workspace_from_arm(mgmt_client) + + connection_params.assert_complete() + # Create QuantumClient self._client = self._create_client() + + def _create_arg_client( + self, + connection_params: WorkspaceConnectionParams, + arg_client: Optional[ResourceGraphClient] = None + ) -> ResourceGraphClient: + if arg_client is not None: + return arg_client + + credential = connection_params.get_credential_or_default() + return ResourceGraphClient( + credential=credential, + base_url=connection_params.arm_endpoint + ) + + def _load_workspace_from_arg(self, arg_client: ResourceGraphClient) -> None: + """ + Queries Azure Resource Graph to find a workspace by name. + Populates subscription_id, resource_group, location, and quantum_endpoint params if found. + """ + connection_params = self._connection_params + + if not connection_params.workspace_name: + raise ValueError("Workspace name must be specified.") + + # Escape single quotes in parameters to prevent KQL injection + workspace_name = self._escape_kql_string(connection_params.workspace_name) + + query = f""" + Resources + | where type =~ 'microsoft.quantum/workspaces' + | where name =~ '{workspace_name}' + """ + + if connection_params.resource_group: + resource_group = self._escape_kql_string(connection_params.resource_group) + query += f"\n | where resourceGroup =~ '{resource_group}'" + + if connection_params.location: + location = self._escape_kql_string(connection_params.location) + query += f"\n | where location =~ '{location}'" + + query += """ + | extend endpointUri = tostring(properties.endpointUri) + | project name, subscriptionId, resourceGroup, location, endpointUri + """ + + subscriptions = [connection_params.subscription_id] if connection_params.subscription_id else None + query_request = QueryRequest(query=query, subscriptions=subscriptions) + + try: + response = arg_client.resources(query_request) + except Exception as e: + raise RuntimeError( + f"Failed to query workspace using ARG: {str(e)}.\n" + "Please retry later or try to specify subscription id and resource group." + ) from e + + if not response.data or len(response.data) == 0: + raise ValueError(f"No matching workspace found with name '{connection_params.workspace_name}'.\n" + "Please specify correct workspace name.") + + if len(response.data) > 1: + if not connection_params.location: + raise ValueError( + f"Multiple Azure Quantum workspaces found with name '{connection_params.workspace_name}'.\n" + "Please specify location." + ) + # not expected to reach here, so ask user to use all params + raise RuntimeError( + f"Multiple Azure Quantum workspaces found with name '{connection_params.workspace_name}' in location '{connection_params.location}'.\n" + "Please specify subscription id and resource group." + ) + + workspace_data = response.data[0] + + connection_params.subscription_id = workspace_data.get('subscriptionId') + connection_params.resource_group = workspace_data.get('resourceGroup') + connection_params.location = workspace_data.get('location') + connection_params.quantum_endpoint = workspace_data.get('endpointUri') + + logger.debug( + "Found workspace '%s' in subscription '%s', resource group '%s', location '%s', endpoint '%s'", + connection_params.workspace_name, + connection_params.subscription_id, + connection_params.resource_group, + connection_params.location, + connection_params.quantum_endpoint + ) + + # If one of the required parameters is missing, probably workspace in failed provisioning state + if not connection_params.is_complete(): + raise ValueError( + f"Failed to retrieve complete workspace details for workspace '{connection_params.workspace_name}'.\n" + "Please check that workspace is in valid state." + ) + + @staticmethod + def _escape_kql_string(value: str) -> str: + """Escape a string value for use in KQL queries.""" + if not value: + return value + # Escape backslashes first, then single quotes + return value.replace('\\', '\\\\').replace("'", "\\'") + + def _create_mgmt_client( + self, + connection_params: WorkspaceConnectionParams, + mgmt_client: Optional[AzureQuantumMgmtClient] = None + ) -> AzureQuantumMgmtClient: + if mgmt_client is not None: + return mgmt_client + + credential = connection_params.get_credential_or_default() + return AzureQuantumMgmtClient( + credential=credential, + subscription_id=connection_params.subscription_id, + base_url=connection_params.arm_endpoint + ) + + def _load_workspace_from_arm(self, mgmt_client: AzureQuantumMgmtClient) -> None: + """ + Fetches the workspace resource from ARM and sets location and endpoint URI params. + """ + connection_params = self._connection_params + + try: + workspace_resource = mgmt_client.workspaces.get( + resource_group_name=connection_params.resource_group, + workspace_name=connection_params.workspace_name + ) + except ResourceNotFoundError as e: + raise ValueError( + f"Azure Quantum workspace '{connection_params.workspace_name}' " + f"not found in resource group '{connection_params.resource_group}' " + f"and subscription '{connection_params.subscription_id}'." + ) from e + except Exception as e: + raise RuntimeError( + f"Failed to fetch workspace from ARM: {str(e)}.\n Please retry later." + ) from e + + # Extract and apply location + if workspace_resource.location: + connection_params.location = workspace_resource.location + logger.debug( + "Updated workspace location from ARM: %s", + workspace_resource.location + ) + else: + raise ValueError( + f"Failed to retrieve location for workspace '{connection_params.workspace_name}'.\n" + f"Please check that workspace is in valid state." + ) + + # Extract and apply endpoint URI from properties + if workspace_resource.properties and workspace_resource.properties.endpoint_uri: + connection_params.quantum_endpoint = workspace_resource.properties.endpoint_uri + logger.debug( + "Updated workspace endpoint from ARM: %s", connection_params.quantum_endpoint + ) + else: + raise ValueError( + f"Failed to retrieve endpoint uri for workspace '{connection_params.workspace_name}'.\n" + f"Please check that workspace is in valid state." + ) def _on_new_client_request(self) -> None: """ @@ -277,6 +481,7 @@ def from_connection_string(cls, connection_string: str, **kwargs) -> Workspace: :rtype: Workspace """ connection_params = WorkspaceConnectionParams(connection_string=connection_string) + kwargs[cls._FROM_CONNECTION_STRING_PARAM] = True return cls( subscription_id=connection_params.subscription_id, resource_group=connection_params.resource_group, diff --git a/azure-quantum/requirements.txt b/azure-quantum/requirements.txt index 03dbc354..11d881bd 100644 --- a/azure-quantum/requirements.txt +++ b/azure-quantum/requirements.txt @@ -1,5 +1,7 @@ azure-core>=1.30,<2.0 azure-identity>=1.17,<2.0 +azure-mgmt-quantum>=1.0.0b4,<2.0 +azure-mgmt-resourcegraph>=8.0.1,<9.0 # TODO: recent versions break recordings. # More than one match for "https://mystorage.blob.core.windows.net/.../rawOutputData" azure-storage-blob==12.20 diff --git a/azure-quantum/tests/unit/common.py b/azure-quantum/tests/unit/common.py index 49b64242..2d0746a2 100644 --- a/azure-quantum/tests/unit/common.py +++ b/azure-quantum/tests/unit/common.py @@ -7,7 +7,8 @@ import os import json import time -from unittest.mock import patch +from typing import Optional, Any +from unittest.mock import patch, MagicMock from vcr.request import Request as VcrRequest from azure_devtools.scenario_tests.base import ReplayableTest @@ -40,6 +41,7 @@ WORKSPACE = "myworkspace" LOCATION = "eastus" STORAGE = "mystorage" +ENDPOINT_URI = "https://myworkspace.eastus.quantum.azure.com/" API_KEY = "myapikey" APP_ID = "testapp" DEFAULT_TIMEOUT_SECS = 300 @@ -285,6 +287,22 @@ def clear_env_vars(self, os_environ): if env_var in os_environ: del os_environ[env_var] + def create_mock_mgmt_client(self, location: str = None) -> MagicMock: + """ + Create a mock Azure Quantum Management Client to avoid ARM calls during tests. + + :param location: + The location to use for the workspace resource. + """ + mock_mgmt_client = MagicMock() + mock_workspace_resource = MagicMock() + mock_workspace_resource.location = location + mock_workspace_resource.properties = MagicMock() + mock_workspace_resource.properties.endpoint_uri = ConnectionConstants.GET_QUANTUM_PRODUCTION_ENDPOINT(location) + mock_mgmt_client.workspaces.get.return_value = mock_workspace_resource + + return mock_mgmt_client + def create_workspace( self, credential = None, @@ -303,6 +321,9 @@ def create_workspace( client_id=ZERO_UID, client_secret=PLACEHOLDER) + # Create mock mgmt_client to avoid ARM calls during tests + mock_mgmt_client = self.create_mock_mgmt_client(location=connection_params.location) + workspace = Workspace( credential=credential, subscription_id=connection_params.subscription_id, @@ -310,11 +331,44 @@ def create_workspace( name=connection_params.workspace_name, location=connection_params.location, user_agent=connection_params.user_agent_app_id, + _mgmt_client=mock_mgmt_client, **kwargs ) return workspace + def create_workspace_with_params( + self, + subscription_id: Optional[str] = None, + resource_group: Optional[str] = None, + name: Optional[str] = None, + storage: Optional[str] = None, + resource_id: Optional[str] = None, + location: Optional[str] = None, + credential: Optional[object] = None, + user_agent: Optional[str] = None, + **kwargs: Any) -> Workspace: + """ + Create workspace with explicit parameters, using a mock management client + to avoid ARM calls during tests. + """ + mock_mgmt_client = self.create_mock_mgmt_client(location=location) + + workspace = Workspace( + subscription_id=subscription_id, + resource_group=resource_group, + name=name, + storage=storage, + resource_id=resource_id, + location=location, + credential=credential, + user_agent=user_agent, + _mgmt_client=mock_mgmt_client, + **kwargs + ) + + return workspace + def create_echo_target( self, credential = None, diff --git a/azure-quantum/tests/unit/recordings/test_create_workspace_instance_valid.yaml b/azure-quantum/tests/unit/recordings/test_create_workspace_instance_valid.yaml new file mode 100644 index 00000000..a95b5e80 --- /dev/null +++ b/azure-quantum/tests/unit/recordings/test_create_workspace_instance_valid.yaml @@ -0,0 +1,292 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://myworkspace.eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://myworkspace.eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://myworkspace.eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://myworkspace.eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n "}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n "}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | where location =~ ''eastus''\n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n "}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n ", "subscriptions": ["11111111-2222-3333-4444-555555555555"]}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | where location =~ ''eastus''\n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n ", "subscriptions": ["11111111-2222-3333-4444-555555555555"]}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | where resourceGroup =~ ''myresourcegroup''\n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n "}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: '{"query": "\n Resources\n | where type =~ ''microsoft.quantum/workspaces''\n | where name =~ ''myworkspace''\n \n | where resourceGroup =~ ''myresourcegroup''\n | where location =~ ''eastus''\n | extend endpointUri = tostring(properties.endpointUri)\n | project name, subscriptionId, resourceGroup, location, endpointUri\n "}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.5 + method: POST + uri: https://management.azure.com/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 + response: + body: + string: '{"totalRecords": 1, "count": 1, "data": [{"name": "myworkspace", "subscriptionId": "11111111-2222-3333-4444-555555555555", "resourceGroup": "myresourcegroup", "location": "eastus", "endpointUri": "https://myworkspace.eastus.quantum.azure.com/"}], "facets": [], "resultTruncated": "false"}' + headers: + content-length: + - '286' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +version: 1 diff --git a/azure-quantum/tests/unit/recordings/test_create_workspace_locations.yaml b/azure-quantum/tests/unit/recordings/test_create_workspace_locations.yaml new file mode 100644 index 00000000..91072023 --- /dev/null +++ b/azure-quantum/tests/unit/recordings/test_create_workspace_locations.yaml @@ -0,0 +1,29 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +version: 1 diff --git a/azure-quantum/tests/unit/recordings/test_workspace_from_connection_string.yaml b/azure-quantum/tests/unit/recordings/test_workspace_from_connection_string.yaml new file mode 100644 index 00000000..cddbfe2e --- /dev/null +++ b/azure-quantum/tests/unit/recordings/test_workspace_from_connection_string.yaml @@ -0,0 +1,108 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_secret=PLACEHOLDER&client_info=1&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '146' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.13.9 (Windows-11-10.0.26200-SP0) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - win32 + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791503872, "ext_expires_in": + 1791503872, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +version: 1 diff --git a/azure-quantum/tests/unit/recordings/test_workspace_get_target_ionq.yaml b/azure-quantum/tests/unit/recordings/test_workspace_get_target_ionq.yaml index fc870705..2ed2cf25 100644 --- a/azure-quantum/tests/unit/recordings/test_workspace_get_target_ionq.yaml +++ b/azure-quantum/tests/unit/recordings/test_workspace_get_target_ionq.yaml @@ -1,4 +1,110 @@ interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_assertion=PLACEHOLDER&client_info=1&client_assertion_type=PLACEHOLDER&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '181' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - linux + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791505688, "ext_expires_in": + 1791505688, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK - request: body: null headers: diff --git a/azure-quantum/tests/unit/recordings/test_workspace_get_targets_ionq.yaml b/azure-quantum/tests/unit/recordings/test_workspace_get_targets_ionq.yaml index 32bc10a1..5917afe0 100644 --- a/azure-quantum/tests/unit/recordings/test_workspace_get_targets_ionq.yaml +++ b/azure-quantum/tests/unit/recordings/test_workspace_get_targets_ionq.yaml @@ -1,4 +1,110 @@ interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_assertion=PLACEHOLDER&client_info=1&client_assertion_type=PLACEHOLDER&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '181' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - linux + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791505688, "ext_expires_in": + 1791505688, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK - request: body: null headers: diff --git a/azure-quantum/tests/unit/recordings/test_workspace_get_targets_quantinuum.yaml b/azure-quantum/tests/unit/recordings/test_workspace_get_targets_quantinuum.yaml index e1f09c8c..f3e1c541 100644 --- a/azure-quantum/tests/unit/recordings/test_workspace_get_targets_quantinuum.yaml +++ b/azure-quantum/tests/unit/recordings/test_workspace_get_targets_quantinuum.yaml @@ -1,4 +1,110 @@ interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_assertion=PLACEHOLDER&client_info=1&client_assertion_type=PLACEHOLDER&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '181' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - linux + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791505688, "ext_expires_in": + 1791505688, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK - request: body: null headers: diff --git a/azure-quantum/tests/unit/recordings/test_workspace_get_targets_result_type.yaml b/azure-quantum/tests/unit/recordings/test_workspace_get_targets_result_type.yaml index 26db532b..7ed739da 100644 --- a/azure-quantum/tests/unit/recordings/test_workspace_get_targets_result_type.yaml +++ b/azure-quantum/tests/unit/recordings/test_workspace_get_targets_result_type.yaml @@ -1,4 +1,110 @@ interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_assertion=PLACEHOLDER&client_info=1&client_assertion_type=PLACEHOLDER&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '181' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - linux + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791505688, "ext_expires_in": + 1791505688, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK - request: body: null headers: diff --git a/azure-quantum/tests/unit/recordings/test_workspace_job_quotas.yaml b/azure-quantum/tests/unit/recordings/test_workspace_job_quotas.yaml index 4bd638b7..22bfe54c 100644 --- a/azure-quantum/tests/unit/recordings/test_workspace_job_quotas.yaml +++ b/azure-quantum/tests/unit/recordings/test_workspace_job_quotas.yaml @@ -1,4 +1,110 @@ interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_assertion=PLACEHOLDER&client_info=1&client_assertion_type=PLACEHOLDER&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '181' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - linux + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791505688, "ext_expires_in": + 1791505688, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK - request: body: null headers: diff --git a/azure-quantum/tests/unit/recordings/test_workspace_list_jobs.yaml b/azure-quantum/tests/unit/recordings/test_workspace_list_jobs.yaml index 6f17a201..4dec255a 100644 --- a/azure-quantum/tests/unit/recordings/test_workspace_list_jobs.yaml +++ b/azure-quantum/tests/unit/recordings/test_workspace_list_jobs.yaml @@ -1,4 +1,110 @@ interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + method: GET + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0/.well-known/openid-configuration + response: + body: + string: '{"token_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token", + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", + "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/discovery/v2.0/keys", + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": + ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": + ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": + ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0", + "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", + "authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/authorize", + "device_authorization_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/devicecode", + "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": + "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/logout", + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", + "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", + "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", + "email"], "kerberos_endpoint": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/kerberos", + "tenant_region_scope": "WW", "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", + "rbac_url": "https://pas.windows.net"}' + headers: + content-length: + - '1826' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: client_id=PLACEHOLDER&grant_type=client_credentials&client_assertion=PLACEHOLDER&client_info=1&client_assertion_type=PLACEHOLDER&scope=https%3A%2F%2Fmanagement.azure.com%2F.default + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '181' + Content-Type: + - application/x-www-form-urlencoded + User-Agent: + - azsdk-python-identity/1.25.0 Python/3.12.3 (Linux-6.6.87.2-microsoft-standard-WSL2-aarch64-with-glibc2.39) + x-client-current-telemetry: + - 4|730,2| + x-client-os: + - linux + x-client-sku: + - MSAL.Python + x-client-ver: + - 1.34.0 + method: POST + uri: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token + response: + body: + string: '{"token_type": "Bearer", "expires_in": 1791505688, "ext_expires_in": + 1791505688, "refresh_in": 31536000, "access_token": "PLACEHOLDER"}' + headers: + content-length: + - '135' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.5 + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK - request: body: null headers: diff --git a/azure-quantum/tests/unit/recordings/test_workspace_user_agent_appid.yaml b/azure-quantum/tests/unit/recordings/test_workspace_user_agent_appid.yaml new file mode 100644 index 00000000..6f025b17 --- /dev/null +++ b/azure-quantum/tests/unit/recordings/test_workspace_user_agent_appid.yaml @@ -0,0 +1,164 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - azsdk-python-mgmt-quantum/1.0.0b5 Python/3.13.9 (Windows-11-10.0.26200-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace?api-version=2023-11-13-preview + response: + body: + string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Quantum/workspaces/myworkspace", + "name": "myworkspace", "type": "Microsoft.Quantum/workspaces", "location": + "eastus", "properties": {"endpointUri": "https://eastus.quantum.azure.com/", + "provisioningState": "Succeeded", "usable": "Yes"}}' + headers: + content-length: + - '385' + content-type: + - application/json; charset=utf-8 + status: + code: 200 + message: OK +version: 1 diff --git a/azure-quantum/tests/unit/test_cirq.py b/azure-quantum/tests/unit/test_cirq.py index c4b3803e..12729678 100644 --- a/azure-quantum/tests/unit/test_cirq.py +++ b/azure-quantum/tests/unit/test_cirq.py @@ -64,9 +64,9 @@ def test_cirq_service_init_with_workspace_not_raises_deprecation(self): # Cause all warnings to always be triggered. warnings.simplefilter("always") # Try to trigger a warning. - workspace = Workspace( + workspace = self.create_workspace_with_params( resource_id=SIMPLE_RESOURCE_ID, - location=LOCATION) + location=LOCATION) AzureQuantumService(workspace) # Verify @@ -75,36 +75,42 @@ def test_cirq_service_init_with_workspace_not_raises_deprecation(self): def test_cirq_service_init_without_workspace_raises_deprecation(self): # testing warning according to https://docs.python.org/3/library/warnings.html#testing-warnings import warnings - - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to trigger a warning. - AzureQuantumService( - resource_id=SIMPLE_RESOURCE_ID, - location=LOCATION) - # Verify - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "Consider passing \"workspace\" argument explicitly" in str(w[-1].message) - - # Validate rising deprecation warning even if workspace is passed, but other parameters are also passed - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to trigger a warning. - workspace = Workspace( - resource_id=SIMPLE_RESOURCE_ID, - location=LOCATION) - - AzureQuantumService( - workspace=workspace, + from unittest.mock import patch + + # Create mock mgmt_client to avoid ARM calls + mock_mgmt_client = self.create_mock_mgmt_client(location=LOCATION) + + with patch('azure.quantum.workspace.AzureQuantumMgmtClient', return_value=mock_mgmt_client): + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to trigger a warning. + AzureQuantumService( resource_id=SIMPLE_RESOURCE_ID, location=LOCATION) - # Verify - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "Consider passing \"workspace\" argument explicitly" in str(w[-1].message) + # Verify + assert len(w) == 1 + assert issubclass(w[-1].category, DeprecationWarning) + assert "Consider passing \"workspace\" argument explicitly" in str(w[-1].message) + + # Validate rising deprecation warning even if workspace is passed, but other parameters are also passed + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to trigger a warning. + workspace = Workspace( + resource_id=SIMPLE_RESOURCE_ID, + location=LOCATION, + _mgmt_client=mock_mgmt_client) + + AzureQuantumService( + workspace=workspace, + resource_id=SIMPLE_RESOURCE_ID, + location=LOCATION) + # Verify + assert len(w) == 1 + assert issubclass(w[-1].category, DeprecationWarning) + assert "Consider passing \"workspace\" argument explicitly" in str(w[-1].message) @pytest.mark.quantinuum @pytest.mark.ionq diff --git a/azure-quantum/tests/unit/test_pagination.py b/azure-quantum/tests/unit/test_pagination.py index ae93ed19..501d1712 100644 --- a/azure-quantum/tests/unit/test_pagination.py +++ b/azure-quantum/tests/unit/test_pagination.py @@ -19,7 +19,7 @@ class TestWorkspacePagination(QuantumTestBase): def test_filter_valid(self): - ws = Workspace( + ws = self.create_workspace_with_params( subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, name=WORKSPACE, @@ -42,7 +42,7 @@ def test_filter_valid(self): def test_orderby_valid(self): var_names = ["Name", "ItemType", "JobType", "ProviderId", "Target", "State", "CreationTime"] - ws = Workspace( + ws = self.create_workspace_with_params( subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, name=WORKSPACE, @@ -64,7 +64,7 @@ def test_orderby_valid(self): self.assertEqual(orderby, expected) def test_orderby_invalid(self): - ws = Workspace( + ws = self.create_workspace_with_params( subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, name=WORKSPACE, diff --git a/azure-quantum/tests/unit/test_qiskit.py b/azure-quantum/tests/unit/test_qiskit.py index 902c7df5..8b7c1104 100644 --- a/azure-quantum/tests/unit/test_qiskit.py +++ b/azure-quantum/tests/unit/test_qiskit.py @@ -590,8 +590,8 @@ def test_qiskit_provider_init_with_workspace_not_raises_deprecation(self): with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") - # Try to trigger a warning. - workspace = Workspace(resource_id=SIMPLE_RESOURCE_ID, location=LOCATION) + # Try to trigger a warning. + workspace = self.create_workspace_with_params(resource_id=SIMPLE_RESOURCE_ID, location=LOCATION) AzureQuantumProvider(workspace) warns = [ @@ -606,11 +606,17 @@ def test_qiskit_provider_init_with_workspace_not_raises_deprecation(self): def test_qiskit_provider_init_without_workspace_raises_deprecation(self): # testing warning according to https://docs.python.org/3/library/warnings.html#testing-warnings - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to trigger a warning. - AzureQuantumProvider(resource_id=SIMPLE_RESOURCE_ID, location=LOCATION) + from unittest.mock import patch + + # Create mock mgmt_client to avoid ARM calls + mock_mgmt_client = self.create_mock_mgmt_client(location=LOCATION) + + with patch('azure.quantum.workspace.AzureQuantumMgmtClient', return_value=mock_mgmt_client): + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to trigger a warning. + AzureQuantumProvider(resource_id=SIMPLE_RESOURCE_ID, location=LOCATION) warns = [ warn @@ -623,12 +629,15 @@ def test_qiskit_provider_init_without_workspace_raises_deprecation(self): assert len(warns) == 1 assert issubclass(warns[0].category, DeprecationWarning) - # Validate rising deprecation warning even if workspace is passed, but other parameters are also passed - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to trigger a warning. - workspace = Workspace(resource_id=SIMPLE_RESOURCE_ID, location=LOCATION) + # Validate rising deprecation warning even if workspace is passed, but other parameters are also passed + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to trigger a warning. + workspace = Workspace( + resource_id=SIMPLE_RESOURCE_ID, + location=LOCATION, + _mgmt_client=mock_mgmt_client) AzureQuantumProvider( workspace=workspace, resource_id=SIMPLE_RESOURCE_ID, location=LOCATION diff --git a/azure-quantum/tests/unit/test_workspace.py b/azure-quantum/tests/unit/test_workspace.py index 9c424c73..f1094b6a 100644 --- a/azure-quantum/tests/unit/test_workspace.py +++ b/azure-quantum/tests/unit/test_workspace.py @@ -12,7 +12,10 @@ WORKSPACE, LOCATION, STORAGE, + ENDPOINT_URI, API_KEY, + ZERO_UID, + PLACEHOLDER, ) from azure.quantum import Workspace from azure.quantum._constants import ( @@ -38,45 +41,96 @@ quantum_endpoint=ConnectionConstants.GET_QUANTUM_PRODUCTION_ENDPOINT(LOCATION) ) +SIMPLE_CONNECTION_STRING_V2 = ConnectionConstants.VALID_CONNECTION_STRING( + subscription_id=SUBSCRIPTION_ID, + resource_group=RESOURCE_GROUP, + workspace_name=WORKSPACE, + api_key=API_KEY, + quantum_endpoint=ConnectionConstants.GET_QUANTUM_PRODUCTION_ENDPOINT_v2(LOCATION) +) + class TestWorkspace(QuantumTestBase): def test_create_workspace_instance_valid(self): + def assert_all_required_params(ws: Workspace): + self.assertEqual(ws.subscription_id, SUBSCRIPTION_ID) + self.assertEqual(ws.resource_group, RESOURCE_GROUP) + self.assertEqual(ws.name, WORKSPACE) + self.assertEqual(ws.location, LOCATION) + self.assertEqual(ws._connection_params.quantum_endpoint, ENDPOINT_URI) + ws = Workspace( subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, name=WORKSPACE, - location=LOCATION, ) - self.assertEqual(ws.subscription_id, SUBSCRIPTION_ID) - self.assertEqual(ws.resource_group, RESOURCE_GROUP) - self.assertEqual(ws.name, WORKSPACE) - self.assertEqual(ws.location, LOCATION) + assert_all_required_params(ws) ws = Workspace( subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, name=WORKSPACE, - location=LOCATION, storage=STORAGE, ) + assert_all_required_params(ws) self.assertEqual(ws.storage, STORAGE) ws = Workspace( resource_id=SIMPLE_RESOURCE_ID, - location=LOCATION, ) - self.assertEqual(ws.subscription_id, SUBSCRIPTION_ID) - self.assertEqual(ws.resource_group, RESOURCE_GROUP) - self.assertEqual(ws.name, WORKSPACE) - self.assertEqual(ws.location, LOCATION) + assert_all_required_params(ws) ws = Workspace( resource_id=SIMPLE_RESOURCE_ID, storage=STORAGE, - location=LOCATION, ) + assert_all_required_params(ws) self.assertEqual(ws.storage, STORAGE) + ws = Workspace( + name=WORKSPACE, + ) + assert_all_required_params(ws) + + ws = Workspace( + name=WORKSPACE, + storage=STORAGE, + ) + assert_all_required_params(ws) + self.assertEqual(ws.storage, STORAGE) + + ws = Workspace( + name=WORKSPACE, + location=LOCATION, + ) + assert_all_required_params(ws) + + ws = Workspace( + name=WORKSPACE, + subscription_id=SUBSCRIPTION_ID, + ) + assert_all_required_params(ws) + + ws = Workspace( + name=WORKSPACE, + subscription_id=SUBSCRIPTION_ID, + location=LOCATION, + ) + assert_all_required_params(ws) + + ws = Workspace( + name=WORKSPACE, + resource_group=RESOURCE_GROUP, + ) + assert_all_required_params(ws) + + ws = Workspace( + name=WORKSPACE, + resource_group=RESOURCE_GROUP, + location=LOCATION, + ) + assert_all_required_params(ws) + def test_create_workspace_locations(self): # User-provided location name should be normalized location = "East US" @@ -168,6 +222,9 @@ def test_workspace_from_connection_string(self): self.assertIsInstance(workspace.credential, AzureKeyCredential) # if we pass a credential, then it should be used + os.environ[EnvironmentVariables.AZURE_CLIENT_ID] = ZERO_UID + os.environ[EnvironmentVariables.AZURE_TENANT_ID] = ZERO_UID + os.environ[EnvironmentVariables.AZURE_CLIENT_SECRET] = PLACEHOLDER workspace = Workspace(credential=EnvironmentCredential()) self.assertIsInstance(workspace.credential, EnvironmentCredential) @@ -204,6 +261,70 @@ def test_workspace_from_connection_string(self): self.assertEqual(workspace.subscription_id, SUBSCRIPTION_ID) self.assertEqual(workspace.resource_group, RESOURCE_GROUP) self.assertEqual(workspace.name, WORKSPACE) + + def test_workspace_from_connection_string_v2(self): + """Test that v2 QuantumEndpoint format is correctly parsed.""" + with mock.patch.dict( + os.environ, + clear=True + ): + workspace = Workspace.from_connection_string(SIMPLE_CONNECTION_STRING_V2) + self.assertEqual(workspace.location, LOCATION) + self.assertEqual(workspace.subscription_id, SUBSCRIPTION_ID) + self.assertEqual(workspace.resource_group, RESOURCE_GROUP) + self.assertEqual(workspace.name, WORKSPACE) + self.assertIsInstance(workspace.credential, AzureKeyCredential) + self.assertEqual(workspace.credential.key, API_KEY) + # pylint: disable=protected-access + self.assertIsInstance( + workspace._client._config.authentication_policy, + AzureKeyCredentialPolicy) + auth_policy = workspace._client._config.authentication_policy + self.assertEqual(auth_policy._name, ConnectionConstants.QUANTUM_API_KEY_HEADER) + self.assertEqual(id(auth_policy._credential), + id(workspace.credential)) + + def test_workspace_from_connection_string_v2_dogfood(self): + """Test v2 QuantumEndpoint with dogfood environment.""" + canary_location = "eastus2euap" + dogfood_connection_string_v2 = ConnectionConstants.VALID_CONNECTION_STRING( + subscription_id=SUBSCRIPTION_ID, + resource_group=RESOURCE_GROUP, + workspace_name=WORKSPACE, + api_key=API_KEY, + quantum_endpoint=ConnectionConstants.GET_QUANTUM_DOGFOOD_ENDPOINT_v2(canary_location) + ) + + with mock.patch.dict(os.environ, clear=True): + workspace = Workspace.from_connection_string(dogfood_connection_string_v2) + self.assertEqual(workspace.location, canary_location) + self.assertEqual(workspace.subscription_id, SUBSCRIPTION_ID) + self.assertEqual(workspace.resource_group, RESOURCE_GROUP) + self.assertEqual(workspace.name, WORKSPACE) + self.assertIsInstance(workspace.credential, AzureKeyCredential) + self.assertEqual(workspace.credential.key, API_KEY) + + def test_env_connection_string_v2(self): + """Test v2 QuantumEndpoint from environment variable.""" + with mock.patch.dict(os.environ): + self.clear_env_vars(os.environ) + os.environ[EnvironmentVariables.CONNECTION_STRING] = SIMPLE_CONNECTION_STRING_V2 + + workspace = Workspace() + self.assertEqual(workspace.location, LOCATION) + self.assertEqual(workspace.subscription_id, SUBSCRIPTION_ID) + self.assertEqual(workspace.name, WORKSPACE) + self.assertEqual(workspace.resource_group, RESOURCE_GROUP) + self.assertIsInstance(workspace.credential, AzureKeyCredential) + self.assertEqual(workspace.credential.key, API_KEY) + # pylint: disable=protected-access + self.assertIsInstance( + workspace._client._config.authentication_policy, + AzureKeyCredentialPolicy) + auth_policy = workspace._client._config.authentication_policy + self.assertEqual(auth_policy._name, ConnectionConstants.QUANTUM_API_KEY_HEADER) + self.assertEqual(id(auth_policy._credential), + id(workspace.credential)) def test_create_workspace_instance_invalid(self): def assert_value_error(exception): @@ -213,48 +334,21 @@ def assert_value_error(exception): with mock.patch.dict(os.environ): self.clear_env_vars(os.environ) - # missing location - with self.assertRaises(ValueError) as context: - Workspace( - location=None, - subscription_id=SUBSCRIPTION_ID, - resource_group=RESOURCE_GROUP, - name=WORKSPACE, - ) - assert_value_error(context.exception) - - # missing location - with self.assertRaises(ValueError) as context: - Workspace(resource_id=SIMPLE_RESOURCE_ID) - assert_value_error(context.exception) - - # missing subscription id - with self.assertRaises(ValueError) as context: - Workspace( - location=LOCATION, - subscription_id=None, - resource_group=RESOURCE_GROUP, - name=WORKSPACE - ) - assert_value_error(context.exception) - - # missing resource group + # missing workspace name with self.assertRaises(ValueError) as context: Workspace( location=LOCATION, subscription_id=SUBSCRIPTION_ID, - resource_group=None, - name=WORKSPACE + resource_group=RESOURCE_GROUP, + name=None ) assert_value_error(context.exception) - # missing workspace name + # provide only subscription id and resource group with self.assertRaises(ValueError) as context: Workspace( - location=LOCATION, subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, - name=None ) assert_value_error(context.exception)