From 149fe1585b8efc1f4969d62525bcfed626e134a1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 2 Jan 2026 22:32:04 +0000
Subject: [PATCH 1/8] Initial plan
From 80e7d37f6bf155c06e3fae03c173fe1378e0bb97 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 2 Jan 2026 22:50:27 +0000
Subject: [PATCH 2/8] Add source generator smoke test scenario definitions
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
---
.../SourceGeneratorScenarios.cs | 210 ++++++++++++++++++
1 file changed, 210 insertions(+)
create mode 100644 test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
diff --git a/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs b/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
new file mode 100644
index 00000000..72aaa85b
--- /dev/null
+++ b/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
@@ -0,0 +1,210 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.IO;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Microsoft.Azure.Functions.Worker;
+using Microsoft.Azure.Functions.Worker.Http;
+using Microsoft.DurableTask;
+using Microsoft.DurableTask.Client;
+using Microsoft.DurableTask.Entities;
+using Microsoft.Extensions.Logging;
+
+namespace AzureFunctionsSmokeTests;
+
+///
+/// Input payload for the generated orchestration scenario.
+///
+/// The name to use when composing greetings.
+public record GeneratorRequest([property: JsonPropertyName("name")] string? Name);
+
+///
+/// Output payload for the generated orchestration scenario.
+///
+/// The greeting text created by the activity function.
+/// The length of the generated greeting.
+/// The current total stored in the entity.
+/// The response returned by the child orchestrator.
+/// The message carried by the durable event.
+public record GeneratorResult(
+ [property: JsonPropertyName("greeting")] string Greeting,
+ [property: JsonPropertyName("greetingLength")] int GreetingLength,
+ [property: JsonPropertyName("counterTotal")] int CounterTotal,
+ [property: JsonPropertyName("childMessage")] string ChildMessage,
+ [property: JsonPropertyName("eventMessage")] string EventMessage);
+
+///
+/// Durable event payload used by the generated orchestration.
+///
+/// The event message.
+[DurableEvent("GeneratorSignal")]
+public record GeneratorSignal([property: JsonPropertyName("message")] string Message);
+
+///
+/// Entity state used by .
+///
+public sealed class GeneratorCounterState
+{
+ ///
+ /// Gets or sets the running total tracked by the entity.
+ ///
+ public int Count { get; set; }
+}
+
+///
+/// Entity implementation used to validate source generator entity trigger output.
+///
+[DurableTask(nameof(GeneratorCounter))]
+public sealed class GeneratorCounter : TaskEntity
+{
+ ///
+ /// Increments the counter by the specified amount.
+ ///
+ /// The task entity context.
+ /// The amount to add.
+ public void Add(TaskEntityContext context, int amount)
+ {
+ this.State.Count += amount;
+ }
+
+ ///
+ /// Gets the current counter value.
+ ///
+ /// The current counter total.
+ public int GetCount()
+ {
+ return this.State.Count;
+ }
+
+ ///
+ protected override GeneratorCounterState InitializeState(TaskEntityOperation entityOperation)
+ {
+ return new GeneratorCounterState();
+ }
+}
+
+///
+/// Activity used to validate source generator activity trigger output.
+///
+[DurableTask(nameof(CountCharactersActivity))]
+public sealed class CountCharactersActivity : TaskActivity
+{
+ ///
+ public override Task RunAsync(TaskActivityContext context, string input)
+ {
+ return Task.FromResult(input?.Length ?? 0);
+ }
+}
+
+///
+/// Child orchestrator used to validate generated sub-orchestration call methods.
+///
+[DurableTask(nameof(ChildGeneratedOrchestration))]
+public sealed class ChildGeneratedOrchestration : TaskOrchestrator
+{
+ ///
+ public override Task RunAsync(TaskOrchestrationContext context, int input)
+ {
+ return Task.FromResult($"Child processed {input}");
+ }
+}
+
+///
+/// Primary orchestration that exercises the Durable Task source generator output for Azure Functions.
+///
+[DurableTask(nameof(GeneratedOrchestration))]
+public sealed class GeneratedOrchestration : TaskOrchestrator
+{
+ const string DefaultName = "SourceGen";
+
+ ///
+ public override async Task RunAsync(TaskOrchestrationContext context, GeneratorRequest? input)
+ {
+ string name = string.IsNullOrWhiteSpace(input?.Name) ? DefaultName : input!.Name!;
+
+ // Function-based activity trigger call using generated extension.
+ string greeting = await context.CallComposeGreetingAsync(name);
+
+ // Class-based activity call using generated extension and activity trigger generated by source generator.
+ int length = await context.CallCountCharactersActivityAsync(greeting);
+
+ // Entity trigger generated by source generator.
+ EntityId counterId = new EntityId(nameof(GeneratorCounter), context.InstanceId);
+ await context.SignalEntityAsync(counterId, "Add", length);
+ int total = await context.CallEntityAsync(counterId, "GetCount");
+
+ // Durable event extensions generated by source generator.
+ context.SendGeneratorSignal(context.InstanceId, new GeneratorSignal($"Processed {name}"));
+ GeneratorSignal confirmation = await context.WaitForGeneratorSignalAsync();
+
+ // Sub-orchestration call using generated extension methods.
+ string childMessage = await context.CallChildGeneratedOrchestrationAsync(length);
+
+ return new GeneratorResult(greeting, length, total, childMessage, confirmation.Message);
+ }
+}
+
+///
+/// HTTP trigger and auxiliary functions used to start source generator scenarios.
+///
+public static class GeneratorFunctions
+{
+ ///
+ /// Composes a greeting string. Generates an activity trigger via source generators.
+ ///
+ /// The name to greet.
+ /// The greeting text.
+ [Function(nameof(ComposeGreeting))]
+ public static string ComposeGreeting([ActivityTrigger] string name)
+ {
+ return $"Hello, {name}!";
+ }
+
+ ///
+ /// Starts the generated orchestration using a generated scheduling extension method.
+ ///
+ /// The HTTP request.
+ /// The durable client.
+ /// The function execution context.
+ /// The HTTP response.
+ [Function("GeneratedOrchestration_HttpStart")]
+ public static async Task StartGeneratedOrchestrationAsync(
+ [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
+ [DurableClient] DurableTaskClient client,
+ FunctionContext executionContext)
+ {
+ ILogger logger = executionContext.GetLogger("GeneratedOrchestration_HttpStart");
+
+ GeneratorRequest? request = await TryReadRequestAsync(req);
+ string instanceId = await client.ScheduleNewGeneratedOrchestrationInstanceAsync(
+ request ?? new GeneratorRequest(DefaultName));
+
+ logger.LogInformation("Started generated orchestration with ID = '{InstanceId}'.", instanceId);
+ return client.CreateCheckStatusResponse(req, instanceId);
+ }
+
+ static async Task TryReadRequestAsync(HttpRequestData req)
+ {
+ if (req.Body.CanSeek)
+ {
+ req.Body.Seek(0, SeekOrigin.Begin);
+ }
+
+ using StreamReader reader = new(req.Body);
+ string body = await reader.ReadToEndAsync();
+ if (string.IsNullOrWhiteSpace(body))
+ {
+ return null;
+ }
+
+ try
+ {
+ return JsonSerializer.Deserialize(body);
+ }
+ catch (JsonException)
+ {
+ return null;
+ }
+ }
+}
From 8d9a3a22082bd3b3f7f4b2ea670b7f515cdaaf41 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 2 Jan 2026 22:59:08 +0000
Subject: [PATCH 3/8] Expand Azure Functions smoke tests for source generator
coverage
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
---
.../AzureFunctionsSmokeTests.csproj | 3 +
test/AzureFunctionsSmokeTests/README.md | 3 +-
.../SourceGeneratorScenarios.cs | 10 +-
.../run-smoketests.ps1 | 117 ++++++++++++++++++
4 files changed, 127 insertions(+), 6 deletions(-)
diff --git a/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj b/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
index 219d5bc9..8b59c6fc 100644
--- a/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
+++ b/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
@@ -2,6 +2,9 @@
net8.0
+ AzureFunctionsSmokeTests
+ AzureFunctionsSmokeTests
+ AzureFunctionsSmokeTests
v4
Exe
enable
diff --git a/test/AzureFunctionsSmokeTests/README.md b/test/AzureFunctionsSmokeTests/README.md
index bfdf4241..527e4173 100644
--- a/test/AzureFunctionsSmokeTests/README.md
+++ b/test/AzureFunctionsSmokeTests/README.md
@@ -13,6 +13,7 @@ The smoke tests ensure that:
## Structure
- **HelloCitiesOrchestration.cs** - Simple orchestration that calls multiple activities
+- **SourceGeneratorScenarios.cs** - Class-based orchestration, activity, entity, and event coverage for source generator validation
- **Program.cs** - Azure Functions host entry point
- **host.json** - Azure Functions host configuration
- **local.settings.json** - Local development settings
@@ -42,7 +43,7 @@ The script will:
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
+7. Trigger the source generator orchestration and validate generated activity/entity/event/sub-orchestrator behaviors
8. Clean up all containers
### Parameters
diff --git a/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs b/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
index 72aaa85b..193228e1 100644
--- a/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
+++ b/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
@@ -116,7 +116,7 @@ public override Task RunAsync(TaskOrchestrationContext context, int inpu
[DurableTask(nameof(GeneratedOrchestration))]
public sealed class GeneratedOrchestration : TaskOrchestrator
{
- const string DefaultName = "SourceGen";
+ internal const string DefaultName = "SourceGen";
///
public override async Task RunAsync(TaskOrchestrationContext context, GeneratorRequest? input)
@@ -130,9 +130,9 @@ public override async Task RunAsync(TaskOrchestrationContext co
int length = await context.CallCountCharactersActivityAsync(greeting);
// Entity trigger generated by source generator.
- EntityId counterId = new EntityId(nameof(GeneratorCounter), context.InstanceId);
- await context.SignalEntityAsync(counterId, "Add", length);
- int total = await context.CallEntityAsync(counterId, "GetCount");
+ EntityInstanceId counterId = new EntityInstanceId(nameof(GeneratorCounter), context.InstanceId);
+ await context.Entities.SignalEntityAsync(counterId, "Add", length);
+ int total = await context.Entities.CallEntityAsync(counterId, "GetCount");
// Durable event extensions generated by source generator.
context.SendGeneratorSignal(context.InstanceId, new GeneratorSignal($"Processed {name}"));
@@ -178,7 +178,7 @@ public static async Task StartGeneratedOrchestrationAsync(
GeneratorRequest? request = await TryReadRequestAsync(req);
string instanceId = await client.ScheduleNewGeneratedOrchestrationInstanceAsync(
- request ?? new GeneratorRequest(DefaultName));
+ request ?? new GeneratorRequest(GeneratedOrchestration.DefaultName));
logger.LogInformation("Started generated orchestration with ID = '{InstanceId}'.", instanceId);
return client.CreateCheckStatusResponse(req, instanceId);
diff --git a/test/AzureFunctionsSmokeTests/run-smoketests.ps1 b/test/AzureFunctionsSmokeTests/run-smoketests.ps1
index 185b9e3d..304ff2e3 100644
--- a/test/AzureFunctionsSmokeTests/run-smoketests.ps1
+++ b/test/AzureFunctionsSmokeTests/run-smoketests.ps1
@@ -252,6 +252,123 @@ try {
throw "Orchestration did not complete within timeout period"
}
+ Write-Host ""
+
+ # Step 9: Trigger source generator orchestration
+ Write-Host "Step 9: Triggering source generator orchestration..." -ForegroundColor Green
+ $generatorStartUrl = "http://localhost:$Port/api/GeneratedOrchestration_HttpStart"
+
+ try {
+ $generatorStartResponse = Invoke-WebRequest -Uri $generatorStartUrl -Method Post -UseBasicParsing
+ if ($generatorStartResponse.StatusCode -ne 202) {
+ throw "Unexpected status code: $($generatorStartResponse.StatusCode)"
+ }
+ }
+ catch {
+ Write-Host "Failed to trigger source generator orchestration. Error: $_" -ForegroundColor Red
+ Write-Host "Container logs:" -ForegroundColor Yellow
+ docker logs $ContainerName
+ throw
+ }
+
+ $generatorResponse = $generatorStartResponse.Content | ConvertFrom-Json
+ $generatorStatusQuery = $generatorResponse.statusQueryGetUri
+ $generatorInstanceId = $generatorResponse.id
+
+ Write-Host "Source generator orchestration started with instance ID: $generatorInstanceId" -ForegroundColor Green
+ Write-Host "Status query URI: $generatorStatusQuery" -ForegroundColor Cyan
+ Write-Host ""
+
+ # Step 10: Poll for completion and validate source generator output
+ Write-Host "Step 10: Polling for source generator orchestration completion..." -ForegroundColor Green
+ $generatorStartTime = Get-Date
+ $generatorCompleted = $false
+ $generatorConsecutiveErrors = 0
+
+ while (((Get-Date) - $generatorStartTime).TotalSeconds -lt $Timeout) {
+ Start-Sleep -Seconds 2
+
+ try {
+ $generatorStatusResponse = Invoke-WebRequest -Uri $generatorStatusQuery -UseBasicParsing
+ $generatorStatus = $generatorStatusResponse.Content | ConvertFrom-Json
+ $generatorConsecutiveErrors = 0
+
+ Write-Host "Current status: $($generatorStatus.runtimeStatus)" -ForegroundColor Yellow
+
+ if ($generatorStatus.runtimeStatus -eq "Completed") {
+ $generatorCompleted = $true
+ $output = $generatorStatus.output
+ Write-Host ""
+ Write-Host "Source generator orchestration completed successfully!" -ForegroundColor Green
+ Write-Host "Output: $output" -ForegroundColor Cyan
+
+ $greeting = $output.greeting
+ if (-not $greeting) { $greeting = $output.Greeting }
+
+ $greetingLength = $output.greetingLength
+ if (-not $greetingLength) { $greetingLength = $output.GreetingLength }
+
+ $counterTotal = $output.counterTotal
+ if (-not $counterTotal) { $counterTotal = $output.CounterTotal }
+
+ $childMessage = $output.childMessage
+ if (-not $childMessage) { $childMessage = $output.ChildMessage }
+
+ $eventMessage = $output.eventMessage
+ if (-not $eventMessage) { $eventMessage = $output.EventMessage }
+
+ if ($greeting -ne "Hello, SourceGen!") {
+ throw "Unexpected greeting from generated activity: $greeting"
+ }
+
+ if (-not $greetingLength) {
+ throw "Greeting length was not populated correctly: $greetingLength"
+ }
+
+ $greetingLengthValue = [int]$greetingLength
+
+ if (-not $counterTotal) {
+ throw "Entity counter total was not populated correctly: $counterTotal"
+ }
+
+ $counterTotalValue = [int]$counterTotal
+
+ if ($counterTotalValue -ne $greetingLengthValue) {
+ throw "Entity counter total $counterTotalValue does not match greeting length $greetingLengthValue"
+ }
+
+ if (-not $childMessage -or ($childMessage -notlike "Child processed *")) {
+ throw "Unexpected child orchestration response: $childMessage"
+ }
+
+ if (-not $eventMessage -or ($eventMessage -notlike "Processed*")) {
+ throw "Unexpected durable event message: $eventMessage"
+ }
+
+ break
+ }
+ elseif ($generatorStatus.runtimeStatus -eq "Failed" -or $generatorStatus.runtimeStatus -eq "Terminated") {
+ throw "Orchestration ended with status: $($generatorStatus.runtimeStatus)"
+ }
+ }
+ catch {
+ $generatorConsecutiveErrors++
+ Write-Host "Error polling generator orchestration (attempt $generatorConsecutiveErrors/3): $_" -ForegroundColor Red
+
+ if ($generatorConsecutiveErrors -ge 3) {
+ Write-Host "Container logs:" -ForegroundColor Yellow
+ docker logs $ContainerName
+ throw "Too many consecutive errors polling source generator orchestration status"
+ }
+ }
+ }
+
+ if (-not $generatorCompleted) {
+ Write-Host "Container logs:" -ForegroundColor Yellow
+ docker logs $ContainerName
+ throw "Source generator orchestration did not complete within timeout period"
+ }
+
Write-Host ""
Write-Host "=== Smoke test completed successfully! ===" -ForegroundColor Green
}
From 59594f6475960c2d0340dfbe89e5554c799e7b68 Mon Sep 17 00:00:00 2001
From: wangbill
Date: Fri, 2 Jan 2026 17:06:38 -0800
Subject: [PATCH 4/8] Update
test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj | 1 -
1 file changed, 1 deletion(-)
diff --git a/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj b/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
index 8b59c6fc..ccb904fc 100644
--- a/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
+++ b/test/AzureFunctionsSmokeTests/AzureFunctionsSmokeTests.csproj
@@ -2,7 +2,6 @@
net8.0
- AzureFunctionsSmokeTests
AzureFunctionsSmokeTests
AzureFunctionsSmokeTests
v4
From 015841501e7907221dff647624a0fde87f0894e2 Mon Sep 17 00:00:00 2001
From: wangbill
Date: Fri, 2 Jan 2026 17:06:52 -0800
Subject: [PATCH 5/8] Update test/AzureFunctionsSmokeTests/README.md
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
test/AzureFunctionsSmokeTests/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/AzureFunctionsSmokeTests/README.md b/test/AzureFunctionsSmokeTests/README.md
index 527e4173..54cf761c 100644
--- a/test/AzureFunctionsSmokeTests/README.md
+++ b/test/AzureFunctionsSmokeTests/README.md
@@ -44,7 +44,7 @@ The script will:
5. Trigger the HelloCities orchestration via HTTP
6. Poll for orchestration completion
7. Trigger the source generator orchestration and validate generated activity/entity/event/sub-orchestrator behaviors
-8. Clean up all containers
+9. Clean up all containers
### Parameters
From a348938284114777ae6bc2102d7bbb9f1736f9a4 Mon Sep 17 00:00:00 2001
From: wangbill
Date: Fri, 2 Jan 2026 17:11:52 -0800
Subject: [PATCH 6/8] Update test/AzureFunctionsSmokeTests/run-smoketests.ps1
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
test/AzureFunctionsSmokeTests/run-smoketests.ps1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/AzureFunctionsSmokeTests/run-smoketests.ps1 b/test/AzureFunctionsSmokeTests/run-smoketests.ps1
index 304ff2e3..7729d96c 100644
--- a/test/AzureFunctionsSmokeTests/run-smoketests.ps1
+++ b/test/AzureFunctionsSmokeTests/run-smoketests.ps1
@@ -327,7 +327,7 @@ try {
$greetingLengthValue = [int]$greetingLength
- if (-not $counterTotal) {
+ if ($null -eq $counterTotal) {
throw "Entity counter total was not populated correctly: $counterTotal"
}
From 8afdd807351e57dbfbef735df256e9c5284ed6ff Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 3 Jan 2026 01:15:02 +0000
Subject: [PATCH 7/8] Handle null checks in smoke validation
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
---
test/AzureFunctionsSmokeTests/run-smoketests.ps1 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/AzureFunctionsSmokeTests/run-smoketests.ps1 b/test/AzureFunctionsSmokeTests/run-smoketests.ps1
index 7729d96c..542132b6 100644
--- a/test/AzureFunctionsSmokeTests/run-smoketests.ps1
+++ b/test/AzureFunctionsSmokeTests/run-smoketests.ps1
@@ -306,10 +306,10 @@ try {
if (-not $greeting) { $greeting = $output.Greeting }
$greetingLength = $output.greetingLength
- if (-not $greetingLength) { $greetingLength = $output.GreetingLength }
+ if ($null -eq $greetingLength) { $greetingLength = $output.GreetingLength }
$counterTotal = $output.counterTotal
- if (-not $counterTotal) { $counterTotal = $output.CounterTotal }
+ if ($null -eq $counterTotal) { $counterTotal = $output.CounterTotal }
$childMessage = $output.childMessage
if (-not $childMessage) { $childMessage = $output.ChildMessage }
@@ -321,7 +321,7 @@ try {
throw "Unexpected greeting from generated activity: $greeting"
}
- if (-not $greetingLength) {
+ if ($null -eq $greetingLength) {
throw "Greeting length was not populated correctly: $greetingLength"
}
From 410895f75615c80dab8ab148811ff6723a352c3a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 3 Jan 2026 01:19:40 +0000
Subject: [PATCH 8/8] Switch generator entity increment to CallEntityAsync
Co-authored-by: YunchuWang <12449837+YunchuWang@users.noreply.github.com>
---
test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs b/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
index 193228e1..0f09d1e2 100644
--- a/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
+++ b/test/AzureFunctionsSmokeTests/SourceGeneratorScenarios.cs
@@ -131,7 +131,7 @@ public override async Task RunAsync(TaskOrchestrationContext co
// Entity trigger generated by source generator.
EntityInstanceId counterId = new EntityInstanceId(nameof(GeneratorCounter), context.InstanceId);
- await context.Entities.SignalEntityAsync(counterId, "Add", length);
+ await context.Entities.CallEntityAsync(counterId, "Add", length);
int total = await context.Entities.CallEntityAsync(counterId, "GetCount");
// Durable event extensions generated by source generator.