diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/APIView/ApiViewHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/APIView/ApiViewHandlers.cs new file mode 100644 index 00000000000..6e0e8099901 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/APIView/ApiViewHandlers.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.Responses; + +namespace Azure.Sdk.Tools.Mock.Handlers.APIView; + +/// +/// Mock handler for azsdk_apiview_get_comments. Returns a small fixed comment payload so +/// callers can exercise the "consume APIView feedback" path deterministically. +/// +public class ApiViewGetCommentsHandler : IMockToolHandler +{ + public string ToolName => "azsdk_apiview_get_comments"; + + public CommandResponse Handle(Dictionary? arguments) => new APIViewResponse + { + Message = "Retrieved APIView comments", + Language = arguments?.GetValueOrDefault("language")?.ToString() ?? ".NET", + PackageName = arguments?.GetValueOrDefault("packageName")?.ToString() ?? "Azure.Template.Contoso", + Result = new[] + { + new + { + id = "comment-1", + line = 42, + text = "Consider renaming this property for clarity.", + author = "reviewer@microsoft.com", + resolved = false + } + } + }; +} + +/// +/// Mock handler for azsdk_apiview_get_review_url. Returns a deterministic review URL for the +/// requested package + language. +/// +public class ApiViewGetReviewUrlHandler : IMockToolHandler +{ + public string ToolName => "azsdk_apiview_get_review_url"; + + public CommandResponse Handle(Dictionary? arguments) + { + var language = arguments?.GetValueOrDefault("language")?.ToString() ?? "dotnet"; + var package = arguments?.GetValueOrDefault("packageName")?.ToString() ?? "Azure.Template.Contoso"; + return new APIViewResponse + { + Message = "APIView URL resolved", + Language = language, + PackageName = package, + Result = $"https://apiview.dev/Assemblies/Review/mock-{language}-{package}".ToLowerInvariant() + }; + } +} + +/// +/// Mock handler for azsdk_apiview_request_copilot_review. Returns a deterministic job ID +/// callers can poll with azsdk_apiview_get_copilot_review. +/// +public class ApiViewRequestCopilotReviewHandler : IMockToolHandler +{ + public string ToolName => "azsdk_apiview_request_copilot_review"; + + public CommandResponse Handle(Dictionary? arguments) => new APIViewResponse + { + Message = "Copilot review submitted", + Language = arguments?.GetValueOrDefault("language")?.ToString() ?? ".NET", + PackageName = arguments?.GetValueOrDefault("packageName")?.ToString() ?? "Azure.Template.Contoso", + Result = "mock-copilot-job-00000001" + }; +} + +/// +/// Mock handler for azsdk_apiview_get_copilot_review. Returns a "completed" review with a +/// single sample comment. +/// +public class ApiViewGetCopilotReviewHandler : IMockToolHandler +{ + public string ToolName => "azsdk_apiview_get_copilot_review"; + + public CommandResponse Handle(Dictionary? arguments) => new APIViewResponse + { + Message = "Copilot review complete", + Result = new + { + jobId = arguments?.GetValueOrDefault("jobId")?.ToString() ?? "mock-copilot-job-00000001", + status = "Completed", + comments = new[] + { + new { line = 24, severity = "info", text = "Mock Copilot suggestion: tighten the parameter type." } + } + } + }; +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Config/ConfigHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Config/ConfigHandlers.cs new file mode 100644 index 00000000000..eb995af3e80 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Config/ConfigHandlers.cs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.Responses; +using Azure.Sdk.Tools.Cli.Models.Responses.Codeowners; + +namespace Azure.Sdk.Tools.Mock.Handlers.Config; + +/// Mock handler for azsdk_check_service_label. +public class CheckServiceLabelHandler : IMockToolHandler +{ + public string ToolName => "azsdk_check_service_label"; + public CommandResponse Handle(Dictionary? arguments) => new ServiceLabelResponse + { + Label = arguments?.GetValueOrDefault("label")?.ToString() ?? "Contoso.WidgetManager", + Status = "Exists" + }; +} + +/// Mock handler for azsdk_create_service_label. +public class CreateServiceLabelHandler : IMockToolHandler +{ + public string ToolName => "azsdk_create_service_label"; + public CommandResponse Handle(Dictionary? arguments) + { + var label = arguments?.GetValueOrDefault("label")?.ToString() ?? "Contoso.WidgetManager"; + return new ServiceLabelResponse + { + Label = label, + Status = "Created", + PullRequestUrl = $"https://github.com/Azure/azure-sdk-tools/pull/99001" + }; + } +} + +/// Mock handler for azsdk_engsys_codeowner_view. +public class CodeownerViewHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_view"; + public CommandResponse Handle(Dictionary? arguments) => new CodeownersViewResponse + { + Packages = + [ + new PackageResponse + { + WorkItemId = 70001, + PackageName = "Azure.Template.Contoso", + Language = ".NET", + PackageType = "client", + Owners = + [ + new OwnerResponse { GitHubAlias = "contoso-owner-1" }, + new OwnerResponse { GitHubAlias = "contoso-owner-2" } + ], + Labels = ["Contoso.WidgetManager"] + } + ] + }; +} + +/// Mock handler for azsdk_engsys_codeowner_check_package. +public class CodeownerCheckPackageHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_check_package"; + public CommandResponse Handle(Dictionary? arguments) => new CheckPackageResponse + { + DirectoryPath = arguments?.GetValueOrDefault("directoryPath")?.ToString() ?? "sdk/contoso/Azure.Template.Contoso", + Owners = ["contoso-owner-1", "contoso-owner-2"], + PRLabels = ["Contoso.WidgetManager"], + ServiceOwners = ["service-team-lead"], + ServiceLabels = ["Service Attention", "Contoso.WidgetManager"] + }; +} + +internal static class CodeownersModifyMockResponses +{ + public static CodeownersModifyResponse OkWithMessage() => new() + { + View = new CodeownersViewResponse + { + Packages = + [ + new PackageResponse + { + WorkItemId = 70001, + PackageName = "Azure.Template.Contoso", + Language = ".NET", + PackageType = "client", + Owners = [new OwnerResponse { GitHubAlias = "contoso-owner-1" }], + Labels = ["Contoso.WidgetManager"] + } + ] + } + }; +} + +/// Mock handler for azsdk_engsys_codeowner_add_package_owner. +public class CodeownerAddPackageOwnerHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_add_package_owner"; + public CommandResponse Handle(Dictionary? arguments) => CodeownersModifyMockResponses.OkWithMessage(); +} + +/// Mock handler for azsdk_engsys_codeowner_add_package_label. +public class CodeownerAddPackageLabelHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_add_package_label"; + public CommandResponse Handle(Dictionary? arguments) => CodeownersModifyMockResponses.OkWithMessage(); +} + +/// Mock handler for azsdk_engsys_codeowner_add_label_owner. +public class CodeownerAddLabelOwnerHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_add_label_owner"; + public CommandResponse Handle(Dictionary? arguments) => CodeownersModifyMockResponses.OkWithMessage(); +} + +/// Mock handler for azsdk_engsys_codeowner_remove_package_owner. +public class CodeownerRemovePackageOwnerHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_remove_package_owner"; + public CommandResponse Handle(Dictionary? arguments) => CodeownersModifyMockResponses.OkWithMessage(); +} + +/// Mock handler for azsdk_engsys_codeowner_remove_package_label. +public class CodeownerRemovePackageLabelHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_remove_package_label"; + public CommandResponse Handle(Dictionary? arguments) => CodeownersModifyMockResponses.OkWithMessage(); +} + +/// Mock handler for azsdk_engsys_codeowner_remove_label_owner. +public class CodeownerRemoveLabelOwnerHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_remove_label_owner"; + public CommandResponse Handle(Dictionary? arguments) => CodeownersModifyMockResponses.OkWithMessage(); +} + +/// Mock handler for azsdk_engsys_codeowner_update_cache. +public class CodeownerUpdateCacheHandler : IMockToolHandler +{ + public string ToolName => "azsdk_engsys_codeowner_update_cache"; + public CommandResponse Handle(Dictionary? arguments) => new DefaultCommandResponse + { + Message = "CODEOWNERS cache refreshed (mock)", + Result = new { packagesRefreshed = 1, labelOwnersRefreshed = 1 } + }; +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Core/UpgradeHandler.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Core/UpgradeHandler.cs new file mode 100644 index 00000000000..71f767a7cb9 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Core/UpgradeHandler.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; + +namespace Azure.Sdk.Tools.Mock.Handlers.Core; + +/// +/// Mock handler for azsdk_upgrade. Always reports the current "mock" version is up to date so +/// callers exercising the upgrade flow don't trigger a real download. +/// +public class UpgradeHandler : IMockToolHandler +{ + public string ToolName => "azsdk_upgrade"; + + public CommandResponse Handle(Dictionary? arguments) => new UpgradeResponse + { + OldVersion = "0.0.0-mock", + NewVersion = "0.0.0-mock", + Message = "azsdk is already up to date (mock).", + RestartRequired = false + }; +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/EngSys/EngSysHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/EngSys/EngSysHandlers.cs new file mode 100644 index 00000000000..f0dce4fe65a --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/EngSys/EngSysHandlers.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.Responses; + +namespace Azure.Sdk.Tools.Mock.Handlers.EngSys; + +/// Mock handler for azsdk_analyze_log_file. Returns a single fake build error. +public class AnalyzeLogFileHandler : IMockToolHandler +{ + public string ToolName => "azsdk_analyze_log_file"; + public CommandResponse Handle(Dictionary? arguments) => new LogAnalysisResponse + { + Summary = "Detected one CS0246 compile error in the build log.", + SuggestedFix = "Add a `using Azure.Core;` directive at the top of the file.", + Errors = + [ + new LogEntry + { + File = "src/Contoso.Widgets/Generated/WidgetsClient.cs", + Line = 42, + Message = "error CS0246: The type or namespace name 'WidgetOptions' could not be found" + } + ] + }; +} + +/// Mock handler for azsdk_get_failed_test_cases. +public class GetFailedTestCasesHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_failed_test_cases"; + public CommandResponse Handle(Dictionary? arguments) => new FailedTestRunListResponse + { + Items = + [ + new FailedTestRunResponse + { + RunId = 100001, + TestCaseTitle = "Contoso.Widgets.Tests.WidgetClientLiveTests.GetWidget", + Outcome = "Failed", + ErrorMessage = "Expected status 200, got 404.", + StackTrace = "at Contoso.Widgets.Tests.WidgetClientLiveTests.GetWidget()" + } + ] + }; +} + +/// Mock handler for azsdk_get_failed_test_case_data. +public class GetFailedTestCaseDataHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_failed_test_case_data"; + public CommandResponse Handle(Dictionary? arguments) => new FailedTestRunResponse + { + RunId = 100001, + TestCaseTitle = arguments?.GetValueOrDefault("testCaseTitle")?.ToString() + ?? "Contoso.Widgets.Tests.WidgetClientLiveTests.GetWidget", + Outcome = "Failed", + ErrorMessage = "Expected status 200, got 404.", + StackTrace = "at Contoso.Widgets.Tests.WidgetClientLiveTests.GetWidget()", + Uri = "https://dev.azure.com/azure-sdk/internal/_test/cases?id=100001" + }; +} + +/// Mock handler for azsdk_get_failed_test_run_data. +public class GetFailedTestRunDataHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_failed_test_run_data"; + public CommandResponse Handle(Dictionary? arguments) => new FailedTestRunListResponse + { + Items = + [ + new FailedTestRunResponse + { + RunId = int.TryParse(arguments?.GetValueOrDefault("runId")?.ToString(), out var id) ? id : 100001, + TestCaseTitle = "Contoso.Widgets.Tests.WidgetClientLiveTests.GetWidget", + Outcome = "Failed", + ErrorMessage = "Expected status 200, got 404.", + StackTrace = "at Contoso.Widgets.Tests.WidgetClientLiveTests.GetWidget()" + } + ] + }; +} + +/// Mock handler for azsdk_cleanup_ai_agents. +public class CleanupAiAgentsHandler : IMockToolHandler +{ + public string ToolName => "azsdk_cleanup_ai_agents"; + public CommandResponse Handle(Dictionary? arguments) => new DefaultCommandResponse + { + Message = "AI agents cleaned up (mock)", + Result = new { agentsDeleted = 3, threadsDeleted = 5 } + }; +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Example/ExampleAndDebugHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Example/ExampleAndDebugHandlers.cs new file mode 100644 index 00000000000..8ee1da1ba65 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Example/ExampleAndDebugHandlers.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; + +namespace Azure.Sdk.Tools.Mock.Handlers.Example; + +/// +/// Mock handler for azsdk_hello_world_fail. Always returns an error response so callers +/// can exercise failure-path handling deterministically. +/// +public class HelloWorldFailHandler : IMockToolHandler +{ + public string ToolName => "azsdk_hello_world_fail"; + + public CommandResponse Handle(Dictionary? arguments) => new DefaultCommandResponse + { + Message = "Simulated failure from azsdk_hello_world_fail", + ResponseError = "MOCK_HELLO_WORLD_FAIL" + }; +} + +/// +/// Shared mock for all azsdk_example_* tools. Each tool maps to a fixed service name and operation +/// label so callers can tell which path was exercised without changing the response shape. +/// +internal static class ExampleResponses +{ + public static CommandResponse Build(string serviceName, string operation, string result) => + new ExampleServiceResponse + { + ServiceName = serviceName, + Operation = operation, + Result = result, + Details = new Dictionary + { + ["mock"] = "true", + ["correlationId"] = "00000000-0000-0000-0000-000000000001" + } + }; +} + +public class ExampleAzureServiceHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_azure_service"; + public CommandResponse Handle(Dictionary? arguments) => + ExampleResponses.Build("AzureStorage", "ListBlobs", "OK"); +} + +public class ExampleDevOpsServiceHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_devops_service"; + public CommandResponse Handle(Dictionary? arguments) => + ExampleResponses.Build("AzureDevOps", "GetBuild", "OK"); +} + +public class ExampleGitHubServiceHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_github_service"; + public CommandResponse Handle(Dictionary? arguments) => + ExampleResponses.Build("GitHub", "GetIssue", "OK"); +} + +public class ExampleAiServiceHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_ai_service"; + public CommandResponse Handle(Dictionary? arguments) => + ExampleResponses.Build("AzureOpenAI", "Completion", "OK"); +} + +public class ExampleErrorHandlingHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_error_handling"; + public CommandResponse Handle(Dictionary? arguments) + { + var response = ExampleResponses.Build("ErrorHandling", "Simulated", "Failed"); + response.ResponseError = "SIMULATED_ERROR"; + return response; + } +} + +public class ExampleProcessExecutionHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_process_execution"; + public CommandResponse Handle(Dictionary? arguments) => + ExampleResponses.Build("Process", "sleep 1", "exit 0"); +} + +public class ExamplePowershellExecutionHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_powershell_execution"; + public CommandResponse Handle(Dictionary? arguments) => + ExampleResponses.Build("PowerShell", "Get-Date", "exit 0"); +} + +public class ExampleAgentFibonacciHandler : IMockToolHandler +{ + public string ToolName => "azsdk_example_agent_fibonacci"; + public CommandResponse Handle(Dictionary? arguments) + { + var n = arguments?.GetValueOrDefault("n")?.ToString() ?? "10"; + // 10th Fibonacci number; intentionally fixed so the mock is deterministic regardless of input. + return ExampleResponses.Build("AgentFibonacci", $"fib({n})", "55"); + } +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/GitHub/GitHubHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/GitHub/GitHubHandlers.cs new file mode 100644 index 00000000000..9a71f695a56 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/GitHub/GitHubHandlers.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.Responses.GitHub; + +namespace Azure.Sdk.Tools.Mock.Handlers.GitHub; + +/// Mock handler for azsdk_get_github_user_details. +public class GetGitHubUserDetailsHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_github_user_details"; + public CommandResponse Handle(Dictionary? arguments) + { + var login = arguments?.GetValueOrDefault("userName")?.ToString() ?? "contoso-user"; + return new DefaultCommandResponse + { + Message = $"Retrieved user details for {login}", + Result = new + { + login, + name = "Contoso User", + email = $"{login}@microsoft.com", + company = "Microsoft" + } + }; + } +} + +internal static class GitHubMockResponses +{ + public static PullRequestDetails ContosoPr(string? url = null) => new() + { + pullRequestNumber = 45001, + Author = "contoso-user", + Status = "open", + Url = url ?? "https://github.com/Azure/azure-sdk-for-net/pull/45001", + IsMerged = false, + IsMergeable = true, + Checks = ["ci: passed", "live-tests: pending"], + Comments = [] + }; +} + +/// Mock handler for azsdk_get_pull_request_link_for_current_branch. +public class GetPrLinkForCurrentBranchHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_pull_request_link_for_current_branch"; + public CommandResponse Handle(Dictionary? arguments) + { + var url = "https://github.com/Azure/azure-sdk-for-net/pull/45001"; + return new GetPullRequestResponse + { + PullRequestUrl = url, + PullRequest = GitHubMockResponses.ContosoPr(url) + }; + } +} + +/// Mock handler for azsdk_create_pull_request. +public class CreatePullRequestHandler : IMockToolHandler +{ + public string ToolName => "azsdk_create_pull_request"; + public CommandResponse Handle(Dictionary? arguments) => new CreatePullRequestResponse + { + PullRequestUrl = "https://github.com/Azure/azure-sdk-for-net/pull/45002", + Messages = ["Pull request created (mock)"] + }; +} + +/// Mock handler for azsdk_get_pull_request. +public class GetPullRequestHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_pull_request"; + public CommandResponse Handle(Dictionary? arguments) + { + var url = arguments?.GetValueOrDefault("pullRequestUrl")?.ToString() + ?? "https://github.com/Azure/azure-sdk-for-net/pull/45001"; + return new GetPullRequestResponse + { + PullRequestUrl = url, + PullRequest = GitHubMockResponses.ContosoPr(url) + }; + } +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Package/PackageOperationHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Package/PackageOperationHandlers.cs new file mode 100644 index 00000000000..157e7ca4a13 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Package/PackageOperationHandlers.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.Responses.Package; + +namespace Azure.Sdk.Tools.Mock.Handlers.Package; + +internal static class PackageMockResponses +{ + public static PackageInfo ContosoPackage(string? language = null) => new() + { + PackageName = "Azure.Template.Contoso", + PackageVersion = "1.0.0-beta.1", + Language = language is null ? SdkLanguage.DotNet : SdkLanguageHelpers.GetSdkLanguage(language), + SdkType = SdkType.Dataplane + }; +} + +/// Mock handler for azsdk_package_generate_code. +public class PackageGenerateCodeHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_generate_code"; + public CommandResponse Handle(Dictionary? arguments) => + PackageOperationResponse.CreateSuccess( + "SDK code generated successfully (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString()), + typespecProjectPath: arguments?.GetValueOrDefault("typeSpecProjectPath")?.ToString()); +} + +/// Mock handler for azsdk_package_build_code. +public class PackageBuildCodeHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_build_code"; + public CommandResponse Handle(Dictionary? arguments) => + PackageOperationResponse.CreateSuccess( + "Package built successfully (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); +} + +/// Mock handler for azsdk_package_run_tests. +public class PackageRunTestsHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_run_tests"; + public CommandResponse Handle(Dictionary? arguments) => + new TestRunResponse(exitCode: 0, testRunOutput: "Test run successful. 42 passed, 0 failed (mock)."); +} + +/// Mock handler for azsdk_package_run_check. +public class PackageRunCheckHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_run_check"; + public CommandResponse Handle(Dictionary? arguments) => + new PackageCheckResponse( + packageName: "Azure.Template.Contoso", + language: SdkLanguage.DotNet, + exitCode: 0, + checkStatusDetails: "All checks passed (mock)."); +} + +/// Mock handler for azsdk_package_pack. +public class PackagePackHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_pack"; + public CommandResponse Handle(Dictionary? arguments) => + PackageOperationResponse.CreateSuccess( + "Package archive created (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); +} + +/// Mock handler for azsdk_package_update_version. +public class PackageUpdateVersionHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_update_version"; + public CommandResponse Handle(Dictionary? arguments) => + PackageOperationResponse.CreateSuccess( + $"Version updated to {arguments?.GetValueOrDefault("newVersion")?.ToString() ?? "1.0.0-beta.2"} (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); +} + +/// Mock handler for azsdk_package_update_metadata. +public class PackageUpdateMetadataHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_update_metadata"; + public CommandResponse Handle(Dictionary? arguments) => + PackageOperationResponse.CreateSuccess( + "Metadata updated (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); +} + +/// Mock handler for azsdk_package_update_changelog_content. +public class PackageUpdateChangelogContentHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_update_changelog_content"; + public CommandResponse Handle(Dictionary? arguments) => + PackageOperationResponse.CreateSuccess( + "Changelog updated (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); +} + +/// Mock handler for azsdk_package_generate_samples. +public class PackageGenerateSamplesHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_generate_samples"; + public CommandResponse Handle(Dictionary? arguments) + { + var resp = PackageOperationResponse.CreateSuccess( + "Samples generated (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); + resp.Result = new { samples_count = 3 }; + return resp; + } +} + +/// Mock handler for azsdk_package_translate_samples. +public class PackageTranslateSamplesHandler : IMockToolHandler +{ + public string ToolName => "azsdk_package_translate_samples"; + public CommandResponse Handle(Dictionary? arguments) + { + var resp = PackageOperationResponse.CreateSuccess( + "Samples translated (mock)", + PackageMockResponses.ContosoPackage(arguments?.GetValueOrDefault("language")?.ToString())); + resp.Result = new { samples_count = 3 }; + return resp; + } +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Pipeline/AnalyzeAndArtifactsHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Pipeline/AnalyzeAndArtifactsHandlers.cs new file mode 100644 index 00000000000..5d72a7d130c --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Pipeline/AnalyzeAndArtifactsHandlers.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; + +namespace Azure.Sdk.Tools.Mock.Handlers.Pipeline; + +/// Mock handler for azsdk_analyze_pipeline. Returns a fake-build summary with a single failed test + task. +public class AnalyzePipelineHandler : IMockToolHandler +{ + public string ToolName => "azsdk_analyze_pipeline"; + + public CommandResponse Handle(Dictionary? arguments) + { + var buildId = arguments?.GetValueOrDefault("buildId")?.ToString() ?? "90001"; + return new AnalyzePipelineResponse + { + PipelineUrl = $"https://dev.azure.com/azure-sdk/internal/_build/results?buildId={buildId}", + FailedTests = new Dictionary> + { + ["Contoso.Widgets.Tests"] = ["WidgetClientLiveTests.GetWidget"] + }, + FailedTasks = + [ + new LogAnalysisResponse + { + Summary = "1 test failure detected in WidgetClientLiveTests", + SuggestedFix = "Check the live test recording for stale data and re-record.", + Errors = + [ + new LogEntry + { + File = "logs/test.log", + Line = 128, + Message = "Test WidgetClientLiveTests.GetWidget failed: expected 200 got 404" + } + ] + } + ] + }; + } +} + +/// Mock handler for azsdk_get_pipeline_llm_artifacts. +public class GetPipelineLlmArtifactsHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_pipeline_llm_artifacts"; + + public CommandResponse Handle(Dictionary? arguments) + { + var buildId = arguments?.GetValueOrDefault("buildId")?.ToString() ?? "90001"; + return new ObjectCommandResponse + { + Message = $"Retrieved LLM artifacts for build {buildId} (mock)", + Result = new + { + buildId, + artifacts = new[] + { + new { name = "log-analysis.json", sizeBytes = 4096 }, + new { name = "failed-tests.json", sizeBytes = 2048 } + } + } + }; + } +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/ReleasePlan/ReleasePlanRemainingHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/ReleasePlan/ReleasePlanRemainingHandlers.cs new file mode 100644 index 00000000000..b092008b4db --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/ReleasePlan/ReleasePlanRemainingHandlers.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.AzureDevOps; +using Azure.Sdk.Tools.Cli.Models.Responses.ReleasePlan; +using Azure.Sdk.Tools.Cli.Models.Responses.ReleasePlanList; + +namespace Azure.Sdk.Tools.Mock.Handlers.ReleasePlan; + +internal static class ReleasePlanMockResponses +{ + public static ReleasePlanWorkItem ContosoWorkItem(string? typespecPath = null, string? releaseMonth = null) => new() + { + WorkItemId = 35000, + Title = "Release Plan - Contoso.WidgetManager", + Status = "Active", + Owner = "testuser@microsoft.com", + SDKReleaseMonth = releaseMonth ?? "06/2026", + ReleasePlanId = 50001, + IsDataPlane = true, + SpecType = "TypeSpec", + ActiveSpecPullRequest = "https://github.com/Azure/azure-rest-api-specs/pull/12345", + APISpecProjectPath = typespecPath ?? "specification/contosowidgetmanager/Contoso.WidgetManager", + SDKReleaseType = "beta", + SDKInfo = + [ + new SDKInfo { Language = ".NET", PackageName = "Azure.Template.Contoso" }, + new SDKInfo { Language = "Python", PackageName = "azure-contoso-widgetmanager" } + ] + }; + + public static ReleaseWorkflowResponse Workflow(string status, params string[] details) => new() + { + Language = SdkLanguage.DotNet, + Status = status, + TypeSpecProject = "specification/contosowidgetmanager/Contoso.WidgetManager", + Details = details.ToList() + }; +} + +/// Mock handler for azsdk_abandon_release_plan. +public class AbandonReleasePlanHandler : IMockToolHandler +{ + public string ToolName => "azsdk_abandon_release_plan"; + public CommandResponse Handle(Dictionary? arguments) => + ReleasePlanMockResponses.Workflow("Abandoned", "Release plan abandoned (mock)"); +} + +/// Mock handler for azsdk_update_release_plan. +public class UpdateReleasePlanHandler : IMockToolHandler +{ + public string ToolName => "azsdk_update_release_plan"; + public CommandResponse Handle(Dictionary? arguments) => new ReleasePlanResponse + { + TypeSpecProject = "specification/contosowidgetmanager/Contoso.WidgetManager", + PackageType = SdkType.Dataplane, + Message = "Release plan updated successfully (mock)", + ReleasePlanDetails = ReleasePlanMockResponses.ContosoWorkItem() + }; +} + +/// Mock handler for azsdk_get_release_plan_for_spec_pr. +public class GetReleasePlanForSpecPrHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_release_plan_for_spec_pr"; + public CommandResponse Handle(Dictionary? arguments) => new ReleasePlanResponse + { + TypeSpecProject = "specification/contosowidgetmanager/Contoso.WidgetManager", + PackageType = SdkType.Dataplane, + Message = "Release plan found for spec PR (mock)", + ReleasePlanDetails = ReleasePlanMockResponses.ContosoWorkItem() + }; +} + +/// Mock handler for azsdk_check_api_spec_ready_for_sdk. +public class CheckApiSpecReadyForSdkHandler : IMockToolHandler +{ + public string ToolName => "azsdk_check_api_spec_ready_for_sdk"; + public CommandResponse Handle(Dictionary? arguments) => + ReleasePlanMockResponses.Workflow("Ready", "API spec is signed off and ready for SDK generation (mock)"); +} + +/// Mock handler for azsdk_get_kpi_attestation_status. +public class GetKpiAttestationStatusHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_kpi_attestation_status"; + public CommandResponse Handle(Dictionary? arguments) => new ReleasePlanListResponse + { + ReleasePlanDetailsList = [ReleasePlanMockResponses.ContosoWorkItem()], + Message = "All required KPIs attested for this release (mock)." + }; +} + +/// Mock handler for azsdk_get_service_details_by_typespec_path. +public class GetServiceDetailsByTypeSpecPathHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_service_details_by_typespec_path"; + public CommandResponse Handle(Dictionary? arguments) => new ProductInfoResponse + { + ProductInfo = new ProductInfo + { + ProductServiceTreeId = "00000000-0000-0000-0000-000000000099", + ServiceId = "00000000-0000-0000-0000-000000000042", + PackageDisplayName = "Azure SDK for Contoso WidgetManager", + ProductServiceTreeLink = "https://servicetree.example.com/products/00000000-0000-0000-0000-000000000099", + WorkItemId = 36000, + Title = "Contoso.WidgetManager" + }, + Message = "Product details resolved from TypeSpec path (mock)." + }; +} + +/// Mock handler for azsdk_update_api_spec_pull_request_in_release_plan. +public class UpdateApiSpecPullRequestInReleasePlanHandler : IMockToolHandler +{ + public string ToolName => "azsdk_update_api_spec_pull_request_in_release_plan"; + public CommandResponse Handle(Dictionary? arguments) => + ReleasePlanMockResponses.Workflow( + "Updated", + "API spec pull request URL updated on release plan (mock)"); +} + +/// Mock handler for azsdk_update_language_exclusion_justification. +public class UpdateLanguageExclusionJustificationHandler : IMockToolHandler +{ + public string ToolName => "azsdk_update_language_exclusion_justification"; + public CommandResponse Handle(Dictionary? arguments) => new DefaultCommandResponse + { + Message = "Updated language exclusion justification in release plan (mock)." + }; +} + diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/TypeSpec/TypeSpecHandlers.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/TypeSpec/TypeSpecHandlers.cs new file mode 100644 index 00000000000..29685abac32 --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/TypeSpec/TypeSpecHandlers.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; +using Azure.Sdk.Tools.Cli.Models.AzureSdkKnowledgeAICompletion; +using Azure.Sdk.Tools.Cli.Models.Responses.Package; +using Azure.Sdk.Tools.Cli.Models.Responses.TypeSpec; + +namespace Azure.Sdk.Tools.Mock.Handlers.TypeSpec; + +internal static class TypeSpecMockResponses +{ + public const string ContosoTypeSpecProject = "specification/contosowidgetmanager/Contoso.WidgetManager"; +} + +/// Mock handler for azsdk_typespec_generate_authoring_plan. +public class TypeSpecGenerateAuthoringPlanHandler : IMockToolHandler +{ + public string ToolName => "azsdk_typespec_generate_authoring_plan"; + public CommandResponse Handle(Dictionary? arguments) => new TypeSpecAuthoringResponse + { + TypeSpecProject = TypeSpecMockResponses.ContosoTypeSpecProject, + Solution = "Add a new operation `GetWidgetById` to the WidgetService and bump the api-version.", + References = + [ + new DocumentReference + { + Title = "TypeSpec authoring guide", + Source = "azure-rest-api-specs-wiki", + Link = "https://aka.ms/typespec-azure-guide" + } + ] + }; +} + +/// Mock handler for azsdk_typespec_init_project. +public class TypeSpecInitProjectHandler : IMockToolHandler +{ + public string ToolName => "azsdk_typespec_init_project"; + public CommandResponse Handle(Dictionary? arguments) => new TspToolResponse + { + TypeSpecProject = TypeSpecMockResponses.ContosoTypeSpecProject, + IsSuccessful = true, + NextSteps = ["Run `tsp compile .` to validate the new project."] + }; +} + +/// Mock handler for azsdk_convert_swagger_to_typespec. +public class ConvertSwaggerToTypeSpecHandler : IMockToolHandler +{ + public string ToolName => "azsdk_convert_swagger_to_typespec"; + public CommandResponse Handle(Dictionary? arguments) => new TspToolResponse + { + TypeSpecProject = TypeSpecMockResponses.ContosoTypeSpecProject, + IsSuccessful = true, + NextSteps = + [ + "Review the converted TypeSpec for any TODOs.", + "Run `tsp compile .` to validate." + ] + }; +} + +/// Mock handler for azsdk_run_typespec_validation. +public class RunTypeSpecValidationHandler : IMockToolHandler +{ + public string ToolName => "azsdk_run_typespec_validation"; + public CommandResponse Handle(Dictionary? arguments) => new TypeSpecValidationResponse + { + TypeSpecProject = TypeSpecMockResponses.ContosoTypeSpecProject, + PackageType = SdkType.Dataplane, + Message = "TypeSpec validation passed (mock).", + validationResults = ["No issues found."] + }; +} + +/// Mock handler for azsdk_get_modified_typespec_projects. +public class GetModifiedTypeSpecProjectsHandler : IMockToolHandler +{ + public string ToolName => "azsdk_get_modified_typespec_projects"; + public CommandResponse Handle(Dictionary? arguments) => new ObjectCommandResponse + { + Message = "Found 1 modified TypeSpec project (mock).", + Result = new[] { TypeSpecMockResponses.ContosoTypeSpecProject } + }; +} + +/// Mock handler for azsdk_typespec_check_project_in_public_repo. +public class TypeSpecCheckProjectInPublicRepoHandler : IMockToolHandler +{ + public string ToolName => "azsdk_typespec_check_project_in_public_repo"; + public CommandResponse Handle(Dictionary? arguments) => new DefaultCommandResponse + { + Message = "TypeSpec project is present in the public azure-rest-api-specs repository (mock).", + Result = true + }; +} + +/// Mock handler for azsdk_typespec_delegate_apiview_feedback. +public class TypeSpecDelegateApiViewFeedbackHandler : IMockToolHandler +{ + public string ToolName => "azsdk_typespec_delegate_apiview_feedback"; + public CommandResponse Handle(Dictionary? arguments) => new DefaultCommandResponse + { + Message = "APIView feedback delegated to the TypeSpec author (mock).", + Result = new + { + commentsDelegated = 2, + assignee = "contoso-typespec-author" + } + }; +} + +/// Mock handler for azsdk_customized_code_update. +public class CustomizedCodeUpdateHandler : IMockToolHandler +{ + public string ToolName => "azsdk_customized_code_update"; + public CommandResponse Handle(Dictionary? arguments) => new CustomizedCodeUpdateResponse + { + Success = true, + Message = "Customized code updated and rebuilt successfully (mock).", + AppliedPatches = + [ + new AppliedPatch( + FilePath: "src/Generated/Customization/WidgetClientCustomization.cs", + Description: "Renamed Get to GetWidget", + ReplacementCount: 2) + ] + }; +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Verify/VerifySetupHandler.cs b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Verify/VerifySetupHandler.cs new file mode 100644 index 00000000000..98edcd4d9cd --- /dev/null +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/Handlers/Verify/VerifySetupHandler.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Azure.Sdk.Tools.Cli.Models; + +namespace Azure.Sdk.Tools.Mock.Handlers.Verify; + +/// +/// Mock handler for azsdk_verify_setup. Returns an all-good response so downstream tools +/// can proceed without environment prerequisites in mock mode. +/// +public class VerifySetupHandler : IMockToolHandler +{ + public string ToolName => "azsdk_verify_setup"; + + public CommandResponse Handle(Dictionary? arguments) => new VerifySetupResponse + { + Results = + [ + new RequirementCheckResult + { + Requirement = "dotnet", + Instructions = ["dotnet SDK is installed."], + RequirementStatusDetails = "dotnet 8.0.100 detected" + }, + new RequirementCheckResult + { + Requirement = "git", + Instructions = ["git is installed."], + RequirementStatusDetails = "git 2.45.0 detected" + } + ] + }; +} diff --git a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/README.md b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/README.md index ea326f81f17..45e12ba2bf5 100644 --- a/tools/azsdk-cli/Azure.Sdk.Tools.Mock/README.md +++ b/tools/azsdk-cli/Azure.Sdk.Tools.Mock/README.md @@ -115,3 +115,14 @@ This lets callers control the mock behavior through input: - `{"message": "Alice"}` → normal success response Use this pattern in any handler to test how your integration handles different scenarios without changing the mock server code. + +## Keeping the Mock in Sync with the Live MCP Server + +The mock reuses the live CLI's tool definitions (`SharedOptions.ToolsList`), so the *set* of advertised tools is always identical. What can drift is which tools have a hand-written `IMockToolHandler`. Tools without a handler fall back to the generic `{"message":"Success"}` default — fine for routing tests but useless for scenarios that chain calls together (e.g. consume an ID returned by a previous tool). + +When you add or rename an MCP tool in `Azure.Sdk.Tools.Cli`, add a matching handler under `Handlers//`: + +1. Look up the live tool's response type under `tools/azsdk-cli/Azure.Sdk.Tools.Cli/Tools/`. The return type is usually a typed `CommandResponse` in `Azure.Sdk.Tools.Cli.Models.Responses.*`. +2. Create a new file under `Handlers//` (e.g., `Handlers/Pipeline/MyToolHandler.cs`). +3. Implement `IMockToolHandler`. Set `ToolName` to the exact `[McpServerTool(Name = "…")]` value from the real tool. +4. Return an instance of the same response type the real tool returns, populated with realistic sample data. For scenarios that need to exercise multiple branches, switch on `arguments` (see `HelloWorldHandler` above).