Skip to content

Commit db8bbfa

Browse files
authored
Add Azure Functions smoke tests with Docker CI automation (#545)
1 parent 2b25301 commit db8bbfa

File tree

10 files changed

+565
-0
lines changed

10 files changed

+565
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Azure Functions Smoke Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- 'feature/**'
8+
paths-ignore: [ '**.md' ]
9+
pull_request:
10+
paths-ignore: [ '**.md' ]
11+
workflow_dispatch:
12+
13+
jobs:
14+
smoke-tests:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v3
20+
21+
- name: Setup .NET
22+
uses: actions/setup-dotnet@v3
23+
with:
24+
dotnet-version: '8.0.x'
25+
26+
- name: Setup .NET from global.json
27+
uses: actions/setup-dotnet@v3
28+
with:
29+
global-json-file: global.json
30+
31+
- name: Restore dependencies
32+
run: dotnet restore test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
33+
34+
- name: Run smoke tests
35+
run: |
36+
cd test/AzureFunctionsSmokeTests
37+
pwsh -File run-smoketests.ps1
38+
39+
- name: Upload smoke test logs on failure
40+
if: failure()
41+
uses: actions/upload-artifact@v4
42+
with:
43+
name: smoke-test-logs
44+
path: test/AzureFunctionsSmokeTests/logs/
45+
if-no-files-found: ignore

Directory.Packages.props

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
<PackageVersion Include="Azure.Identity" Version="1.17.1" />
2626
<PackageVersion Include="Azure.Storage.Blobs" Version="12.26.0" />
2727
<PackageVersion Include="Microsoft.Azure.Functions.Worker" Version="2.51.0" />
28+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.3.0" />
29+
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.7" />
2830
</ItemGroup>
2931

3032
<!-- DurableTask Packages -->
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
6+
<OutputType>Exe</OutputType>
7+
<Nullable>enable</Nullable>
8+
<!-- This is a smoke test application, not a unit test project -->
9+
<IsTestProject>false</IsTestProject>
10+
<IsPackable>false</IsPackable>
11+
<!-- Disable SDK's source generation to allow reflection-based discovery of source-generated functions -->
12+
<FunctionsEnableExecutorSourceGen>false</FunctionsEnableExecutorSourceGen>
13+
<FunctionsEnableWorkerIndexing>false</FunctionsEnableWorkerIndexing>
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<!-- Azure Functions packages (external) -->
18+
<PackageReference Include="Microsoft.Azure.Functions.Worker" />
19+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" />
20+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" />
21+
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" OutputItemType="Analyzer" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<!-- Local project references for testing code in this repo -->
26+
<ProjectReference Include="..\..\src\Abstractions\Abstractions.csproj" />
27+
<ProjectReference Include="..\..\src\Client\Core\Client.csproj" />
28+
<ProjectReference Include="..\..\src\Client\Grpc\Client.Grpc.csproj" />
29+
<ProjectReference Include="..\..\src\Worker\Core\Worker.csproj" />
30+
<ProjectReference Include="..\..\src\Worker\Grpc\Worker.Grpc.csproj" />
31+
<ProjectReference Include="..\..\src\Grpc\Grpc.csproj" />
32+
<ProjectReference Include="..\..\src\Analyzers\Analyzers.csproj" OutputItemType="Analyzer" />
33+
<ProjectReference Include="..\..\src\Generators\Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
34+
</ItemGroup>
35+
36+
<ItemGroup>
37+
<None Update="host.json">
38+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
39+
</None>
40+
<None Update="local.settings.json">
41+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
42+
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
43+
</None>
44+
</ItemGroup>
45+
46+
</Project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Use the Azure Functions base image for .NET 8.0 isolated
2+
FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0
3+
4+
# Set environment variables
5+
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
6+
AzureFunctionsJobHost__Logging__Console__IsEnabled=true \
7+
FUNCTIONS_WORKER_RUNTIME=dotnet-isolated
8+
9+
# Copy the published app
10+
COPY ./publish /home/site/wwwroot
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Azure.Functions.Worker;
5+
using Microsoft.Azure.Functions.Worker.Http;
6+
using Microsoft.DurableTask;
7+
using Microsoft.DurableTask.Client;
8+
using Microsoft.Extensions.Logging;
9+
10+
namespace AzureFunctionsSmokeTests;
11+
12+
/// <summary>
13+
/// Smoke test orchestration functions for Azure Functions with Durable Task.
14+
/// </summary>
15+
public static class HelloCitiesOrchestration
16+
{
17+
[Function(nameof(HelloCitiesOrchestration))]
18+
public static async Task<List<string>> RunOrchestrator(
19+
[OrchestrationTrigger] TaskOrchestrationContext context)
20+
{
21+
ILogger logger = context.CreateReplaySafeLogger(nameof(HelloCitiesOrchestration));
22+
logger.LogInformation("Starting HelloCities orchestration.");
23+
24+
List<string> outputs = new List<string>();
25+
26+
// Call activities in sequence
27+
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
28+
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
29+
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));
30+
31+
logger.LogInformation("HelloCities orchestration completed.");
32+
33+
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
34+
return outputs;
35+
}
36+
37+
[Function(nameof(SayHello))]
38+
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
39+
{
40+
ILogger logger = executionContext.GetLogger(nameof(SayHello));
41+
logger.LogInformation($"Saying hello to {name}.");
42+
return $"Hello {name}!";
43+
}
44+
45+
[Function("HelloCitiesOrchestration_HttpStart")]
46+
public static async Task<HttpResponseData> HttpStart(
47+
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
48+
[DurableClient] DurableTaskClient client,
49+
FunctionContext executionContext)
50+
{
51+
ILogger logger = executionContext.GetLogger("HelloCitiesOrchestration_HttpStart");
52+
53+
// Function input comes from the request content.
54+
string instanceId = await client
55+
.ScheduleNewOrchestrationInstanceAsync(nameof(HelloCitiesOrchestration));
56+
57+
logger.LogInformation($"Started orchestration with ID = '{instanceId}'.");
58+
59+
// Returns an HTTP 202 response with an instance management payload.
60+
return client.CreateCheckStatusResponse(req, instanceId);
61+
}
62+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.Extensions.Hosting;
5+
6+
namespace AzureFunctionsSmokeTests;
7+
8+
public class Program
9+
{
10+
public static void Main()
11+
{
12+
IHost host = new HostBuilder()
13+
.ConfigureFunctionsWorkerDefaults()
14+
.Build();
15+
16+
host.Run();
17+
}
18+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Azure Functions Smoke Tests
2+
3+
This directory contains smoke tests for Azure Functions with Durable Task, designed to validate the SDK and Source Generator functionality in a real Azure Functions isolated .NET environment.
4+
5+
## Overview
6+
7+
The smoke tests ensure that:
8+
- The Durable Task SDK works correctly with Azure Functions isolated worker
9+
- Source generators produce valid code
10+
- Orchestrations can be triggered and completed successfully
11+
- The complete end-to-end workflow functions as expected
12+
13+
## Structure
14+
15+
- **HelloCitiesOrchestration.cs** - Simple orchestration that calls multiple activities
16+
- **Program.cs** - Azure Functions host entry point
17+
- **host.json** - Azure Functions host configuration
18+
- **local.settings.json** - Local development settings
19+
- **Dockerfile** - Docker image configuration for the Functions app
20+
- **run-smoketests.ps1** - PowerShell script to run smoke tests locally or in CI
21+
22+
## Running Smoke Tests Locally
23+
24+
### Prerequisites
25+
26+
- Docker installed and running
27+
- PowerShell Core (pwsh) installed
28+
- .NET 8.0 SDK or later
29+
30+
### Run the Tests
31+
32+
From the `test/AzureFunctionsSmokeTests` directory:
33+
34+
```bash
35+
pwsh -File run-smoketests.ps1
36+
```
37+
38+
The script will:
39+
1. Build and publish the Azure Functions project
40+
2. Create a Docker image
41+
3. Start Azurite (Azure Storage emulator) in a Docker container
42+
4. Start the Azure Functions app in a Docker container
43+
5. Trigger the HelloCities orchestration via HTTP
44+
6. Poll for orchestration completion
45+
7. Validate the result
46+
8. Clean up all containers
47+
48+
### Parameters
49+
50+
The script accepts the following optional parameters:
51+
52+
```powershell
53+
pwsh -File run-smoketests.ps1 `
54+
-ImageName "custom-image-name" `
55+
-ContainerName "custom-container-name" `
56+
-Port 8080 `
57+
-Timeout 120
58+
```
59+
60+
## CI Integration
61+
62+
The smoke tests are automatically run in GitHub Actions via the `.github/workflows/azure-functions-smoke-tests.yml` workflow on:
63+
- Push to `main` or `feature/**` branches
64+
- Pull requests targeting `main` or `feature/**` branches
65+
- Manual workflow dispatch
66+
67+
## Troubleshooting
68+
69+
If the smoke tests fail:
70+
71+
1. **Check container logs**: The script will display logs automatically on failure
72+
2. **Verify Azurite is running**: Ensure port 10000-10002 are available
73+
3. **Check Functions app port**: Ensure the configured port (default 8080) is available
74+
4. **Build errors**: Ensure all dependencies are restored with `dotnet restore`
75+
76+
## Adding New Smoke Tests
77+
78+
To add new orchestration scenarios:
79+
80+
1. Create new function classes following the pattern in `HelloCitiesOrchestration.cs`
81+
2. Ensure proper XML documentation comments
82+
3. Add test logic to validate the new scenario
83+
4. Update this README with the new test case
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"version": "2.0",
3+
"logging": {
4+
"logLevel": {
5+
"Default": "Information",
6+
"DurableTask.AzureStorage": "Warning",
7+
"DurableTask.Core": "Warning"
8+
},
9+
"applicationInsights": {
10+
"samplingSettings": {
11+
"isEnabled": true,
12+
"excludedTypes": "Request"
13+
}
14+
}
15+
},
16+
"extensions": {
17+
"durableTask": {
18+
"hubName": "DotNetIsolatedSmokeTests"
19+
}
20+
}
21+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"IsEncrypted": false,
3+
"Values": {
4+
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
5+
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
6+
}
7+
}

0 commit comments

Comments
 (0)