Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ public enum SupportedLanguages
JavaScript,
Java,
CSharp,
PowerShell
PowerShell,
Go
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,15 @@ public sealed class FunctionsService(
Merge ProjectFiles carefully with existing project files:
- **local.settings.json**: Merge "Values" entries, don't overwrite existing connection strings
- **host.json**: Keep existing extensionBundle settings, merge other sections
- **requirements.txt / package.json / pom.xml**: Add new dependencies, avoid duplicates
- **requirements.txt / package.json / pom.xml / go.mod / go.sum**: Add new dependencies, avoid duplicates
If anything conflicts, ask the user before overwriting.

Function files placement by language:
- Python: Add/merge function code into `function_app.py`
- TypeScript/JavaScript: Place files in `src/functions/`
- Java: Place files in `src/main/java/com/function/`
- C#: Place files in the project root alongside the .csproj
- Go: Place `.go` files in the project root
""";

public async Task<LanguageListResult> GetLanguageListAsync(CancellationToken cancellationToken = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public sealed class LanguageMetadataProvider : ILanguageMetadataProvider
["javascript"] = "JavaScript",
["java"] = "Java",
["csharp"] = "CSharp",
["powershell"] = "PowerShell"
["powershell"] = "PowerShell",
["go"] = "Go"
};

/// <summary>
Expand Down Expand Up @@ -310,6 +311,47 @@ func start
],
TemplateParameterName = null,
RecommendationNotes = null
},
["go"] = new LanguageInfoStatic
{
Name = "Go",
Runtime = "native",
ProgrammingModel = "Native Go worker SDK",
Prerequisites = ["Go 1.24+", "Azure Functions Core Tools v4.12+"],
DevelopmentTools = ["VS Code with Go + Azure Functions extensions", "Azure Functions Core Tools"],
InitCommand = "go mod init <module-name>",
RunCommand = "func start",
BuildCommand = "go build ./...",
ProjectFiles = ["go.mod", "go.sum"],
InitInstructions = """
## Go Azure Functions Project Setup

1. Initialize the Go module:
```bash
go mod init <module-name>
```

2. Add or merge the template's dependencies into `go.mod`

3. Build and run locally:
```bash
go build ./...
func start
```
""",
ProjectStructure =
[
"*.go # Go source files",
"go.mod # Go module definition and dependencies",
"go.sum # Go dependency checksums",
"host.json # Azure Functions host configuration",
"local.settings.json # Local development settings (do not commit)",
"README.md # Project documentation",
".gitignore # Git ignore patterns",
".funcignore # Files to exclude from deployment"
],
TemplateParameterName = null,
RecommendationNotes = null
}
};

Expand All @@ -324,7 +366,8 @@ func start
["javascript"] = new RuntimeVersionInfo { Supported = ["20"], Default = "20" },
["java"] = new RuntimeVersionInfo { Supported = ["21"], Default = "21" },
["csharp"] = new RuntimeVersionInfo { Supported = ["8"], Default = "8" },
["powershell"] = new RuntimeVersionInfo { Supported = ["7.4"], Default = "7.4" }
["powershell"] = new RuntimeVersionInfo { Supported = ["7.4"], Default = "7.4" },
["go"] = new RuntimeVersionInfo { Supported = [], Preview = ["1.0"], Default = "1.0" }
};

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace Azure.Mcp.Tools.Functions.Tests;
public class FunctionsCommandTests(ITestOutputHelper output, TestProxyFixture fixture, LiveServerFixture liveServerFixture)
: RecordedCommandTestsBase(output, fixture, liveServerFixture)
{
private static readonly string[] s_expectedLanguages = ["python", "typescript", "javascript", "csharp", "java", "powershell"];
private static readonly string[] s_expectedLanguages = ["python", "typescript", "javascript", "csharp", "java", "powershell", "go"];

/// <summary>
/// Disable default sanitizer additions since Functions tests don't have
Expand Down Expand Up @@ -71,8 +71,8 @@ public async Task ExecuteAsync_ReturnsAllSupportedLanguages()
Assert.NotEmpty(languageList.FunctionsRuntimeVersion);
Assert.NotEmpty(languageList.ExtensionBundleVersion);

// Verify all 6 expected languages are present
Assert.Equal(6, languageList.Languages.Count);
// Verify all 7 expected languages are present
Assert.Equal(7, languageList.Languages.Count);
var languageNames = languageList.Languages.Select(l => l.Language).ToList();
foreach (var expected in s_expectedLanguages)
{
Expand All @@ -95,7 +95,8 @@ public async Task ExecuteAsync_ReturnsCorrectLanguageInfo_AllLanguages()
["javascript"] = ("Node.js - JavaScript", "node", "v4 (Schema-based)"),
["java"] = ("Java", "java", "Annotations-based"),
["csharp"] = ("dotnet-isolated - C#", "dotnet", "Isolated worker process"),
["powershell"] = ("PowerShell", "powershell", "Script-based")
["powershell"] = ("PowerShell", "powershell", "Script-based"),
["go"] = ("Go", "native", "Native Go worker SDK")
};

foreach (var (languageKey, expected) in expectations)
Expand Down Expand Up @@ -126,16 +127,19 @@ public async Task ExecuteAsync_ReturnsRuntimeVersions_AllLanguages()
var language = GetLanguage(languageList, languageKey);

Assert.NotNull(language.RuntimeVersions);
Assert.NotEmpty(language.RuntimeVersions.Supported);
Assert.NotEmpty(language.RuntimeVersions.Default);

// Default should be one of the supported versions
Assert.Contains(language.RuntimeVersions.Default, language.RuntimeVersions.Supported);
var availableVersions = language.RuntimeVersions.Supported
.Concat(language.RuntimeVersions.Preview ?? [])
.ToList();
Assert.NotEmpty(availableVersions);
Assert.Contains(language.RuntimeVersions.Default, availableVersions);

// Same versions should be in Info.RuntimeVersions
Assert.NotNull(language.Info.RuntimeVersions);
Assert.Equal(language.RuntimeVersions.Default, language.Info.RuntimeVersions.Default);
Assert.Equal(language.RuntimeVersions.Supported, language.Info.RuntimeVersions.Supported);
Assert.Equal(language.RuntimeVersions.Preview, language.Info.RuntimeVersions.Preview);
}
}

Expand Down Expand Up @@ -207,8 +211,8 @@ public async Task ExecuteAsync_LanguagesWithoutTemplateParameters()
{
var languageList = await GetLanguageListAsync();

// Python, C#, and PowerShell don't have template parameters
var languagesWithoutParams = new[] { "python", "csharp", "powershell" };
// Python, C#, PowerShell, and Go don't have template parameters
var languagesWithoutParams = new[] { "python", "csharp", "powershell", "go" };
foreach (var languageKey in languagesWithoutParams)
{
var language = GetLanguage(languageList, languageKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ public async Task ExecuteAsync_DeserializationValidation()
["JavaScript"] = new RuntimeVersionInfo { Supported = ["20", "22"], Preview = ["24"], Default = "22" },
["Java"] = new RuntimeVersionInfo { Supported = ["8", "11", "17", "21"], Preview = ["25"], Default = "21" },
["CSharp"] = new RuntimeVersionInfo { Supported = ["8", "9", "10"], FrameworkSupported = ["4.8.1"], Default = "8" },
["PowerShell"] = new RuntimeVersionInfo { Supported = ["7.4"], Default = "7.4" }
["PowerShell"] = new RuntimeVersionInfo { Supported = ["7.4"], Default = "7.4" },
["Go"] = new RuntimeVersionInfo { Supported = [], Preview = ["1.0"], Default = "1.0" }
}
};
mockManifestService.FetchManifestAsync(Arg.Any<CancellationToken>()).Returns(manifest);
Expand All @@ -188,7 +189,7 @@ public async Task ExecuteAsync_DeserializationValidation()

var result = results[0];
Assert.Equal("4.x", result.FunctionsRuntimeVersion);
Assert.Equal(6, result.Languages.Count);
Assert.Equal(7, result.Languages.Count);

// Verify all expected languages are present
var languageNames = result.Languages.Select(l => l.Language).ToList();
Expand All @@ -198,5 +199,6 @@ public async Task ExecuteAsync_DeserializationValidation()
Assert.Contains("java", languageNames);
Assert.Contains("csharp", languageNames);
Assert.Contains("powershell", languageNames);
Assert.Contains("go", languageNames);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ public async Task ExecuteAsync_DeserializationValidation()
[InlineData(SupportedLanguages.CSharp)]
[InlineData(SupportedLanguages.JavaScript)]
[InlineData(SupportedLanguages.PowerShell)]
[InlineData(SupportedLanguages.Go)]
public async Task ExecuteAsync_ReturnsTemplateForAllLanguages(SupportedLanguages language)
{
// Arrange - use representative mocked data per language
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,41 @@ public async Task FetchManifestAsync_UsesPrimary_WhenPrimarySucceeds()

#region FetchTemplateFilesViaArchiveAsync Tests

[Fact]
public async Task GetFunctionTemplateAsync_GoAddMode_IncludesGoMergeGuidance()
{
var zipBytes = CreateTestZipArchive(new Dictionary<string, string>
{
["Azure-repo-abc123/main.go"] = "package main",
["Azure-repo-abc123/go.mod"] = "module example.com/functions",
["Azure-repo-abc123/go.sum"] = "",
["Azure-repo-abc123/host.json"] = "{\"version\": \"2.0\"}"
});
var handler = new MockHttpMessageHandler(zipBytes, HttpStatusCode.OK);
var httpClientFactory = CreateHttpClientFactory(handler);
var manifest = new TemplateManifest
{
Version = "1.0",
Templates = [CreateTestEntry("go", ".", "http-trigger-go-azd")]
};
_manifestService.FetchManifestAsync(Arg.Any<CancellationToken>()).Returns(manifest);
var service = CreateService(httpClientFactory);

var result = await service.GetFunctionTemplateAsync(
SupportedLanguages.Go,
"http-trigger-go-azd",
null,
TemplateOutput.Add,
CancellationToken.None);

Assert.Contains("Go: Place `.go` files in the project root", result.MergeInstructions);
Assert.NotNull(result.FunctionFiles);
Assert.NotNull(result.ProjectFiles);
Assert.Contains(result.FunctionFiles, file => file.FileName == "main.go");
Assert.Contains(result.ProjectFiles, file => file.FileName == "go.mod");
Assert.Contains(result.ProjectFiles, file => file.FileName == "go.sum");
}

[Fact]
public async Task FetchTemplateFilesViaArchiveAsync_ExtractsFiles_FromZipball()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,38 @@ namespace Azure.Mcp.Tools.Functions.Tests.Services;

public sealed class FunctionsServiceTests
{
[Fact]
public void LanguageMetadataProvider_Go_ReturnsNativeWorkerMetadata()
{
var provider = new LanguageMetadataProvider();
var runtimeVersions = new Dictionary<string, RuntimeVersionInfo>
{
["Go"] = new() { Supported = [], Preview = ["1.0"], Default = "1.0" }
};

var result = provider.GetLanguageInfo("go", runtimeVersions);

Assert.NotNull(result);
Assert.Equal("Go", result.Name);
Assert.Equal("native", result.Runtime);
Assert.Equal("Native Go worker SDK", result.ProgrammingModel);
Assert.Equal("go mod init <module-name>", result.InitCommand);
Assert.Equal("go build ./...", result.BuildCommand);
Assert.Contains("go.mod", result.ProjectFiles);
Assert.Contains("go.sum", result.ProjectFiles);
Assert.Equal("1.0", result.RuntimeVersions.Default);
Assert.Contains("1.0", result.RuntimeVersions.Preview!);
}

[Fact]
public void LanguageMetadataProvider_KnownProjectFiles_IncludesGoModuleFiles()
{
var provider = new LanguageMetadataProvider();

Assert.Contains("go.mod", provider.KnownProjectFiles);
Assert.Contains("go.sum", provider.KnownProjectFiles);
}

#region BuildRawGitHubUrl Tests

[Theory]
Expand Down
Loading