Skip to content
Draft
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
48 changes: 48 additions & 0 deletions .github/workflows/azure-functions-smoke-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Azure Functions Smoke Tests

on:
push:
branches:
- main
- 'feature/**'
paths-ignore: [ '**.md' ]
pull_request:
branches:
- main
- 'feature/**'
paths-ignore: [ '**.md' ]
workflow_dispatch:

jobs:
smoke-tests:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'

- name: Setup .NET from global.json
uses: actions/setup-dotnet@v3
with:
global-json-file: global.json

- name: Restore dependencies
run: dotnet restore test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj

- name: Run smoke tests
run: |
cd test/AzureFunctionsSmokeTests
pwsh -File run-smoketests.ps1

- name: Upload smoke test logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: smoke-test-logs
path: test/AzureFunctionsSmokeTests/logs/
if-no-files-found: ignore
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
<PackageVersion Include="Azure.Identity" Version="1.17.1" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.26.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker" Version="2.51.0" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
<PackageVersion Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.2" />
</ItemGroup>

<!-- DurableTask Packages -->
Expand Down
35 changes: 35 additions & 0 deletions test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<!-- This is a smoke test application, not a unit test project -->
<IsTestProject>false</IsTestProject>
<IsPackable>false</IsPackable>
<!-- Disable SDK's source generation to allow reflection-based discovery of source-generated functions -->
<FunctionsEnableExecutorSourceGen>false</FunctionsEnableExecutorSourceGen>
<FunctionsEnableWorkerIndexing>false</FunctionsEnableWorkerIndexing>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" OutputItemType="Analyzer" />
<!-- Reference the source generator project directly for local development -->
<ProjectReference Include="..\..\src\Generators\Generators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>

</Project>
10 changes: 10 additions & 0 deletions test/AzureFunctionsSmokeTests/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Use the Azure Functions base image for .NET 8.0 isolated
FROM mcr.microsoft.com/azure-functions/dotnet-isolated:4-dotnet-isolated8.0

# Set environment variables
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
AzureFunctionsJobHost__Logging__Console__IsEnabled=true \
FUNCTIONS_WORKER_RUNTIME=dotnet-isolated

# Copy the published app
COPY ./publish /home/site/wwwroot
62 changes: 62 additions & 0 deletions test/AzureFunctionsSmokeTests/HelloCitiesOrchestration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Logging;

namespace AzureFunctionsSmokeTests;

/// <summary>
/// Smoke test orchestration functions for Azure Functions with Durable Task.
/// </summary>
public static class HelloCitiesOrchestration
{
[Function(nameof(HelloCitiesOrchestration))]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
ILogger logger = context.CreateReplaySafeLogger(nameof(HelloCitiesOrchestration));
logger.LogInformation("Starting HelloCities orchestration.");

List<string> outputs = new List<string>();

// Call activities in sequence
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));

logger.LogInformation("HelloCities orchestration completed.");

// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}

[Function(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger(nameof(SayHello));
logger.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}

[Function("HelloCitiesOrchestration_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("HelloCitiesOrchestration_HttpStart");

// Function input comes from the request content.
string instanceId = await client
.ScheduleNewOrchestrationInstanceAsync(nameof(HelloCitiesOrchestration));

logger.LogInformation($"Started orchestration with ID = '{instanceId}'.");

// Returns an HTTP 202 response with an instance management payload.
return client.CreateCheckStatusResponse(req, instanceId);
}
}
18 changes: 18 additions & 0 deletions test/AzureFunctionsSmokeTests/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Extensions.Hosting;

namespace AzureFunctionsSmokeTests;

public class Program
{
public static void Main()
{
IHost host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.Build();

host.Run();
}
}
83 changes: 83 additions & 0 deletions test/AzureFunctionsSmokeTests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Azure Functions Smoke Tests

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.

## Overview

The smoke tests ensure that:
- The Durable Task SDK works correctly with Azure Functions isolated worker
- Source generators produce valid code
- Orchestrations can be triggered and completed successfully
- The complete end-to-end workflow functions as expected

## Structure

- **HelloCitiesOrchestration.cs** - Simple orchestration that calls multiple activities
- **Program.cs** - Azure Functions host entry point
- **host.json** - Azure Functions host configuration
- **local.settings.json** - Local development settings
- **Dockerfile** - Docker image configuration for the Functions app
- **run-smoketests.ps1** - PowerShell script to run smoke tests locally or in CI

## Running Smoke Tests Locally

### Prerequisites

- Docker installed and running
- PowerShell Core (pwsh) installed
- .NET 8.0 SDK or later

### Run the Tests

From the `test/AzureFunctionsSmokeTests` directory:

```bash
pwsh -File run-smoketests.ps1
```

The script will:
1. Build and publish the Azure Functions project
2. Create a Docker image
3. Start Azurite (Azure Storage emulator) in a Docker container
4. Start the Azure Functions app in a Docker container
5. Trigger the HelloCities orchestration via HTTP
6. Poll for orchestration completion
7. Validate the result
8. Clean up all containers

### Parameters

The script accepts the following optional parameters:

```powershell
pwsh -File run-smoketests.ps1 `
-ImageName "custom-image-name" `
-ContainerName "custom-container-name" `
-Port 8080 `
-Timeout 120
```

## CI Integration

The smoke tests are automatically run in GitHub Actions via the `.github/workflows/azure-functions-smoke-tests.yml` workflow on:
- Push to `main` or `feature/**` branches
- Pull requests targeting `main` or `feature/**` branches
- Manual workflow dispatch

## Troubleshooting

If the smoke tests fail:

1. **Check container logs**: The script will display logs automatically on failure
2. **Verify Azurite is running**: Ensure port 10000-10002 are available
3. **Check Functions app port**: Ensure the configured port (default 8080) is available
4. **Build errors**: Ensure all dependencies are restored with `dotnet restore`

## Adding New Smoke Tests

To add new orchestration scenarios:

1. Create new function classes following the pattern in `HelloCitiesOrchestration.cs`
2. Ensure proper XML documentation comments
3. Add test logic to validate the new scenario
4. Update this README with the new test case
21 changes: 21 additions & 0 deletions test/AzureFunctionsSmokeTests/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "2.0",
"logging": {
"logLevel": {
"Default": "Information",
"DurableTask.AzureStorage": "Warning",
"DurableTask.Core": "Warning"
},
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensions": {
"durableTask": {
"hubName": "DotNetIsolatedSmokeTests"
}
}
}
7 changes: 7 additions & 0 deletions test/AzureFunctionsSmokeTests/local.settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
Loading
Loading