diff --git a/src/quantum/azext_quantum/_help.py b/src/quantum/azext_quantum/_help.py index e4686ba83aa..38301de1c45 100644 --- a/src/quantum/azext_quantum/_help.py +++ b/src/quantum/azext_quantum/_help.py @@ -86,15 +86,15 @@ - name: Submit the Q# program from the current folder. text: |- az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation \\ - --job-name MyJob + -t MyTarget --job-name MyJob - name: Submit the Q# program from the current folder with job parameters for a target. text: |- az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation \\ - --job-name MyJob --job-params param1=value1 param2=value2 + -t MyTarget --job-name MyJob --job-params param1=value1 param2=value2 - name: Submit the Q# program with program parameters (e.g. n-qubits = 2). text: |- az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation \\ - --job-name MyJob -- --n-qubits=2 + -t MyTarget --job-name MyJob -- --n-qubits=2 - name: Submit a Q# program from the current folder with a target-capability parameter. text: |- az quantum job submit -g MyResourceGroup -w MyWorkspace -l MyLocation -t MyTarget \\ @@ -190,7 +190,7 @@ helps['quantum target show'] = """ type: command - short-summary: Get the details of the given (or current) target to use when submitting jobs to Azure Quantum. + short-summary: Get the Target ID of the current default target to use when submitting jobs to Azure Quantum. examples: - name: Show the currently selected default target text: |- @@ -231,12 +231,9 @@ type: command short-summary: Delete the given (or current) Azure Quantum workspace. examples: - - name: Delete an Azure Quantum workspace by name and group. + - name: Delete an Azure Quantum workspace by resource group and workspace name. If a default workspace has been set, the -g and -w parameters are not required. text: |- az quantum workspace delete -g MyResourceGroup -w MyWorkspace - - name: Delete and clear the default Azure Quantum workspace (if one has been set). - text: |- - az quantum workspace delete """ helps['quantum workspace list'] = """ @@ -256,10 +253,7 @@ type: command short-summary: List the quotas for the given (or current) Azure Quantum workspace. examples: - - name: List the quota information of the default workspace if set. - text: |- - az quantum workspace quotas - - name: List the quota information of a specified Azure Quantum workspace. + - name: List the quota information of a specified Azure Quantum workspace. If a default workspace has been set, the -g, -w, and -l parameters are not required. text: |- az quantum workspace quotas -g MyResourceGroup -w MyWorkspace -l MyLocation """ diff --git a/src/quantum/azext_quantum/_params.py b/src/quantum/azext_quantum/_params.py index 5f662e0c7ca..16169e5da5e 100644 --- a/src/quantum/azext_quantum/_params.py +++ b/src/quantum/azext_quantum/_params.py @@ -27,10 +27,10 @@ def get_action(self, values, option_string): def load_arguments(self, _): - workspace_name_type = CLIArgumentType(options_list=['--workspace-name', '-w'], help='Name of the Quantum Workspace. You can configure the default workspace using `az quantum workspace set`.', id_part=None, required=False) + workspace_name_type = CLIArgumentType(options_list=['--workspace-name', '-w'], help='Name of the Quantum Workspace. You can configure the default workspace using `az quantum workspace set`.', configured_default='workspace', id_part=None) storage_account_name_type = CLIArgumentType(options_list=['--storage-account', '-a'], help='Name of the storage account to be used by a quantum workspace.') program_args_type = CLIArgumentType(nargs='*', help='List of arguments expected by the Q# operation specified as --name=value after `--`.') - target_id_type = CLIArgumentType(options_list=['--target-id', '-t'], help='Execution engine for quantum computing jobs. When a workspace is configured with a set of provider, they each enable one or more targets. You can configure the default target using `az quantum target set`.') + target_id_type = CLIArgumentType(options_list=['--target-id', '-t'], help='Execution engine for quantum computing jobs. When a workspace is configured with a set of providers, they each enable one or more targets. You can configure the default target using `az quantum target set`.', configured_default='target_id') project_type = CLIArgumentType(help='The location of the Q# project to submit. Defaults to current folder.') job_name_type = CLIArgumentType(help='A friendly name to give to this run of the program.') job_id_type = CLIArgumentType(options_list=['--job-id', '-j'], help='Job unique identifier in GUID format.') @@ -60,6 +60,10 @@ def load_arguments(self, _): c.argument('workspace_name', workspace_name_type) c.argument('target_id', target_id_type) + with self.argument_context('quantum target show') as c: + c.argument('workspace_name', workspace_name_type) + c.argument('target_id', target_id_type, required=False) + with self.argument_context('quantum job') as c: c.argument('workspace_name', workspace_name_type) c.argument('job_id', job_id_type) diff --git a/src/quantum/azext_quantum/_validators.py b/src/quantum/azext_quantum/_validators.py index ea1faf6eac8..edf8859fdb7 100644 --- a/src/quantum/azext_quantum/_validators.py +++ b/src/quantum/azext_quantum/_validators.py @@ -50,7 +50,8 @@ def validate_target_info(cmd, namespace): target = TargetInfo(cmd, target_id) if not target.target_id: - raise ValueError("Missing target-id argument") + # raise ValueError("Missing target-id argument. Use `az quantum target set -t MyTarget` to set a default Target ID.") + raise ValueError("No default Target ID has been saved. Use `az quantum target set -t MyTarget` to set a default Target ID.") def validate_workspace_and_target_info(cmd, namespace): diff --git a/src/quantum/azext_quantum/commands.py b/src/quantum/azext_quantum/commands.py index 8b076a1b457..d2694d58de0 100644 --- a/src/quantum/azext_quantum/commands.py +++ b/src/quantum/azext_quantum/commands.py @@ -132,13 +132,13 @@ def load_command_table(self, _): with self.command_group('quantum target', target_ops) as t: t.command('list', 'list', validator=validate_workspace_info, table_transformer=transform_targets) - t.show_command('show', validator=validate_target_info) + t.show_command('show', 'target_show', validator=validate_target_info) t.command('set', 'set', validator=validate_target_info) t.command('clear', 'clear') with self.command_group('quantum job', job_ops) as j: j.command('list', 'list', validator=validate_workspace_info, table_transformer=transform_jobs) - j.show_command('show', validator=validate_workspace_info, table_transformer=transform_job) + j.show_command('show', 'job_show', validator=validate_workspace_info, table_transformer=transform_job) j.command('submit', 'submit', validator=validate_workspace_and_target_info, table_transformer=transform_job) j.command('wait', 'wait', validator=validate_workspace_info, table_transformer=transform_job) j.command('output', 'output', validator=validate_workspace_info, table_transformer=transform_output) diff --git a/src/quantum/azext_quantum/operations/job.py b/src/quantum/azext_quantum/operations/job.py index 1ca7b84dd6c..dab3bee477b 100644 --- a/src/quantum/azext_quantum/operations/job.py +++ b/src/quantum/azext_quantum/operations/job.py @@ -21,7 +21,7 @@ knack_logger = knack.log.get_logger(__name__) -def list(cmd, resource_group_name=None, workspace_name=None, location=None): +def list(cmd, resource_group_name, workspace_name, location): """ Get the list of jobs in a Quantum Workspace. """ @@ -168,7 +168,7 @@ def _has_completed(job): return job.status in ("Succeeded", "Failed", "Cancelled") -def submit(cmd, program_args, resource_group_name=None, workspace_name=None, location=None, target_id=None, +def submit(cmd, program_args, resource_group_name, workspace_name, location, target_id, project=None, job_name=None, shots=None, storage=None, no_build=False, job_params=None, target_capability=None): """ @@ -203,7 +203,7 @@ def submit(cmd, program_args, resource_group_name=None, workspace_name=None, loc # Query for the job and return status to caller. return get(cmd, job_id, resource_group_name, workspace_name, location) - # The program compiled succesfully, but executing the stand-alone .exe failed to run. + # The program compiled successfully, but executing the stand-alone .exe failed to run. logger.error("Submission of job failed with error code %s", result.returncode) print(result.stdout.decode('ascii')) raise AzureInternalError("Failed to submit job.") @@ -229,7 +229,7 @@ def _parse_blob_url(url): } -def output(cmd, job_id, resource_group_name=None, workspace_name=None, location=None): +def output(cmd, job_id, resource_group_name, workspace_name, location): """ Get the results of running a Q# job. """ @@ -305,7 +305,7 @@ def _validate_max_poll_wait_secs(max_poll_wait_secs): return valid_max_poll_wait_secs -def wait(cmd, job_id, resource_group_name=None, workspace_name=None, location=None, max_poll_wait_secs=5): +def wait(cmd, job_id, resource_group_name, workspace_name, location, max_poll_wait_secs=5): """ Place the CLI in a waiting state until the job finishes running. """ @@ -334,7 +334,17 @@ def wait(cmd, job_id, resource_group_name=None, workspace_name=None, location=No return job -def run(cmd, program_args, resource_group_name=None, workspace_name=None, location=None, target_id=None, +def job_show(cmd, job_id, resource_group_name, workspace_name, location): + """ + Get the job's status and details. + """ + info = WorkspaceInfo(cmd, resource_group_name, workspace_name, location) + client = cf_jobs(cmd.cli_ctx, info.subscription, info.resource_group, info.name, info.location) + job = client.get(job_id) + return job + + +def run(cmd, program_args, resource_group_name, workspace_name, location, target_id, project=None, job_name=None, shots=None, storage=None, no_build=False, job_params=None, target_capability=None): """ Submit a job to run on Azure Quantum, and waits for the result. @@ -353,7 +363,7 @@ def run(cmd, program_args, resource_group_name=None, workspace_name=None, locati return output(cmd, job.id, resource_group_name, workspace_name, location) -def cancel(cmd, job_id, resource_group_name=None, workspace_name=None, location=None): +def cancel(cmd, job_id, resource_group_name, workspace_name, location): """ Request to cancel a job on Azure Quantum if it hasn't completed. """ diff --git a/src/quantum/azext_quantum/operations/offerings.py b/src/quantum/azext_quantum/operations/offerings.py index 625483b1567..ec789cf2f36 100644 --- a/src/quantum/azext_quantum/operations/offerings.py +++ b/src/quantum/azext_quantum/operations/offerings.py @@ -49,7 +49,7 @@ def _valid_publisher_and_offer(provider, publisher, offer): return True -def list_offerings(cmd, location=None, autoadd_only=False): +def list_offerings(cmd, location, autoadd_only=False): """ Get the list of all provider offerings available on the given location. """ @@ -70,7 +70,7 @@ def list_offerings(cmd, location=None, autoadd_only=False): return offerings -def show_terms(cmd, provider_id=None, sku=None, location=None): +def show_terms(cmd, provider_id, sku, location): """ Show the terms of a provider and SKU combination including license URL and acceptance status. """ @@ -82,7 +82,7 @@ def show_terms(cmd, provider_id=None, sku=None, location=None): return _get_terms_from_marketplace(cmd, publisher_id, offer_id, sku) -def accept_terms(cmd, provider_id=None, sku=None, location=None): +def accept_terms(cmd, provider_id, sku, location): """ Accept the terms of a provider and SKU combination to enable it for workspace creation. """ diff --git a/src/quantum/azext_quantum/operations/target.py b/src/quantum/azext_quantum/operations/target.py index 758f61886ad..ce93669f7fe 100644 --- a/src/quantum/azext_quantum/operations/target.py +++ b/src/quantum/azext_quantum/operations/target.py @@ -13,9 +13,6 @@ class TargetInfo: def __init__(self, cmd, target_id=None): def select_value(key, value): - if value is not None: - return value - value = cmd.cli_ctx.config.get('quantum', key, None) if value is not None: return value value = cmd.cli_ctx.config.get(cmd.cli_ctx.config.defaults_section_name, key, None) @@ -30,7 +27,7 @@ def save(self, cmd): from azure.cli.core.util import ConfiguredDefaultSetter with ConfiguredDefaultSetter(cmd.cli_ctx.config, False): - cmd.cli_ctx.config.set_value('quantum', 'target_id', self.target_id) + cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'target_id', self.target_id) def get(cmd, target_id=None): @@ -41,7 +38,7 @@ def get(cmd, target_id=None): return info -def set(cmd, target_id=None): +def set(cmd, target_id): """ Select the default target to use when submitting jobs to Azure Quantum. """ @@ -51,7 +48,7 @@ def set(cmd, target_id=None): return info -def list(cmd, resource_group_name=None, workspace_name=None, location=None): +def list(cmd, resource_group_name, workspace_name, location): """ Get the list of providers and their targets in an Azure Quantum workspace. """ @@ -67,3 +64,14 @@ def clear(cmd): info = TargetInfo(cmd) info.clear() info.save(cmd) + + +# Added to fix output problem +# def show(cmd, target_id): +def target_show(cmd, target_id): + """ + Show the currently selected default target. + """ + info = TargetInfo(cmd, target_id) + info.target_id += "" # Kludge excuse: Without this the only output we ever get is "targetId": {"isDefault": true} + return info diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index 8563f7eaacd..b2db7bc8fed 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -3,7 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -# pylint: disable=line-too-long,redefined-builtin,unnecessary-comprehension, too-many-locals, too-many-statements +# pylint: disable=line-too-long,redefined-builtin,unnecessary-comprehension, too-many-locals, too-many-statements, too-many-nested-blocks import os.path import json @@ -50,12 +50,8 @@ def __init__(self, cmd, resource_group_name=None, workspace_name=None, location= # Hierarchically selects the value for the given key. # First tries the value provided as argument, as that represents the value from the command line - # then it checks if the key exists in the 'quantum' section in config, and uses that if available. - # finally, it checks in the 'global' section in the config. + # then it checks if the key exists in the [defaults] section in config, and uses that if available. def select_value(key, value): - if value is not None: - return value - value = cmd.cli_ctx.config.get('quantum', key, None) if value is not None: return value value = cmd.cli_ctx.config.get(cmd.cli_ctx.config.defaults_section_name, key, None) @@ -75,10 +71,11 @@ def clear(self): def save(self, cmd): from azure.cli.core.util import ConfiguredDefaultSetter + # Save in the global [defaults] section of the .azure\config file with ConfiguredDefaultSetter(cmd.cli_ctx.config, False): - cmd.cli_ctx.config.set_value('quantum', 'group', self.resource_group) - cmd.cli_ctx.config.set_value('quantum', 'workspace', self.name) - cmd.cli_ctx.config.set_value('quantum', 'location', self.location) + cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'group', self.resource_group) + cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'workspace', self.name) + cmd.cli_ctx.config.set_value(cmd.cli_ctx.config.defaults_section_name, 'location', self.location) def _show_tip(msg): @@ -211,7 +208,7 @@ def _validate_storage_account(tier_or_kind_msg_text, tier_or_kind, supported_tie f"Storage account {tier_or_kind_msg_text}{plural} currently supported: {tier_or_kind_list[:-2]}") -def create(cmd, resource_group_name=None, workspace_name=None, location=None, storage_account=None, skip_role_assignment=False, provider_sku_list=None, auto_accept=False): +def create(cmd, resource_group_name, workspace_name, location, storage_account, skip_role_assignment=False, provider_sku_list=None, auto_accept=False): """ Create a new Azure Quantum workspace. """ @@ -315,7 +312,7 @@ def create(cmd, resource_group_name=None, workspace_name=None, location=None, st return quantum_workspace -def delete(cmd, resource_group_name=None, workspace_name=None): +def delete(cmd, resource_group_name, workspace_name): """ Delete the given (or current) Azure Quantum workspace. """ @@ -354,7 +351,7 @@ def get(cmd, resource_group_name=None, workspace_name=None): return ws -def quotas(cmd, resource_group_name=None, workspace_name=None, location=None): +def quotas(cmd, resource_group_name, workspace_name, location): """ List the quotas for the given (or current) Azure Quantum workspace. """ @@ -363,7 +360,7 @@ def quotas(cmd, resource_group_name=None, workspace_name=None, location=None): return client.list() -def set(cmd, workspace_name, resource_group_name=None, location=None): +def set(cmd, workspace_name, resource_group_name, location): """ Set the default Azure Quantum workspace. """ diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_jobs.py b/src/quantum/azext_quantum/tests/latest/test_quantum_jobs.py index 46e7ab3890c..3c8d1d009f9 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_jobs.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_jobs.py @@ -5,13 +5,14 @@ import json import os +import pytest import unittest from azure.cli.testsdk.scenario_tests import AllowLargeResponse, live_only -from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) +from azure.cli.testsdk import ScenarioTest from azure.cli.core.azclierror import InvalidArgumentValueError, AzureInternalError -from .utils import get_test_subscription_id, get_test_resource_group, get_test_workspace, get_test_workspace_location +from .utils import get_test_subscription_id, get_test_resource_group, get_test_workspace, get_test_workspace_location, issue_cmd_with_param_missing from ..._client_factory import _get_data_credentials from ...commands import transform_output from ...operations.workspace import WorkspaceInfo @@ -31,6 +32,17 @@ def test_jobs(self): targets = self.cmd('az quantum target list -o json').get_output_in_json() assert len(targets) > 0 + # @pytest.fixture(autouse=True) + # def _pass_fixtures(self, capsys): + # self.capsys = capsys + # # See "TODO" in issue_cmd_with_param_missing un utils.py + + def test_job_errors(self): + issue_cmd_with_param_missing(self, "az quantum job cancel", "az quantum job cancel -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy\nCancel an Azure Quantum job by id.") + issue_cmd_with_param_missing(self, "az quantum job output", "az quantum job output -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy -o table\nPrint the results of a successful Azure Quantum job.") + issue_cmd_with_param_missing(self, "az quantum job show", "az quantum job show -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy --query status\nGet the status of an Azure Quantum job.") + issue_cmd_with_param_missing(self, "az quantum job wait", "az quantum job wait -g MyResourceGroup -w MyWorkspace -l MyLocation -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy --max-poll-wait-secs 60 -o table\nWait for completion of a job, check at 60 second intervals.") + def test_build(self): result = build(self, target_id='ionq.simulator', project='src\\quantum\\azext_quantum\\tests\\latest\\source_for_build_test\\QuantumRNG.csproj', target_capability='BasicQuantumFunctionality') assert result == {'result': 'ok'} diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_offerings.py b/src/quantum/azext_quantum/tests/latest/test_quantum_offerings.py new file mode 100644 index 00000000000..9a33a2aa8ad --- /dev/null +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_offerings.py @@ -0,0 +1,21 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import pytest + +from azure.cli.testsdk import ScenarioTest +from .utils import issue_cmd_with_param_missing + + +class QuantumOfferingsScenarioTest(ScenarioTest): + + # @pytest.fixture(autouse=True) + # def _pass_fixtures(self, capsys): + # self.capsys = capsys + # # See "TODO" in issue_cmd_with_param_missing un utils.py + + def test_offerings_errors(self): + issue_cmd_with_param_missing(self, "az quantum offerings accept-terms", "az quantum offerings accept-terms -p MyProviderId -k MySKU -l MyLocation\nOnce terms have been reviewed, accept the invoking this command.") + issue_cmd_with_param_missing(self, "az quantum offerings show-terms", "az quantum offerings show-terms -p MyProviderId -k MySKU -l MyLocation\nUse a Provider Id and SKU from `az quantum offerings list` to review the terms.") diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py b/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py index ed2ff2ba9a7..e350a96ae32 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py @@ -4,12 +4,13 @@ # -------------------------------------------------------------------------------------------- import os +import pytest import unittest from azure.cli.testsdk.scenario_tests import AllowLargeResponse from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) -from .utils import get_test_resource_group, get_test_workspace, get_test_workspace_location +from .utils import get_test_resource_group, get_test_workspace, get_test_workspace_location, issue_cmd_with_param_missing TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..')) @@ -42,3 +43,12 @@ def test_targets(self): # clear self.cmd(f'az quantum target clear') + + # @pytest.fixture(autouse=True) + # def _pass_fixtures(self, capsys): + # self.capsys = capsys + # # See "TODO" in issue_cmd_with_param_missing un utils.py + + def test_target_errors(self): + self.cmd(f'az quantum target clear') + issue_cmd_with_param_missing(self, "az quantum target set", "az quantum target set -t target-id\nSelect a default when submitting jobs to Azure Quantum.") diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py index 9c6fae2f557..ed65adfb7c4 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py @@ -4,12 +4,13 @@ # -------------------------------------------------------------------------------------------- import os +import pytest import unittest from azure.cli.testsdk.scenario_tests import AllowLargeResponse, live_only from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) from azure.cli.core.azclierror import RequiredArgumentMissingError, ResourceNotFoundError, InvalidArgumentValueError -from .utils import get_test_resource_group, get_test_workspace, get_test_workspace_location, get_test_workspace_storage, get_test_workspace_storage_grs, get_test_workspace_random_name, get_test_workspace_random_long_name, get_test_capabilities, get_test_workspace_provider_sku_list, all_providers_are_in_capabilities +from .utils import get_test_resource_group, get_test_workspace, get_test_workspace_location, get_test_workspace_storage, get_test_workspace_storage_grs, get_test_workspace_random_name, get_test_workspace_random_long_name, get_test_capabilities, get_test_workspace_provider_sku_list, all_providers_are_in_capabilities, issue_cmd_with_param_missing from ..._version_check_helper import check_version from datetime import datetime from ...__init__ import CLI_REPORTED_VERSION @@ -151,26 +152,22 @@ def test_workspace_create_destroy(self): else: self.skipTest(f"Skipping test_workspace_create_destroy: One or more providers in '{test_provider_sku_list}' not found in AZURE_QUANTUM_CAPABILITIES") + # @pytest.fixture(autouse=True) + # def _pass_fixtures(self, capsys): + # self.capsys = capsys + # # See "TODO" in issue_cmd_with_param_missing un utils.py + @live_only() def test_workspace_errors(self): # initialize values test_location = get_test_workspace_location() test_resource_group = get_test_resource_group() test_workspace_temp = get_test_workspace_random_name() - test_storage_account = get_test_workspace_storage() - # NOTE: The following command will not fail when Credits For All providers are in the region: - # # Attempt to create workspace, but omit the provider/SKU parameter - # try: - # self.cmd(f'az quantum workspace create -g {test_resource_group} -w {test_workspace_temp} -l {test_location} -a {test_storage_account} --skip-role-assignment') - # except RequiredArgumentMissingError: - # pass + # Attempt to create workspace, but omit the storage account parameter + issue_cmd_with_param_missing(self, f'az quantum workspace create -w {test_workspace_temp} -l {test_location} -g {test_resource_group} -r "Microsoft/Basic"', + 'az quantum workspace create -g MyResourceGroup -w MyWorkspace -l MyLocation -r "MyProvider1 / MySKU1, MyProvider2 / MySKU2" -a MyStorageAccountName To display a list of available providers and their SKUs, use the following command: az quantum offerings list -l MyLocation -o table\nCreate a new Azure Quantum workspace with a specific list of providers.') - # Attempt to create workspace, but omit the resource group parameter - try: - self.cmd(f'az quantum workspace create -w {test_workspace_temp} -l {test_location} -a {test_storage_account} -r "Microsoft/Basic" --skip-role-assignment') - except ResourceNotFoundError: - pass @live_only() def test_version_check(self): diff --git a/src/quantum/azext_quantum/tests/latest/utils.py b/src/quantum/azext_quantum/tests/latest/utils.py index 1f980038b55..a2540136728 100644 --- a/src/quantum/azext_quantum/tests/latest/utils.py +++ b/src/quantum/azext_quantum/tests/latest/utils.py @@ -53,3 +53,22 @@ def all_providers_are_in_capabilities(provider_sku_string, capabilities_string): if provider not in capabilities_string: return False return True + +# import pytest +# import sys +# import traceback +# # See "TODO" in except block below + +# TEST_ERROR_MESSAGE_PREAMBLE = "the following arguments are required: " + +def issue_cmd_with_param_missing(calling_object, command, help_example): + try: + calling_object.cmd(command) + assert False # Fail the test if we DON'T get an exception + except: + # TODO: Figure out why this works locally, but not in the Azure CLI CI/CD checks pipeline. Is there an alternative way to capture the error message? + # print(traceback.format_exc()) + # out, err = calling_object.capsys.readouterr() + # assert TEST_ERROR_MESSAGE_PREAMBLE in out + # assert help_example in err + pass