From 0fedb3b5eb2074999d8ccdb839e054ea80da486f Mon Sep 17 00:00:00 2001 From: Kathy Wu Date: Wed, 8 Apr 2026 17:08:00 -0700 Subject: [PATCH] fix: Change express mode user flow so it's more clear that an express mode project is being created Also adds link to express mode so the user knows that it is Co-authored-by: Kathy Wu PiperOrigin-RevId: 896778982 --- src/google/adk/cli/cli_create.py | 83 +++++++++-------- tests/unittests/cli/utils/test_cli_create.py | 94 +++++++++++--------- 2 files changed, 98 insertions(+), 79 deletions(-) diff --git a/src/google/adk/cli/cli_create.py b/src/google/adk/cli/cli_create.py index f5a5810d..a563d6ad 100644 --- a/src/google/adk/cli/cli_create.py +++ b/src/google/adk/cli/cli_create.py @@ -327,48 +327,61 @@ def _handle_login_with_google() -> ( selected_project_id = projects[project_index - 1][0] region = _prompt_for_google_cloud_region(None) return None, selected_project_id, region - else: - if click.confirm( - "No projects found automatically. Would you like to enter one" - " manually?", - default=False, - ): - selected_project_id = _prompt_for_google_cloud(None) - region = _prompt_for_google_cloud_region(None) - return None, selected_project_id, region - # Check Express eligibility - if gcp_utils.check_express_eligibility(): - click.secho(_EXPRESS_TOS_MSG, fg="yellow") - if click.confirm("Do you accept the Terms of Service?", default=False): - selected_region = click.prompt( - """\ + click.secho( + "A Google Cloud project is required to continue. You can enter an" + " existing project ID or create an Express Mode project. Learn more:" + " https://cloud.google.com/resources/cloud-express-faqs", + fg="green", + ) + action = click.prompt( + "1. Enter an existing Google Cloud project ID\n" + "2. Create a new project (Express Mode)\n" + "3. Abandon\n" + "Choose an action", + type=click.Choice(["1", "2", "3"]), + ) + + if action == "3": + raise click.Abort() + + if action == "1": + google_cloud_project = _prompt_for_google_cloud(None) + google_cloud_region = _prompt_for_google_cloud_region(None) + return None, google_cloud_project, google_cloud_region + + elif action == "2": + if gcp_utils.check_express_eligibility(): + click.secho(_EXPRESS_TOS_MSG, fg="yellow") + if click.confirm("Do you accept the Terms of Service?", default=False): + selected_region = click.prompt( + """\ Choose a region for Express Mode: 1. us-central1 2. europe-west1 3. asia-southeast1 Choose region""", - type=click.Choice(["1", "2", "3"]), - default="1", - ) - region_map = { - "1": "us-central1", - "2": "europe-west1", - "3": "asia-southeast1", - } - region = region_map[selected_region] - express_info = gcp_utils.sign_up_express(location=region) - api_key = express_info.get("api_key") - project_id = express_info.get("project_id") - region = express_info.get("region", region) - click.secho( - f"Express Mode project created: {project_id}", - fg="green", - ) - return api_key, project_id, region + type=click.Choice(["1", "2", "3"]), + default="1", + ) + region_map = { + "1": "us-central1", + "2": "europe-west1", + "3": "asia-southeast1", + } + region = region_map[selected_region] + express_info = gcp_utils.sign_up_express(location=region) + api_key = express_info.get("api_key") + project_id = express_info.get("project_id") + region = express_info.get("region", region) + click.secho( + f"Express Mode project created: {project_id}", + fg="green", + ) + return api_key, project_id, region - click.secho(_NOT_ELIGIBLE_MSG, fg="red") - raise click.Abort() + click.secho(_NOT_ELIGIBLE_MSG, fg="red") + raise click.Abort() def _prompt_to_choose_type() -> str: diff --git a/tests/unittests/cli/utils/test_cli_create.py b/tests/unittests/cli/utils/test_cli_create.py index 520245b3..892e5229 100644 --- a/tests/unittests/cli/utils/test_cli_create.py +++ b/tests/unittests/cli/utils/test_cli_create.py @@ -356,17 +356,51 @@ def test_handle_login_with_google_select_gcp_project( assert region == "us-east1" -def test_handle_login_with_google_express_signup( +def test_handle_login_with_google_manual_project( monkeypatch: pytest.MonkeyPatch, ) -> None: - """Handler should sign up for Express if eligible and user accepts TOS.""" + """Handler should allow manual project ID entry when '0' is selected.""" + monkeypatch.setattr(gcp_utils, "check_adc", lambda: True) + monkeypatch.setattr(gcp_utils, "retrieve_express_project", lambda: None) + monkeypatch.setattr( + gcp_utils, "list_gcp_projects", lambda limit: [("p1", "Project 1")] + ) + prompts = iter([0, "manual-proj", "us-east1"]) + monkeypatch.setattr(click, "prompt", lambda *a, **k: next(prompts)) + + api_key, proj, region = cli_create._handle_login_with_google() + assert api_key is None + assert proj == "manual-proj" + assert region == "us-east1" + + +def test_handle_login_with_google_option_1( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """User selects 1, enters project ID and region.""" + monkeypatch.setattr(gcp_utils, "check_adc", lambda: True) + monkeypatch.setattr(gcp_utils, "retrieve_express_project", lambda: None) + monkeypatch.setattr(gcp_utils, "list_gcp_projects", lambda limit: []) + prompts = iter(["1", "test-proj", "us-east1"]) + monkeypatch.setattr(click, "prompt", lambda *a, **k: next(prompts)) + + api_key, proj, region = cli_create._handle_login_with_google() + assert api_key is None + assert proj == "test-proj" + assert region == "us-east1" + + +def test_handle_login_with_google_option_2( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """User selects 2, goes through express sign up.""" monkeypatch.setattr(gcp_utils, "check_adc", lambda: True) monkeypatch.setattr(gcp_utils, "retrieve_express_project", lambda: None) monkeypatch.setattr(gcp_utils, "list_gcp_projects", lambda limit: []) monkeypatch.setattr(gcp_utils, "check_express_eligibility", lambda: True) - confirms = iter([False, True]) - monkeypatch.setattr(click, "confirm", lambda *a, **k: next(confirms)) - monkeypatch.setattr(click, "prompt", lambda *a, **k: "1") + monkeypatch.setattr(click, "confirm", lambda *a, **k: True) + prompts = iter(["2", "1"]) + monkeypatch.setattr(click, "prompt", lambda *a, **k: next(prompts)) monkeypatch.setattr( gcp_utils, "sign_up_express", @@ -383,6 +417,17 @@ def test_handle_login_with_google_express_signup( assert region == "us-central1" +def test_handle_login_with_google_option_3( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """User selects 3, aborts.""" + monkeypatch.setattr(gcp_utils, "retrieve_express_project", lambda: None) + monkeypatch.setattr(gcp_utils, "list_gcp_projects", lambda limit: []) + monkeypatch.setattr(click, "prompt", lambda *a, **k: "3") + with pytest.raises(click.Abort): + cli_create._handle_login_with_google() + + # prompt_str def test_prompt_str_non_empty(monkeypatch: pytest.MonkeyPatch) -> None: """_prompt_str should retry until a non-blank string is provided.""" @@ -416,42 +461,3 @@ def test_get_gcp_region_from_gcloud_fail( ), ) assert cli_create._get_gcp_region_from_gcloud() == "" - - -def test_handle_login_with_google_manual_project( - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Handler should allow manual project ID entry when '0' is selected.""" - monkeypatch.setattr(gcp_utils, "check_adc", lambda: True) - monkeypatch.setattr(gcp_utils, "retrieve_express_project", lambda: None) - monkeypatch.setattr( - gcp_utils, "list_gcp_projects", lambda limit: [("p1", "Project 1")] - ) - # First prompt is for project selection (0), second is for manual ID entry, - # third is for region selection. - prompts = iter([0, "manual-proj", "us-east1"]) - monkeypatch.setattr(click, "prompt", lambda *a, **k: next(prompts)) - - api_key, proj, region = cli_create._handle_login_with_google() - assert api_key is None - assert proj == "manual-proj" - assert region == "us-east1" - - -def test_handle_login_with_google_empty_projects_manual_entry( - monkeypatch: pytest.MonkeyPatch, -) -> None: - """Handler should allow manual entry if no projects are found and user accepts.""" - monkeypatch.setattr(gcp_utils, "check_adc", lambda: True) - monkeypatch.setattr(gcp_utils, "retrieve_express_project", lambda: None) - monkeypatch.setattr(gcp_utils, "list_gcp_projects", lambda limit: []) - - # User says Yes to "enter manually", then provides project ID and region - prompts = iter(["manual-proj", "us-east1"]) - monkeypatch.setattr(click, "confirm", lambda *a, **k: True) - monkeypatch.setattr(click, "prompt", lambda *a, **k: next(prompts)) - - api_key, proj, region = cli_create._handle_login_with_google() - assert api_key is None - assert proj == "manual-proj" - assert region == "us-east1"