Update
KNOWN_ISSUES.mdonly when there are actual new or resolved known issues — do not add routine PR changes. Do not updateREADME.mdunless the PR specifically changes user-facing documentation, APIs, or installation steps.
This is an integration testing framework for Azure Functions (dotnet-isolated) that provides a TestServer/WebApplicationFactory-like experience. It runs Azure Functions in-process without func.exe, using ASP.NET Core's TestServer for both the gRPC communication channel and the worker's HTTP server — no TCP ports are opened.
Current Status: FunctionsTestHost is fully functional for the current Worker SDK 2.x (.NET 10) samples and test suites. It supports both direct gRPC mode (ConfigureFunctionsWorkerDefaults()) and ASP.NET Core integration mode (ConfigureFunctionsWebApplication()), and works with both the classic IHostBuilder API and the modern IHostApplicationBuilder / FunctionsApplicationBuilder API (via WithHostApplicationBuilderFactory). Under the hood, the framework uses ASP.NET Core's TestServer for both the gRPC communication channel and the worker's HTTP server — no TCP ports are opened. Features include full CRUD, TimerTrigger, QueueTrigger, ServiceBusTrigger, BlobTrigger, EventGridTrigger, middleware assertions, Services, ConfigureSetting(), output binding capture, custom route prefixes, and service overrides via ConfigureServices. Startup/readiness is event-driven and the direct gRPC path precompiles route matching per host. All framework libraries target net8.0;net10.0 and declare <FrameworkReference Include="Microsoft.AspNetCore.App" /> to prevent ASP.NET Core type-identity issues. Tests run in parallel and in isolation across xUnit, NUnit, and TUnit. No known blockers.
-
AzureFunctions.TestFramework.Core: Main framework
FunctionsTestHost: Orchestrates worker startup and gRPC communicationGrpcHostService: Implements Azure Functions host gRPC protocol (bidirectional streaming)FindFunctionId(method, path, routePrefix): Route matching with{param}supportSendInvocationRequestAsync(invocationId, method, path): Fires InvocationRequest to worker; always includes an emptyRpcHttpParameterBindingfor the HTTP trigger binding name (e.g."req") soFunctionsHttpProxyingMiddleware.IsHttpTriggerFunctioncorrectly identifies the function andIHttpCoordinatorcoordination runs — without itFunctionContext.Items["HttpRequestContext"]is never populated
GrpcServerManager: Manages in-memory gRPC server lifecycle (backed byTestServer)WorkerHostService: Starts Azure Functions Worker using HostBuilder or FunctionsApplicationBuilder (in-process); when ASP.NET Core integration is detected, replaces Kestrel'sIServerwithTestServerand exposes the in-memoryHttpMessageHandlerFunctionsHttpMessageHandler: Custom HttpMessageHandler for intercepting HTTP requestsHttpRequestMapper/HttpResponseMapper: Convert between HTTP and gRPC messages
-
AzureFunctions.TestFramework.Http: HTTP client support (
CreateHttpClient()extension), request/response mapping, and forwarding handlers for both direct gRPC and ASP.NET Core integration modes -
AzureFunctions.TestFramework.Timer: TimerTrigger invocation support — depends on Core +
Microsoft.Azure.Functions.Worker.Extensions.Timer. ExposesInvokeTimerAsync(this IFunctionsTestHost, string functionName, TimerInfo? timerInfo = null)extension method. -
AzureFunctions.TestFramework.Queue: QueueTrigger invocation —
InvokeQueueAsync(this IFunctionsTestHost, string functionName, string message). -
AzureFunctions.TestFramework.ServiceBus: ServiceBusTrigger invocation —
InvokeServiceBusAsync(this IFunctionsTestHost, string functionName, ServiceBusMessage message). -
AzureFunctions.TestFramework.Blob: BlobTrigger invocation —
InvokeBlobAsync(this IFunctionsTestHost, string functionName, BinaryData content, ...). -
AzureFunctions.TestFramework.EventGrid: EventGridTrigger invocation —
InvokeEventGridAsync(...)supporting bothEventGridEventandCloudEvent. -
AzureFunctions.TestFramework.Durable: Fake-backed durable support —
ConfigureFakeDurableSupport(...),FakeDurableTaskClient,FakeDurableTaskClientInputConverter,FakeTaskOrchestrationContext,FakeDurableExternalEventHub,FunctionsDurableClientProvider, andInvokeActivityAsync<TResult>(). Uses DI-based converter interception so[DurableClient] DurableTaskClientresolves in both gRPC-direct and ASP.NET Core paths. -
Sample.FunctionApp.Worker: Example functions (TodoAPI with CRUD + HeartbeatTimerFunction + CorrelationIdMiddleware + output binding demos + Queue/ServiceBus/Blob/EventGrid triggers). Exposes
Program.CreateHostBuilder(ASP.NET Core integration mode). -
Sample.FunctionApp: Minimal worker app (net10.0) used by sample test projects (
Sample.FunctionApp.Tests.XUnit,.NUnit,.TUnit). -
Sample.FunctionApp.Durable: Durable Functions sample (HTTP starter + orchestrator + activity + sub-orchestrator + external events). Exposes
Program.CreateHostBuilder(ASP.NET Core integration mode). -
Sample.FunctionApp.CustomRoutePrefix: Custom route prefix sample using
ConfigureFunctionsWorkerDefaults()withhost.jsonroutePrefix: "v1" -
Sample.FunctionApp.CustomRoutePrefix.AspNetCore: Custom route prefix sample using
ConfigureFunctionsWebApplication(). -
TestProject.HostBuilder: Function app project for the
IHostBuilderdirect-gRPC test flavour. ExposesProgram.CreateWorkerHostBuilder. -
TestProject.HostBuilder.AspNetCore: Function app project for the
IHostBuilderASP.NET Core test flavour. ExposesProgram.CreateHostBuilder. -
TestProject.HostApplicationBuilder: Function app project for the
FunctionsApplicationBuilderdirect-gRPC test flavour. ExposesProgram.CreateApplicationBuilder. -
TestProject.HostApplicationBuilder.AspNetCore: Function app project for the
FunctionsApplicationBuilderASP.NET Core test flavour. ExposesProgram.CreateWebApplicationBuilder. -
tests/Shared: Shared test logic consumed by all test flavour projects.
Shared/Functions/— shared function implementations used by all test projectsShared/Tests/— abstract base classes for all test categories (HTTP trigger, middleware, triggers, durable, custom route prefix, etc.)TestProject.Shared— shared class library (services, models) referenced by the test function-app projects
-
TestProject.HostBuilder.Tests: xUnit tests — direct gRPC mode,
IHostBuilder -
TestProject.HostBuilder.AspNetCore.Tests: xUnit tests — ASP.NET Core integration mode,
IHostBuilder -
TestProject.HostApplicationBuilder.Tests: xUnit tests — direct gRPC mode,
FunctionsApplicationBuilder -
TestProject.HostApplicationBuilder.AspNetCore.Tests: xUnit tests — ASP.NET Core integration mode,
FunctionsApplicationBuilder -
TestProject.CustomRoutePrefix.HostBuilder.Tests: xUnit CRP tests — direct gRPC mode,
IHostBuilder -
TestProject.CustomRoutePrefix.HostBuilder.AspNetCore.Tests: xUnit CRP tests — ASP.NET Core integration mode,
IHostBuilder -
TestProject.CustomRoutePrefix.HostApplicationBuilder.Tests: xUnit CRP tests — direct gRPC mode,
FunctionsApplicationBuilder -
TestProject.CustomRoutePrefix.HostApplicationBuilder.AspNetCore.Tests: xUnit CRP tests — ASP.NET Core integration mode,
FunctionsApplicationBuilder -
Sample.FunctionApp.Worker.Tests:
FunctionsTestHostintegration tests covering ASP.NET Core integration mode (xUnit) -
Sample.FunctionApp.Durable.Tests: Durable Functions integration tests (xUnit)
-
Sample.FunctionApp.CustomRoutePrefix.Tests: Custom route prefix tests via direct gRPC mode (WorkerDefaults, xUnit)
-
Sample.FunctionApp.CustomRoutePrefix.AspNetCore.Tests: Custom route prefix tests via ASP.NET Core integration mode (AspNetCore, xUnit)
Mode 1: Direct gRPC (ConfigureFunctionsWorkerDefaults())
- Build Phase:
FunctionsTestHostBuilder.Build()creates GrpcServerManager and starts it withTestServer(in-memory, no TCP port) - Startup Phase: WorkerHostService creates HostBuilder, configures it to connect to gRPC server via an in-memory
HttpMessageHandler; Worker'sStartAsync()connects; GrpcHostService handles bidirectional streaming (EventStream RPC) - Testing Phase: HttpClient uses FunctionsHttpMessageHandler → converts HTTP request → gRPC InvocationRequest → worker executes → gRPC InvocationResponse → HTTP response
Mode 2: ASP.NET Core integration (ConfigureFunctionsWebApplication())
- Same startup as above; additionally, WorkerHostService replaces the worker's Kestrel
IServerwithTestServer(in-memory, no TCP port) WorkerHostServicedetects ASP.NET Core integration and obtains the in-memoryHttpMessageHandlerfromTestServerAspNetCoreForwardingHandlerroutes HTTP requests through the in-memory handler instead of over gRPC- The full ASP.NET Core middleware pipeline runs;
HttpRequest,FunctionContext, typed route params, andCancellationTokenall bind correctly
The test writes use WithHostBuilderFactory(Program.CreateHostBuilder) for ASP.NET Core integration mode or WithHostBuilderFactory(Program.CreateWorkerHostBuilder) for direct gRPC mode. Alternatively, WithHostApplicationBuilderFactory(Program.CreateApplicationBuilder) or WithHostApplicationBuilderFactory(Program.CreateWebApplicationBuilder) can be used when the function app uses the modern FunctionsApplication.CreateBuilder() startup pattern. The mode is auto-detected.
- Function discovery:
GrpcHostServiceparsestimerTriggerbindings duringHandleFunctionsMetadataResponse, populating_timerFunctionMap[functionName] = (FunctionId, ParameterName) - API:
host.InvokeTimerAsync("HeartbeatTimer", timerInfo?)(fromAzureFunctions.TestFramework.Timer) - Flow: Extension method serializes
TimerInfo→ camelCase JSON, puts it atcontext.InputData["$timerJson"], callshost.Invoker.InvokeAsyncwithTriggerType = "timerTrigger"; Core reads it back and callsGrpcHostService.InvokeTimerFunctionAsyncwhich builds anInvocationRequestwith the timer JSON asParameterBinding
All trigger packages follow the same pattern through IFunctionInvoker.InvokeAsync:
- Extension method (e.g.
InvokeQueueAsync) serializes the trigger-specific payload and sets it onInvocationContext.InputData - Core's
IFunctionInvokerreads the input data and callsGrpcHostServiceto build anInvocationRequestwith the appropriateParameterBindingfor the trigger type - Worker deserializes the binding and executes the function
- Result is captured in
FunctionInvocationResult, which surfaces plain return values and output-binding payloads
FunctionInvocationResult captures ReturnValue and OutputData from non-HTTP trigger invocations:
ReadReturnValueAs<T>()— typed access to the function's return valueReadOutputAs<T>(bindingName)— typed access to named output bindings (queue, blob, table, etc.)
When using ConfigureFunctionsWebApplication(), the ASP.NET Core path does not send synthetic durable binding data in InputData. The real DurableTaskClientConverter gets null context.Source and [ConverterFallbackBehavior(Disallow)] blocks fallback. The framework registers FakeDurableTaskClientInputConverter in DI as the service for the real DurableTaskClientConverter type, so ActivatorUtilities.GetServiceOrCreateInstance returns our fake instead.
The worker needs these configuration keys (set internally by WorkerHostService):
Functions:Worker:HostEndpoint- gRPC server URI (placeholder; actual traffic is routed in-memory viaInMemoryGrpcClientFactory)Functions:Worker:WorkerId- Unique GUIDFunctions:Worker:RequestId- Unique GUIDFunctions:Worker:GrpcMaxMessageLength- "2147483647"
ℹ️ The
HostEndpointURI is still required by the Worker SDK for configuration validation, but no TCP connection is made. The framework'sInMemoryGrpcClientFactoryintercepts gRPC client creation and routes all traffic throughTestServer.CreateHandler()— a pure in-memoryHttpMessageHandler.
Uses Azure Functions RPC protocol from azure-functions-language-worker-protobuf:
- FunctionRpc.EventStream: Bidirectional streaming RPC
- Key messages: StartStream, WorkerInitRequest/Response, FunctionsMetadataRequest/Response, FunctionLoadRequest/Response, InvocationRequest/Response
The functions assembly contains source-generated classes (FunctionMetadataProviderAutoStartup, FunctionExecutorAutoStartup) implementing IAutoConfigureStartup. WorkerHostService scans for and invokes these to register GeneratedFunctionMetadataProvider and DirectFunctionExecutor, overriding the defaults that would require a functions.metadata file.
The Worker SDK's DefaultMethodInfoLocator.GetMethod() calls AssemblyLoadContext.Default.LoadFromAssemblyPath() during FunctionLoadRequest processing. In-process hosting (test runner + worker in same process) can cause the same assembly to be loaded twice, breaking type-identity checks (typeof(T) ==, obj is T). The framework prevents this at three layers:
-
Root fix:
InProcessMethodInfoLocator— Replaces the SDK's internalIMethodInfoLocatorviaDispatchProxy(since the interface is internal). SearchesAppDomain.CurrentDomain.GetAssemblies()for already-loaded assemblies instead of callingLoadFromAssemblyPath. Registered withAddSingleton(notTryAdd) so it wins over the SDK'sTryAddSingleton. Falls back toLoadFromAssemblyPathonly if the assembly isn't already loaded. -
Defense-in-depth:
TestFunctionContextConverter+TestHttpRequestConverter— Registered at converter index 0 viaPostConfigure<WorkerOptions>, these compare byFullNamestrings (immune to dual-load) and use reflection to access properties (bypassingis Tcasts). -
Build-time:
<FrameworkReference Include="Microsoft.AspNetCore.App" />— Ensures ASP.NET Core types resolve from the shared framework, not from NuGet packages.
FunctionsTestHostBuilder.Build() reads extensions.http.routePrefix from the functions assembly's host.json. The prefix is used by FunctionsHttpMessageHandler (to strip it when matching routes) and by FunctionsTestHost (to set HttpClient.BaseAddress). This makes custom route prefixes (e.g. "v1") work transparently without test-side configuration.
GeneratedFunctionMetadataProvider computes a stable hash for each function (Name + ScriptFile + EntryPoint). GrpcHostService stores the hash-based FunctionId from FunctionMetadataResponse in _functionRouteToId (not the GUID from FunctionLoadRequest), so SendInvocationRequestAsync sends the correct ID that matches the worker's internal _functionMap.
The Azure Functions worker SDK's GrpcWorker.StopAsync() returns Task.CompletedTask immediately — it does NOT close the gRPC channel. FunctionsTestHost calls _grpcHostService.SignalShutdownAsync() before stopping _grpcServerManager to gracefully end the EventStream so TestServer can stop instantly.
# Build solution
dotnet build
# Run all tests
dotnet test
# 4-flavour test matrix (IHostBuilder / FunctionsApplicationBuilder × direct gRPC / ASP.NET Core)
dotnet test tests/TestProject.HostBuilder.Tests
dotnet test tests/TestProject.HostBuilder.AspNetCore.Tests
dotnet test tests/TestProject.HostApplicationBuilder.Tests
dotnet test tests/TestProject.HostApplicationBuilder.AspNetCore.Tests
# Custom route prefix tests (4-flavour)
dotnet test tests/TestProject.CustomRoutePrefix.HostBuilder.Tests
dotnet test tests/TestProject.CustomRoutePrefix.HostBuilder.AspNetCore.Tests
dotnet test tests/TestProject.CustomRoutePrefix.HostApplicationBuilder.Tests
dotnet test tests/TestProject.CustomRoutePrefix.HostApplicationBuilder.AspNetCore.Tests
# Worker SDK 2.x sample tests (xUnit)
dotnet test samples/Sample.FunctionApp.Worker.Tests
# Durable Functions tests
dotnet test samples/Sample.FunctionApp.Durable.Tests
# Custom route prefix sample tests
dotnet test samples/Sample.FunctionApp.CustomRoutePrefix.Tests
dotnet test samples/Sample.FunctionApp.CustomRoutePrefix.AspNetCore.Tests
# Run single test
dotnet test samples/Sample.FunctionApp.Worker.Tests --filter "GetTodos_ReturnsEmptyList" --logger "console;verbosity=detailed"- Use nullable reference types
- Add XML documentation for public APIs
- Follow existing patterns in GrpcHostService for async message handling
- Don't block the gRPC event stream (use Task.Run for long-running operations)
- xUnit tests use
IAsyncLifetimeper-test (each test gets its ownFunctionsTestHost) - NUnit tests use
[SetUp]/[TearDown]for per-test host lifecycle - Shared-host patterns use
IClassFixture(xUnit) or[OneTimeSetUp](NUnit) + per-test state reset - New features must be tested across all four flavours: direct gRPC × IHostBuilder, direct gRPC × FunctionsApplicationBuilder, ASP.NET Core × IHostBuilder, ASP.NET Core × FunctionsApplicationBuilder. Shared test logic lives in
tests/Shared/Tests/as abstract base classes consumed by each flavour's test project. - One test project references exactly one function app project. CRP (custom route prefix) test projects are separate from main test projects.
- Each function app project's
Program.csexposes a single builder factory method (no multi-builder files). UseMiddleware<T>()onFunctionsApplicationBuilderrequiresusing Microsoft.Extensions.Hosting;— it is an extension method fromMiddlewareWorkerApplicationBuilderExtensionsin that namespace
src/
AzureFunctions.TestFramework.Core/
Core abstractions, in-memory gRPC server (TestServer-backed), worker hosting — both direct gRPC and ASP.NET Core integration modes
├── Grpc/
│ ├── GrpcHostService.cs # Bidirectional streaming handler + route matching
│ ├── GrpcServerManager.cs # In-memory gRPC server lifecycle (TestServer)
│ ├── InMemoryWorkerClientFactory.cs # Routes Worker SDK gRPC traffic through TestServer (no TCP)
│ └── GrpcLoggingInterceptor.cs # Logging middleware
├── Worker/
│ ├── WorkerHostService.cs # In-process worker hosting (IHostBuilder + FunctionsApplicationBuilder; replaces Kestrel with TestServer)
│ ├── InProcessMethodInfoLocator.cs # DispatchProxy-based IMethodInfoLocator replacement
│ └── Converters/
│ ├── TestFunctionContextConverter.cs # ALC defense-in-depth
│ └── TestHttpRequestConverter.cs # ALC defense-in-depth
├── Protos/
│ └── FunctionRpc.proto # Azure Functions RPC protocol
├── FunctionsTestHost.cs # Main orchestrator
├── FunctionsTestHostBuilder.cs # Fluent builder API (WithHostBuilderFactory + WithHostApplicationBuilderFactory)
└── IFunctionsTestHostBuilder.cs # Builder interface
AzureFunctions.TestFramework.Http/
HTTP client support, request/response mapping, and forwarding handlers
├── FunctionsTestHostHttpExtensions.cs # CreateHttpClient() extension — auto-selects gRPC or TestServer handler
├── FunctionsHttpMessageHandler.cs # Routes requests via gRPC InvocationRequest (direct gRPC mode)
├── AspNetCoreForwardingHandler.cs # Routes requests via in-memory TestServer (ASP.NET Core integration mode)
├── HttpRequestMapper.cs # HTTP → gRPC conversion
└── HttpResponseMapper.cs # gRPC → HTTP conversion
AzureFunctions.TestFramework.Timer/ # InvokeTimerAsync extension
AzureFunctions.TestFramework.Queue/ # InvokeQueueAsync extension
AzureFunctions.TestFramework.ServiceBus/ # InvokeServiceBusAsync extension
AzureFunctions.TestFramework.Blob/ # InvokeBlobAsync extension
AzureFunctions.TestFramework.EventGrid/ # InvokeEventGridAsync extension
AzureFunctions.TestFramework.Durable/ # Fake durable support (converter, client, context, runner, events)
samples/
Sample.FunctionApp/
Minimal worker app (net10.0) — used by sample test projects
Sample.FunctionApp.Tests.XUnit/ # xUnit sample test project
Sample.FunctionApp.Tests.NUnit/ # NUnit sample test project
Sample.FunctionApp.Tests.TUnit/ # TUnit sample test project
Sample.FunctionApp.Worker/
Worker SDK 2.x sample (net10.0) — TodoAPI, middleware, triggers, output bindings
CreateHostBuilder = ASP.NET Core integration mode
Sample.FunctionApp.Worker.Tests/ # xUnit sample integration tests
Sample.FunctionApp.Durable/
Durable Functions sample (net10.0) — HTTP starter + orchestrator + activity + sub-orchestrator
CreateHostBuilder = ASP.NET Core integration mode
Sample.FunctionApp.Durable.Tests/ # xUnit Durable sample tests
Sample.FunctionApp.CustomRoutePrefix/
Custom route prefix sample (net10.0) — ConfigureFunctionsWorkerDefaults() + host.json routePrefix "v1"
Sample.FunctionApp.CustomRoutePrefix.Tests/ # xUnit CRP sample tests (gRPC)
Sample.FunctionApp.CustomRoutePrefix.AspNetCore/
Custom route prefix sample (net10.0) — ConfigureFunctionsWebApplication()
Sample.FunctionApp.CustomRoutePrefix.AspNetCore.Tests/ # xUnit CRP sample tests (ASP.NET Core)
tests/
# 4-flavour test matrix — shared logic in tests/Shared/
TestProject.HostBuilder/
Function app — IHostBuilder, ConfigureFunctionsWorkerDefaults()
CreateWorkerHostBuilder = direct gRPC mode
TestProject.HostBuilder.Tests/ # xUnit — direct gRPC, IHostBuilder
TestProject.HostBuilder.AspNetCore/
Function app — IHostBuilder, ConfigureFunctionsWebApplication()
CreateHostBuilder = ASP.NET Core integration mode
TestProject.HostBuilder.AspNetCore.Tests/ # xUnit — ASP.NET Core integration, IHostBuilder
TestProject.HostApplicationBuilder/
Function app — FunctionsApplicationBuilder, ConfigureFunctionsWorkerDefaults()
CreateApplicationBuilder = direct gRPC mode
TestProject.HostApplicationBuilder.Tests/ # xUnit — direct gRPC, FunctionsApplicationBuilder
TestProject.HostApplicationBuilder.AspNetCore/
Function app — FunctionsApplicationBuilder, ConfigureFunctionsWebApplication()
CreateWebApplicationBuilder = ASP.NET Core integration mode
TestProject.HostApplicationBuilder.AspNetCore.Tests/ # xUnit — ASP.NET Core integration, FunctionsApplicationBuilder
# Custom route prefix 4-flavour matrix (one test project per CRP function app)
TestProject.CustomRoutePrefix.HostBuilder/ # CRP — IHostBuilder, gRPC
TestProject.CustomRoutePrefix.HostBuilder.Tests/
TestProject.CustomRoutePrefix.HostBuilder.AspNetCore/ # CRP — IHostBuilder, ASP.NET Core
TestProject.CustomRoutePrefix.HostBuilder.AspNetCore.Tests/
TestProject.CustomRoutePrefix.HostApplicationBuilder/ # CRP — FunctionsApplicationBuilder, gRPC
TestProject.CustomRoutePrefix.HostApplicationBuilder.Tests/
TestProject.CustomRoutePrefix.HostApplicationBuilder.AspNetCore/ # CRP — FunctionsApplicationBuilder, ASP.NET Core
TestProject.CustomRoutePrefix.HostApplicationBuilder.AspNetCore.Tests/
Shared/
Functions/ # Shared function implementations for the 4-flavour matrix
Tests/ # Abstract base classes for all test categories
TestProject.Shared/ # Shared class library (services, models) consumed by test projects
- Azure Functions Worker: https://github.com/Azure/azure-functions-dotnet-worker
- RPC Protocol: https://github.com/Azure/azure-functions-language-worker-protobuf
- Worker Configuration: See WorkerHostBuilderExtensions.cs in azure-functions-dotnet-worker
✅ Solution builds successfully (net8.0 / net10.0)
✅ Worker starts in-process using HostBuilder
✅ Worker starts in-process using FunctionsApplicationBuilder (IHostApplicationBuilder)
✅ Worker connects to gRPC server
✅ gRPC bidirectional streaming works
✅ Function loading/discovery
✅ Function invocation works (FunctionsTestHost — all HTTP methods + all trigger types)
✅ All FunctionsTestHost integration tests pass (xUnit + NUnit + TUnit)
✅ Direct gRPC mode: full CRUD, middleware, service overrides, configuration, output bindings
✅ ASP.NET Core integration mode: full CRUD, HttpRequest, FunctionContext, typed route params, CancellationToken, middleware, service overrides
✅ IHostBuilder and FunctionsApplicationBuilder both supported across direct gRPC and ASP.NET Core modes (4-flavour matrix)
✅ Tests run in parallel and in isolation (xUnit parallelizeTestCollections + IAsyncLifetime / NUnit SetUp+TearDown)
✅ Graceful gRPC EventStream shutdown (no connection-abort errors)
✅ CI workflow runs on pull requests and pushes to main (xUnit + NUnit suites)
✅ Current sample targets Worker SDK 2.x (2.51.0)
✅ All framework libraries target net8.0;net10.0
✅ Custom route prefix auto-detection from host.json
✅ Output binding capture (queue, blob, table)
✅ Durable Functions (starter, orchestrator, activity, sub-orchestrator, external events)
✅ NuGet packaging with MinVer, Source Link, symbol packages
✅ Worker-side logging configurable via ConfigureWorkerLogging (routes function ILogger output to test output)
🎯 Code coverage target: ≥ 80% line coverage on framework source (Core, Http, Timer, Queue, ServiceBus, Blob, EventGrid, Durable)