From 7326006eaec1dee32bc792365f0bf65211e571fb Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 27 Jul 2026 15:07:20 +0000 Subject: [PATCH] fix(test): resolve subscription non-interactively in provision validation tests The nightly `azure-dev - cli` pipeline has failed every night since 07-24. Regression from #9125 ("Auto-enable no-prompt mode in CI environments"). Root cause: the provision-validation-quota tests answer a fixed stdin script, but the interactive prompt sequence differs between playback and live: - In playback the recording harness sets AZD_DEBUG_SYNTHETIC_SUBSCRIPTION, so no subscription prompt fires (only the validation confirm). - In live nightly there is no synthetic subscription, so azd prompts to select the subscription first, shifting every stdin answer by one line. #9125 also changed Confirm to return its default (Yes) on EOF/blank instead of the old blank=No behavior, removing the blank-line padding the original stdin relied on. The combination broke the tests in live: - Sub tests: the lone "n" was eaten by the subscription Select prompt -> `'n' is not an allowed choice`. - RG tests: answers shifted; the validation confirm hit EOF -> default Yes -> azd deployed the intentionally-invalid template -> `template contains errors`. Fix: persist AZURE_SUBSCRIPTION_ID to the azd environment (via cfgOrStoredSubscription(session), mirroring deploy_test.go) so the subscription prompt never fires in either mode. The prompt sequence is now identical across playback and live, so the stdin scripts answer exactly the remaining prompts (RG select/name for RG scope, then the explicit "n" for the validation confirm). Cassettes must be re-recorded because the recorded flow prompted for (and enumerated) the subscription, whereas the fixed flow resolves it non-interactively: cd cli/azd && mage record -filter=Test_CLI_ProvisionValidationQuota Fixes #9312 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e3af6cd6-bc77-45e7-a150-33390aa47bd2 --- .../provision_validation_quota_test.go | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/cli/azd/test/functional/provision_validation_quota_test.go b/cli/azd/test/functional/provision_validation_quota_test.go index c7a6ad90bed..d61d69e68d8 100644 --- a/cli/azd/test/functional/provision_validation_quota_test.go +++ b/cli/azd/test/functional/provision_validation_quota_test.go @@ -38,6 +38,12 @@ func Test_CLI_ProvisionValidationQuota_RG_DefaultCapacity(t *testing.T) { _, err = cli.RunCommandWithStdIn(ctx, stdinForInit(envName), "init") require.NoError(t, err) + // Persist the subscription so the subscription selection prompt does not fire during + // provision. In playback the recorded subscription id is used; in live CI it comes from + // cfg.SubscriptionID. This keeps the interactive prompt sequence identical across modes. + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_SUBSCRIPTION_ID", cfgOrStoredSubscription(session)) + require.NoError(t, err) + // Persist AZURE_LOCATION to the azd environment so the validation check // can resolve it for RG-scoped deployments where the RG doesn't exist yet. _, err = cli.RunCommand(ctx, "env", "set", "AZURE_LOCATION", "eastus2") @@ -84,6 +90,12 @@ func Test_CLI_ProvisionValidationQuota_RG_InvalidModelName(t *testing.T) { _, err = cli.RunCommandWithStdIn(ctx, stdinForInit(envName), "init") require.NoError(t, err) + // Persist the subscription so the subscription selection prompt does not fire during + // provision. In playback the recorded subscription id is used; in live CI it comes from + // cfg.SubscriptionID. This keeps the interactive prompt sequence identical across modes. + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_SUBSCRIPTION_ID", cfgOrStoredSubscription(session)) + require.NoError(t, err) + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_LOCATION", "eastus2") require.NoError(t, err) @@ -123,6 +135,12 @@ func Test_CLI_ProvisionValidationQuota_RG_InvalidVersion(t *testing.T) { _, err = cli.RunCommandWithStdIn(ctx, stdinForInit(envName), "init") require.NoError(t, err) + // Persist the subscription so the subscription selection prompt does not fire during + // provision. In playback the recorded subscription id is used; in live CI it comes from + // cfg.SubscriptionID. This keeps the interactive prompt sequence identical across modes. + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_SUBSCRIPTION_ID", cfgOrStoredSubscription(session)) + require.NoError(t, err) + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_LOCATION", "eastus2") require.NoError(t, err) @@ -159,6 +177,12 @@ func Test_CLI_ProvisionValidationQuota_Sub_DefaultCapacity(t *testing.T) { _, err = cli.RunCommandWithStdIn(ctx, stdinForInit(envName), "init") require.NoError(t, err) + // Persist the subscription so the subscription selection prompt does not fire during + // provision. In playback the recorded subscription id is used; in live CI it comes from + // cfg.SubscriptionID. This keeps the interactive prompt sequence identical across modes. + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_SUBSCRIPTION_ID", cfgOrStoredSubscription(session)) + require.NoError(t, err) + result, err := cli.RunCommandWithStdIn( ctx, stdinForProvisionWithValidationNo(), @@ -194,6 +218,12 @@ func Test_CLI_ProvisionValidationQuota_Sub_InvalidModelName(t *testing.T) { _, err = cli.RunCommandWithStdIn(ctx, stdinForInit(envName), "init") require.NoError(t, err) + // Persist the subscription so the subscription selection prompt does not fire during + // provision. In playback the recorded subscription id is used; in live CI it comes from + // cfg.SubscriptionID. This keeps the interactive prompt sequence identical across modes. + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_SUBSCRIPTION_ID", cfgOrStoredSubscription(session)) + require.NoError(t, err) + result, err := cli.RunCommandWithStdIn( ctx, stdinForProvisionWithValidationNo(), @@ -230,6 +260,12 @@ func Test_CLI_ProvisionValidationQuota_Sub_DifferentLocation(t *testing.T) { _, err = cli.RunCommandWithStdIn(ctx, stdinForInit(envName), "init") require.NoError(t, err) + // Persist the subscription so the subscription selection prompt does not fire during + // provision. In playback the recorded subscription id is used; in live CI it comes from + // cfg.SubscriptionID. This keeps the interactive prompt sequence identical across modes. + _, err = cli.RunCommand(ctx, "env", "set", "AZURE_SUBSCRIPTION_ID", cfgOrStoredSubscription(session)) + require.NoError(t, err) + result, err := cli.RunCommandWithStdIn( ctx, stdinForProvisionWithValidationNo(), @@ -244,23 +280,26 @@ func Test_CLI_ProvisionValidationQuota_Sub_DifferentLocation(t *testing.T) { // stdinForProvisionWithValidationNo provides stdin for subscription-scoped provision. // -// Subscription and location are resolved from the environment (AZURE_SUBSCRIPTION_ID / -// AZURE_LOCATION), so no interactive subscription/location prompt fires. The only prompt -// is the validation warning confirm ("Proceed with deployment despite the warnings above?"), -// which we answer "No" to. A blank line would now be interpreted as the confirm's default -// (Yes) since azd honors the prompt default at EOF/blank input, so we must answer explicitly. +// The subscription and location are resolved non-interactively (the test persists +// AZURE_SUBSCRIPTION_ID / AZURE_LOCATION to the azd environment before provisioning), so no +// subscription/location prompt fires. The only prompt is the validation warning confirm +// ("Proceed with deployment despite the warnings above?"), which we answer "No" to. The answer +// must be explicit ("n") rather than a blank line, because azd honors the confirm's default +// (Yes) at EOF/blank input. func stdinForProvisionWithValidationNo() string { return "n" // decline the validation warning } // stdinForRGProvisionWithValidationNo provides stdin for resource-group-scoped provision. // -// For RG-scoped provision, azd prompts to pick a resource group and to name a new one -// before validating the deployment; both are answered with a blank line to accept the +// The subscription and location are resolved non-interactively (the test persists +// AZURE_SUBSCRIPTION_ID / AZURE_LOCATION to the azd environment before provisioning), so no +// subscription/location prompt fires. azd still prompts to pick a resource group and to name a +// new one before validating the deployment; both are answered with a blank line to accept the // defaults ("Create a new resource group" and the default name). The final prompt is the -// validation warning confirm ("Proceed with deployment despite the warnings above?"), which -// we answer "No" to. The answer must be explicit ("n") rather than a blank line, because -// azd now honors the confirm's default (Yes) at EOF/blank input. +// validation warning confirm ("Proceed with deployment despite the warnings above?"), which we +// answer "No" to. The confirm answer must be explicit ("n") rather than a blank line, because +// azd honors the confirm's default (Yes) at EOF/blank input. func stdinForRGProvisionWithValidationNo() string { return strings.Join([]string{ "", // pick resource group (default = create new)