From 2f9bd61d4eeff963ad02086f02ef01b0ea1ade34 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Mon, 24 Nov 2025 20:07:12 +0000 Subject: [PATCH 1/8] Use generator for `azd extension show` --- cli/azd/internal/figspec/customizations.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/internal/figspec/customizations.go b/cli/azd/internal/figspec/customizations.go index 1986bc44e11..66f60b5b63d 100644 --- a/cli/azd/internal/figspec/customizations.go +++ b/cli/azd/internal/figspec/customizations.go @@ -88,7 +88,7 @@ func (c *Customizations) GetCommandArgGenerator(ctx *CommandContext, argName str if argName == "template" { return FigGenListTemplates } - case "azd extension install": + case "azd extension install", "azd extension show": if argName == "extension-id" { return FigGenListExtensions } From c66ab54655e86b863dd916e438af32f67f2ec88c Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Mon, 24 Nov 2025 20:41:09 +0000 Subject: [PATCH 2/8] Improve extension support in figspec and usage snapshot tests --- .github/workflows/ext-registry-ci.yml | 50 ++++++++++ cli/azd/cmd/extension_helpers_test.go | 94 +++++++++++++++++++ cli/azd/cmd/figspec_test.go | 12 +++ cli/azd/cmd/testdata/TestFigSpec.ts | 45 +++++++++ .../cmd/testdata/TestUsage-azd-ai-agent.snap | 2 +- cli/azd/cmd/testdata/TestUsage-azd-ai.snap | 4 +- cli/azd/cmd/testdata/TestUsage-azd-demo.snap | 16 ++++ cli/azd/cmd/testdata/TestUsage-azd.snap | 50 +++++----- cli/azd/cmd/usage_test.go | 11 +++ 9 files changed, 259 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/ext-registry-ci.yml create mode 100644 cli/azd/cmd/extension_helpers_test.go create mode 100644 cli/azd/cmd/testdata/TestUsage-azd-demo.snap diff --git a/.github/workflows/ext-registry-ci.yml b/.github/workflows/ext-registry-ci.yml new file mode 100644 index 00000000000..08eb665ddfc --- /dev/null +++ b/.github/workflows/ext-registry-ci.yml @@ -0,0 +1,50 @@ +name: ext-registry-ci + +on: + pull_request: + paths: + - "cli/azd/extensions/registry.json" + - ".github/workflows/ext-registry-ci.yml" + branches: [main] + +# If two events are triggered within a short time in the same PR, cancel the run of the oldest event +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + snapshot-tests: + runs-on: ubuntu-latest + defaults: + run: + working-directory: cli/azd + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: "^1.25" + cache-dependency-path: | + cli/azd/go.sum + + - name: Build azd + run: go build . + + - name: Run FigSpec snapshot test + run: go test ./cmd -v -run TestFigSpec + + - name: Run Usage snapshot test + run: go test ./cmd -v -run TestUsage + + - name: Check snapshot test results + if: failure() + run: | + echo "::error::Snapshots may be out of date. Run the following locally to update them:" + echo "" + echo " cd cli/azd" + echo " UPDATE_SNAPSHOTS=true go test ./cmd -run 'TestFigSpec|TestUsage'" + exit 1 diff --git a/cli/azd/cmd/extension_helpers_test.go b/cli/azd/cmd/extension_helpers_test.go new file mode 100644 index 00000000000..b89bc8865cf --- /dev/null +++ b/cli/azd/cmd/extension_helpers_test.go @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package cmd + +import ( + "context" + "encoding/json" + "path/filepath" + "testing" + "time" + + "github.com/azure/azure-dev/cli/azd/test/azdcli" + "github.com/stretchr/testify/require" +) + +const ( + commandTimeout = 20 * time.Minute + localSourceName = "figspec-local" +) + +// extensionListEntry represents an extension entry returned from the extension list command. +type extensionListEntry struct { + ID string `json:"id"` + Version string `json:"version"` + Source string `json:"source"` +} + +func installRegistryExtensions(t *testing.T, cli *azdcli.CLI) { + t.Helper() + + ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) + defer cancel() + + result, err := cli.RunCommand(ctx, "extension", "list", "--source", localSourceName, "--output", "json") + require.NoError(t, err, "failed to list extensions from registry") + + var extensions []extensionListEntry + err = json.Unmarshal([]byte(result.Stdout), &extensions) + require.NoError(t, err, "failed to unmarshal extension list") + require.NotEmpty(t, extensions, "extension registry returned no entries") + + for _, ext := range extensions { + args := []string{"extension", "install", ext.ID, "--source", localSourceName} + if ext.Version != "" { + args = append(args, "--version", ext.Version) + } + + t.Logf("Installing extension %s@%s", ext.ID, ext.Version) + _, err := cli.RunCommand(ctx, args...) + require.NoErrorf(t, err, "failed to install extension %s", ext.ID) + } +} + +func uninstallAllExtensions(t *testing.T, cli *azdcli.CLI) { + t.Helper() + + ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) + defer cancel() + + t.Log("Uninstalling all extensions") + if _, err := cli.RunCommand(ctx, "extension", "uninstall", "--all"); err != nil { + t.Logf("warning: failed to uninstall extensions: %v", err) + } +} + +func addLocalRegistrySource(t *testing.T, cli *azdcli.CLI) { + t.Helper() + + ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) + defer cancel() + + registryPath := filepath.Join(azdcli.GetSourcePath(), "extensions", "registry.json") + t.Logf("Adding local registry source '%s' from %s", localSourceName, registryPath) + _, err := cli.RunCommand( + ctx, + "extension", "source", "add", + "-n", localSourceName, + "-t", "file", + "-l", registryPath, + ) + require.NoError(t, err, "failed to add local registry source") +} + +func removeLocalExtensionSource(t *testing.T, cli *azdcli.CLI) { + t.Helper() + + ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) + defer cancel() + + if _, err := cli.RunCommand(ctx, "extension", "source", "remove", localSourceName); err != nil { + t.Logf("warning: failed to remove extension source %s: %v", localSourceName, err) + } +} diff --git a/cli/azd/cmd/figspec_test.go b/cli/azd/cmd/figspec_test.go index 0a613bd9e37..6deb0619977 100644 --- a/cli/azd/cmd/figspec_test.go +++ b/cli/azd/cmd/figspec_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/azure/azure-dev/cli/azd/internal/figspec" + "github.com/azure/azure-dev/cli/azd/test/azdcli" "github.com/azure/azure-dev/cli/azd/test/snapshot" "github.com/stretchr/testify/require" ) @@ -22,6 +23,17 @@ import ( // For Pwsh, // $env:UPDATE_SNAPSHOTS='true'; go test ./cmd -run TestFigSpec; $env:UPDATE_SNAPSHOTS=$null func TestFigSpec(t *testing.T) { + configDir := t.TempDir() + t.Setenv("AZD_CONFIG_DIR", configDir) + + cli := azdcli.NewCLI(t) + + addLocalRegistrySource(t, cli) + t.Cleanup(func() { removeLocalExtensionSource(t, cli) }) + + installRegistryExtensions(t, cli) + t.Cleanup(func() { uninstallAllExtensions(t, cli) }) + root := NewRootCmd(false, nil, nil) builder := figspec.NewSpecBuilder(false) diff --git a/cli/azd/cmd/testdata/TestFigSpec.ts b/cli/azd/cmd/testdata/TestFigSpec.ts index 88551bd0bb9..fbcec8a3821 100644 --- a/cli/azd/cmd/testdata/TestFigSpec.ts +++ b/cli/azd/cmd/testdata/TestFigSpec.ts @@ -186,6 +186,16 @@ const completionSpec: Fig.Spec = { name: ['add'], description: 'Add a component to your project.', }, + { + name: ['ai'], + description: 'Extension for the Foundry Agent Service. (Preview)', + subcommands: [ + { + name: ['agent'], + description: 'Extension for the Foundry Agent Service. (Preview)', + }, + ], + }, { name: ['auth'], description: 'Authenticate with Azure.', @@ -269,6 +279,10 @@ const completionSpec: Fig.Spec = { }, ], }, + { + name: ['coding-agent'], + description: 'This extension configures GitHub Copilot Coding Agent access to Azure', + }, { name: ['completion'], description: 'Generate shell completion scripts.', @@ -346,6 +360,10 @@ const completionSpec: Fig.Spec = { }, ], }, + { + name: ['demo'], + description: 'This extension provides examples of the AZD extension framework.', + }, { name: ['deploy'], description: 'Deploy your project code to Azure.', @@ -656,6 +674,7 @@ const completionSpec: Fig.Spec = { ], args: { name: 'extension-id', + generators: azdGenerators.listExtensions, }, }, { @@ -1486,6 +1505,10 @@ const completionSpec: Fig.Spec = { name: ['version'], description: 'Print the version number of Azure Developer CLI.', }, + { + name: ['x'], + description: 'This extension provides a set of tools for AZD extension developers to test and debug their extensions.', + }, { name: ['help'], description: 'Help about any command', @@ -1494,6 +1517,16 @@ const completionSpec: Fig.Spec = { name: ['add'], description: 'Add a component to your project.', }, + { + name: ['ai'], + description: 'Extension for the Foundry Agent Service. (Preview)', + subcommands: [ + { + name: ['agent'], + description: 'Extension for the Foundry Agent Service. (Preview)', + }, + ], + }, { name: ['auth'], description: 'Authenticate with Azure.', @@ -1508,6 +1541,10 @@ const completionSpec: Fig.Spec = { }, ], }, + { + name: ['coding-agent'], + description: 'This extension configures GitHub Copilot Coding Agent access to Azure', + }, { name: ['completion'], description: 'Generate shell completion scripts.', @@ -1564,6 +1601,10 @@ const completionSpec: Fig.Spec = { }, ], }, + { + name: ['demo'], + description: 'This extension provides examples of the AZD extension framework.', + }, { name: ['deploy'], description: 'Deploy your project code to Azure.', @@ -1780,6 +1821,10 @@ const completionSpec: Fig.Spec = { name: ['version'], description: 'Print the version number of Azure Developer CLI.', }, + { + name: ['x'], + description: 'This extension provides a set of tools for AZD extension developers to test and debug their extensions.', + }, ], }, ], diff --git a/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap b/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap index bdd562da18d..ace14cb8639 100644 --- a/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap +++ b/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap @@ -1,5 +1,5 @@ -This extension provides custom commands for working with agents using Azure Developer CLI. +Extension for the Foundry Agent Service. (Preview) Usage azd ai agent [flags] diff --git a/cli/azd/cmd/testdata/TestUsage-azd-ai.snap b/cli/azd/cmd/testdata/TestUsage-azd-ai.snap index 3cb04d2e0a2..b024c1110df 100644 --- a/cli/azd/cmd/testdata/TestUsage-azd-ai.snap +++ b/cli/azd/cmd/testdata/TestUsage-azd-ai.snap @@ -1,11 +1,11 @@ -This extension provides custom commands for building AI applications using Azure Developer CLI. +Extension for the Foundry Agent Service. (Preview) Usage azd ai [command] Available Commands - builder : This extension provides custom commands for building AI applications using Azure Developer CLI. + agent : Extension for the Foundry Agent Service. (Preview) Global Flags -C, --cwd string : Sets the current working directory. diff --git a/cli/azd/cmd/testdata/TestUsage-azd-demo.snap b/cli/azd/cmd/testdata/TestUsage-azd-demo.snap new file mode 100644 index 00000000000..1c18c30c392 --- /dev/null +++ b/cli/azd/cmd/testdata/TestUsage-azd-demo.snap @@ -0,0 +1,16 @@ + +This extension provides examples of the AZD extension framework. + +Usage + azd demo [flags] + +Global Flags + -C, --cwd string : Sets the current working directory. + --debug : Enables debugging and diagnostics logging. + --docs : Opens the documentation for azd demo in your web browser. + -h, --help : Gets help for demo. + --no-prompt : Accepts the default value instead of prompting, or it fails if there is no default. + +Find a bug? Want to let us know how we're doing? Fill out this brief survey: https://aka.ms/azure-dev/hats. + + diff --git a/cli/azd/cmd/testdata/TestUsage-azd.snap b/cli/azd/cmd/testdata/TestUsage-azd.snap index 4c49fd61943..f814afb0860 100644 --- a/cli/azd/cmd/testdata/TestUsage-azd.snap +++ b/cli/azd/cmd/testdata/TestUsage-azd.snap @@ -6,36 +6,42 @@ Usage Commands Getting started - init : Initialize a new application. - up : Provision and deploy your project to Azure with a single command. + init : Initialize a new application. + up : Provision and deploy your project to Azure with a single command. Create and manage Azure resources - auth : Authenticate with Azure. - deploy : Deploy your project code to Azure. - down : Delete your project's Azure resources. - provision : Provision Azure resources for your project. - publish : Publish a service to a container registry. + auth : Authenticate with Azure. + deploy : Deploy your project code to Azure. + down : Delete your project's Azure resources. + provision : Provision Azure resources for your project. + publish : Publish a service to a container registry. Manage and show settings - completion : Generate shell completion scripts. - config : Manage azd configurations (ex: default Azure subscription, location). - env : Manage environments (ex: default environment, environment variables). - show : Display information about your project and its resources. - version : Print the version number of Azure Developer CLI. + completion : Generate shell completion scripts. + config : Manage azd configurations (ex: default Azure subscription, location). + env : Manage environments (ex: default environment, environment variables). + show : Display information about your project and its resources. + version : Print the version number of Azure Developer CLI. Beta commands - add : Add a component to your project. - extension : Manage azd extensions. - hooks : Develop, test and run hooks for a project. - infra : Manage your Infrastructure as Code (IaC). - monitor : Monitor a deployed project. - package : Packages the project's code to be deployed to Azure. - pipeline : Manage and configure your deployment pipelines. - restore : Restores the project's dependencies. - template : Find and view template details. + add : Add a component to your project. + extension : Manage azd extensions. + hooks : Develop, test and run hooks for a project. + infra : Manage your Infrastructure as Code (IaC). + monitor : Monitor a deployed project. + package : Packages the project's code to be deployed to Azure. + pipeline : Manage and configure your deployment pipelines. + restore : Restores the project's dependencies. + template : Find and view template details. Enabled alpha commands - mcp : Manage Model Context Protocol (MCP) server. (Alpha) + mcp : Manage Model Context Protocol (MCP) server. (Alpha) + + Enabled extensions commands + ai : Extension for the Foundry Agent Service. (Preview) + coding-agent : This extension configures GitHub Copilot Coding Agent access to Azure + demo : This extension provides examples of the AZD extension framework. + x : This extension provides a set of tools for AZD extension developers to test and debug their extensions. Flags -C, --cwd string : Sets the current working directory. diff --git a/cli/azd/cmd/usage_test.go b/cli/azd/cmd/usage_test.go index b0686134e55..6037aadeb70 100644 --- a/cli/azd/cmd/usage_test.go +++ b/cli/azd/cmd/usage_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/azure/azure-dev/cli/azd/test/azdcli" "github.com/azure/azure-dev/cli/azd/test/snapshot" "github.com/spf13/cobra" "github.com/stretchr/testify/require" @@ -24,6 +25,16 @@ import ( func TestUsage(t *testing.T) { // disable rich formatting output t.Setenv("TERM", "dumb") + configDir := t.TempDir() + t.Setenv("AZD_CONFIG_DIR", configDir) + + cli := azdcli.NewCLI(t) + + addLocalRegistrySource(t, cli) + t.Cleanup(func() { removeLocalExtensionSource(t, cli) }) + + installRegistryExtensions(t, cli) + t.Cleanup(func() { uninstallAllExtensions(t, cli) }) root := NewRootCmd(false, nil, nil) usageSnapshot(t, root) From 3160371a219ceba7d63beb373dd3a454059b7232 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Mon, 24 Nov 2025 23:51:15 +0000 Subject: [PATCH 3/8] Update documentation --- cli/azd/docs/extension-framework.md | 16 ++++++++++++++++ cli/azd/docs/fig-spec.md | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/cli/azd/docs/extension-framework.md b/cli/azd/docs/extension-framework.md index 5efb52e2f22..4842b7ab442 100644 --- a/cli/azd/docs/extension-framework.md +++ b/cli/azd/docs/extension-framework.md @@ -51,6 +51,8 @@ If you previously removed it and want to add it back: azd extension source add -n azd -t url -l "https://aka.ms/azd/extensions/registry" ``` +> **Note:** When the `registry.json` file is modified, CI automatically runs snapshot tests to ensure extension commands are properly documented in CLI help output and VS Code IntelliSense. See [Snapshot Testing for Extensions](#snapshot-testing-for-extensions) for details. + #### Development Registry > [!CAUTION] @@ -662,6 +664,20 @@ Future ideas include: - Pipeline providers (e.g., TeamCity) --- +### Snapshot Testing for Extensions + +Extension commands are included in CLI snapshot tests (`TestUsage` and `TestFigSpec`) to ensure they appear in help output and VS Code IntelliSense. Tests run in an **isolated environment** (temporary `AZD_CONFIG_DIR`) that installs all extensions from `registry.json`, generates snapshots, then cleans up. + +Update snapshots when modifying `registry.json` or extension commands: + +```bash +# From cli/azd directory +UPDATE_SNAPSHOTS=true go test ./cmd -run TestUsage +UPDATE_SNAPSHOTS=true go test ./cmd -run TestFigSpec +``` + +The [ext-registry-ci workflow](/.github/workflows/ext-registry-ci.yml) runs these tests automatically when `registry.json` is modified in a PR. + ### Developer Workflow The following are the most common developer workflow for developing extensions. diff --git a/cli/azd/docs/fig-spec.md b/cli/azd/docs/fig-spec.md index be5f527c6a6..178c0ca8d43 100644 --- a/cli/azd/docs/fig-spec.md +++ b/cli/azd/docs/fig-spec.md @@ -35,6 +35,15 @@ UPDATE_SNAPSHOTS=true go test ./cmd -run TestFigSpec The snapshot is stored at `cli/azd/cmd/testdata/TestFigSpec.ts`. +### Extension commands in snapshots + +The `TestFigSpec` and `TestUsage` tests automatically include all extension commands to ensure complete IntelliSense and CLI help coverage. Tests run in an **isolated environment** (temporary `AZD_CONFIG_DIR`) that doesn't affect your local installation: + +1. Adds a local extension source pointing to `cli/azd/extensions/registry.json` +2. Installs all registry extensions, generates snapshots, then cleans up + +The [ext-registry-ci workflow](/.github/workflows/ext-registry-ci.yml) runs these tests when `registry.json` is modified in a PR. + ## Updating the spec in VS Code After azd command or flag changes have been released, update the Fig spec in the VS Code repository to keep IntelliSense up to date. From 847250bf18914e102858c705f557107d9facecf2 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Tue, 25 Nov 2025 01:50:37 +0000 Subject: [PATCH 4/8] Update context handling --- cli/azd/cmd/extension_helpers_test.go | 37 ++++++++++----------------- cli/azd/cmd/figspec_test.go | 20 +++++++++++---- cli/azd/cmd/usage_test.go | 19 +++++++++++--- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/cli/azd/cmd/extension_helpers_test.go b/cli/azd/cmd/extension_helpers_test.go index b89bc8865cf..c2263a0af12 100644 --- a/cli/azd/cmd/extension_helpers_test.go +++ b/cli/azd/cmd/extension_helpers_test.go @@ -8,14 +8,12 @@ import ( "encoding/json" "path/filepath" "testing" - "time" "github.com/azure/azure-dev/cli/azd/test/azdcli" "github.com/stretchr/testify/require" ) const ( - commandTimeout = 20 * time.Minute localSourceName = "figspec-local" ) @@ -26,50 +24,45 @@ type extensionListEntry struct { Source string `json:"source"` } -func installRegistryExtensions(t *testing.T, cli *azdcli.CLI) { +func installAllExtensions(ctx context.Context, t *testing.T, cli *azdcli.CLI, sourceName string) { t.Helper() - ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) - defer cancel() - - result, err := cli.RunCommand(ctx, "extension", "list", "--source", localSourceName, "--output", "json") - require.NoError(t, err, "failed to list extensions from registry") + result, err := cli.RunCommand(ctx, "extension", "list", "--source", sourceName, "--output", "json") + require.NoError(t, err, "failed to list extensions from source %s", sourceName) var extensions []extensionListEntry err = json.Unmarshal([]byte(result.Stdout), &extensions) require.NoError(t, err, "failed to unmarshal extension list") - require.NotEmpty(t, extensions, "extension registry returned no entries") + + if len(extensions) == 0 { + t.Logf("No extensions found in source %s to install", sourceName) + return + } for _, ext := range extensions { - args := []string{"extension", "install", ext.ID, "--source", localSourceName} + args := []string{"extension", "install", ext.ID, "--source", sourceName} if ext.Version != "" { args = append(args, "--version", ext.Version) } t.Logf("Installing extension %s@%s", ext.ID, ext.Version) _, err := cli.RunCommand(ctx, args...) - require.NoErrorf(t, err, "failed to install extension %s", ext.ID) + require.NoErrorf(t, err, "failed to install extension %s from source %s", ext.ID, sourceName) } } -func uninstallAllExtensions(t *testing.T, cli *azdcli.CLI) { +func uninstallAllExtensions(ctx context.Context, t *testing.T, cli *azdcli.CLI) { t.Helper() - ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) - defer cancel() - t.Log("Uninstalling all extensions") if _, err := cli.RunCommand(ctx, "extension", "uninstall", "--all"); err != nil { t.Logf("warning: failed to uninstall extensions: %v", err) } } -func addLocalRegistrySource(t *testing.T, cli *azdcli.CLI) { +func addLocalRegistrySource(ctx context.Context, t *testing.T, cli *azdcli.CLI) string { t.Helper() - ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) - defer cancel() - registryPath := filepath.Join(azdcli.GetSourcePath(), "extensions", "registry.json") t.Logf("Adding local registry source '%s' from %s", localSourceName, registryPath) _, err := cli.RunCommand( @@ -80,14 +73,12 @@ func addLocalRegistrySource(t *testing.T, cli *azdcli.CLI) { "-l", registryPath, ) require.NoError(t, err, "failed to add local registry source") + return localSourceName } -func removeLocalExtensionSource(t *testing.T, cli *azdcli.CLI) { +func removeLocalExtensionSource(ctx context.Context, t *testing.T, cli *azdcli.CLI) { t.Helper() - ctx, cancel := context.WithTimeout(context.Background(), commandTimeout) - defer cancel() - if _, err := cli.RunCommand(ctx, "extension", "source", "remove", localSourceName); err != nil { t.Logf("warning: failed to remove extension source %s: %v", localSourceName, err) } diff --git a/cli/azd/cmd/figspec_test.go b/cli/azd/cmd/figspec_test.go index 6deb0619977..c9bd6134452 100644 --- a/cli/azd/cmd/figspec_test.go +++ b/cli/azd/cmd/figspec_test.go @@ -4,7 +4,9 @@ package cmd import ( + "context" "testing" + "time" "github.com/azure/azure-dev/cli/azd/internal/figspec" "github.com/azure/azure-dev/cli/azd/test/azdcli" @@ -28,11 +30,19 @@ func TestFigSpec(t *testing.T) { cli := azdcli.NewCLI(t) - addLocalRegistrySource(t, cli) - t.Cleanup(func() { removeLocalExtensionSource(t, cli) }) - - installRegistryExtensions(t, cli) - t.Cleanup(func() { uninstallAllExtensions(t, cli) }) + sourceName := addLocalRegistrySource(t.Context(), t, cli) + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + removeLocalExtensionSource(ctx, t, cli) + }) + + installAllExtensions(t.Context(), t, cli, sourceName) + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + uninstallAllExtensions(ctx, t, cli) + }) root := NewRootCmd(false, nil, nil) diff --git a/cli/azd/cmd/usage_test.go b/cli/azd/cmd/usage_test.go index 6037aadeb70..85cd10b2708 100644 --- a/cli/azd/cmd/usage_test.go +++ b/cli/azd/cmd/usage_test.go @@ -5,9 +5,11 @@ package cmd import ( "bytes" + "context" "html/template" "strings" "testing" + "time" "github.com/azure/azure-dev/cli/azd/test/azdcli" "github.com/azure/azure-dev/cli/azd/test/snapshot" @@ -30,11 +32,20 @@ func TestUsage(t *testing.T) { cli := azdcli.NewCLI(t) - addLocalRegistrySource(t, cli) - t.Cleanup(func() { removeLocalExtensionSource(t, cli) }) + sourceName := addLocalRegistrySource(t.Context(), t, cli) + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + removeLocalExtensionSource(ctx, t, cli) + }) + + installAllExtensions(t.Context(), t, cli, sourceName) + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + uninstallAllExtensions(ctx, t, cli) + }) - installRegistryExtensions(t, cli) - t.Cleanup(func() { uninstallAllExtensions(t, cli) }) root := NewRootCmd(false, nil, nil) usageSnapshot(t, root) From 90d98b6b21734cb42a2a6c36cc80c9141989e720 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Tue, 25 Nov 2025 21:06:28 +0000 Subject: [PATCH 5/8] Test out-of-date figspec --- cli/azd/cmd/testdata/TestFigSpec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cli/azd/cmd/testdata/TestFigSpec.ts b/cli/azd/cmd/testdata/TestFigSpec.ts index fbcec8a3821..9088e403d4d 100644 --- a/cli/azd/cmd/testdata/TestFigSpec.ts +++ b/cli/azd/cmd/testdata/TestFigSpec.ts @@ -1505,10 +1505,6 @@ const completionSpec: Fig.Spec = { name: ['version'], description: 'Print the version number of Azure Developer CLI.', }, - { - name: ['x'], - description: 'This extension provides a set of tools for AZD extension developers to test and debug their extensions.', - }, { name: ['help'], description: 'Help about any command', From a0480a2a9a17354c0bc84ac4a8752592b25d66af Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Tue, 25 Nov 2025 21:28:57 +0000 Subject: [PATCH 6/8] Test out-of-date usage snapshot --- cli/azd/cmd/testdata/TestFigSpec.ts | 4 ++++ cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cli/azd/cmd/testdata/TestFigSpec.ts b/cli/azd/cmd/testdata/TestFigSpec.ts index 9088e403d4d..fbcec8a3821 100644 --- a/cli/azd/cmd/testdata/TestFigSpec.ts +++ b/cli/azd/cmd/testdata/TestFigSpec.ts @@ -1505,6 +1505,10 @@ const completionSpec: Fig.Spec = { name: ['version'], description: 'Print the version number of Azure Developer CLI.', }, + { + name: ['x'], + description: 'This extension provides a set of tools for AZD extension developers to test and debug their extensions.', + }, { name: ['help'], description: 'Help about any command', diff --git a/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap b/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap index ace14cb8639..3b55800617b 100644 --- a/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap +++ b/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap @@ -1,5 +1,5 @@ -Extension for the Foundry Agent Service. (Preview) +Extension for the Foundry Agent Service. Usage azd ai agent [flags] From 361c2d06107a8984594af9e00791ca5509e7362c Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Tue, 25 Nov 2025 21:45:56 +0000 Subject: [PATCH 7/8] Restore snapshots --- cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap b/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap index 3b55800617b..ace14cb8639 100644 --- a/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap +++ b/cli/azd/cmd/testdata/TestUsage-azd-ai-agent.snap @@ -1,5 +1,5 @@ -Extension for the Foundry Agent Service. +Extension for the Foundry Agent Service. (Preview) Usage azd ai agent [flags] From c78d85853e94b47fb132aab0b49d5bdd40a6c460 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen Date: Tue, 25 Nov 2025 21:59:02 +0000 Subject: [PATCH 8/8] Update source name --- cli/azd/cmd/extension_helpers_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/cmd/extension_helpers_test.go b/cli/azd/cmd/extension_helpers_test.go index c2263a0af12..995da4ef70b 100644 --- a/cli/azd/cmd/extension_helpers_test.go +++ b/cli/azd/cmd/extension_helpers_test.go @@ -14,7 +14,7 @@ import ( ) const ( - localSourceName = "figspec-local" + localSourceName = "local" ) // extensionListEntry represents an extension entry returned from the extension list command.