Skip to content

Integration CosmosDB Extension

aryehcitron@gmail.com edited this page May 24, 2026 · 27 revisions

The Kronikol.Extensions.CosmosDB package adds Azure Cosmos DB SDK operation tracking to your test diagrams. Instead of showing raw HTTP requests like POST /dbs/abc123/colls/xyz789/docs, your sequence diagrams show classified operations like Read: /colls/orders/docs/order-1 or Query: /orders.

Using a shared library or abstraction layer? If your code doesn't use the Cosmos DB SDK directly — e.g. it goes through a shared repository library, wrapper, or custom abstraction — this extension won't be able to intercept the underlying calls. See Tracking Custom Dependencies for alternative approaches including RequestResponseLogger.LogPair(), TrackingProxy<T>, and MessageTracker.


How It Works

The Azure Cosmos DB SDK translates all operations into HTTP requests internally. CosmosTrackingMessageHandler is a DelegatingHandler that intercepts these HTTP requests, classifies each one into a Cosmos operation (Create, Read, Query, Upsert, etc.) using the HTTP method, URL path, and headers, then logs it to RequestResponseLogger with a human-readable label.

Because it logs to the same RequestResponseLogger as the standard TestTrackingMessageHandler, Cosmos operations appear alongside your HTTP API calls in the same sequence diagram — showing the complete flow from test → API → Cosmos DB.

Important: This requires ConnectionMode.Gateway. Cosmos DB's Direct mode uses a custom TCP protocol (RNTBD) that bypasses HttpMessageHandler entirely. All setup methods in this package force Gateway mode automatically.


Install

dotnet add package Kronikol.Extensions.CosmosDB

Verbosity Levels

The extension supports three verbosity levels that control how much detail appears in the diagrams:

Level Method shown URI shown Headers Request body Response body
Raw HTTP method (POST, GET, etc.) Full SDK URI (with _rid-encoded paths) All except excluded set Full JSON Full JSON
Detailed Classified operation (Read) Clean path without database (/colls/orders/docs/order-1) Filtered (noisy Cosmos headers excluded) SQL text for queries, full JSON for others Full JSON
Summarised Classified operation (Read) Collection name only (/orders) None SQL text for queries only Full JSON

The default is Detailed. Response body is shown at all verbosity levels when LogResponseContent = true (the default). Set LogResponseContent = false for empty response arrows (previous behaviour).

Diagram Label Examples

Operation Raw Detailed Summarised
Create a document POST: /dbs/mydb/colls/orders/docs Create: /colls/orders Create: /orders
Read a document GET: /dbs/mydb/colls/orders/docs/order-1 Read: /colls/orders/docs/order-1 Read: /orders
Query documents POST: /dbs/mydb/colls/orders/docs Query: /colls/orders Query: /orders
Upsert a document POST: /dbs/mydb/colls/orders/docs Upsert: /colls/orders Upsert: /orders
Delete a document DELETE: /dbs/mydb/colls/orders/docs/order-1 Delete: /colls/orders/docs/order-1 Delete: /orders
Replace a document PUT: /dbs/mydb/colls/orders/docs/order-1 Replace: /colls/orders/docs/order-1 Replace: /orders
Patch a document PATCH: /dbs/mydb/colls/orders/docs/order-1 Patch: /colls/orders/docs/order-1 Patch: /orders
List documents GET: /dbs/mydb/colls/orders/docs List: /colls/orders List: /orders
Execute stored proc POST: /dbs/mydb/colls/orders/sprocs/mysproc ExecStoredProc: /colls/orders/sprocs/mysproc ExecStoredProc: /orders
Batch operation POST: /dbs/mydb/colls/orders/docs Batch: /colls/orders Batch: /orders
SDK metadata GET: /dbs/mydb/colls/orders/pkranges Other: /colls/orders (skipped)

Classified Operations

The classifier recognises these Cosmos operations from the SDK's HTTP traffic:

Operation HTTP Pattern
Create POST /docs (no upsert or query headers)
Read GET /docs/{id}
Replace PUT /docs/{id}
Patch PATCH /docs/{id}
Delete DELETE /docs/{id}
Upsert POST /docs with x-ms-documentdb-is-upsert: true
Query POST /docs with x-ms-documentdb-isquery: true
List GET /docs (no document ID)
ExecStoredProc POST /sprocs/{id}
Batch POST /docs/{resourceId}
Other SDK metadata requests (partition key ranges, collection reads, etc.)

In Summarised mode, Other operations (SDK metadata) are silently skipped.


Setup

Which TestContext? The examples below use xUnit v3's Xunit.TestContext — it's ambient, so TestContext.Current is always available during test execution with no extra setup. If you're using a different framework (xUnit v2, NUnit, MSTest, TUnit, LightBDD), see CurrentTestInfoFetcher by Framework below for the equivalent snippet.

CosmosTrackingMessageHandler is a standard DelegatingHandler — it works with any Cosmos DB backend: CosmosDB.InMemoryEmulator, the Microsoft Azure Cosmos DB Emulator, a Testcontainers instance, or a real Azure Cosmos DB account. Choose the setup option that matches your test infrastructure.

Recommendation: CosmosDB.InMemoryEmulator gives you the fastest feedback loop — instant startup, no Docker, no network, and sub-millisecond operations. It also has first-class integration with this tracking extension via WithHttpMessageHandlerWrapper / WrapHandler. That said, the tracking handler itself is backend-agnostic and works identically regardless of what's behind it.

Option A: With CosmosDB.InMemoryEmulator — DI (recommended)

If you use CosmosDB.InMemoryEmulator with the UseInMemoryCosmosDB() DI extension, use WithHttpMessageHandlerWrapper to insert the tracking handler:

Requires CosmosDB.InMemoryEmulator 2.0.5 or later (the version that added WithHttpMessageHandlerWrapper). Options B and C below require 3.0.0 or later.

builder.ConfigureTestServices(services =>
{
    services.UseInMemoryCosmosDB(options => options
        .AddContainer("orders", "/customerId")
        .AddContainer("customers", "/id")
        .WithHttpMessageHandlerWrapper(fakeHandler =>
            new CosmosTrackingMessageHandler(
                new CosmosTrackingMessageHandlerOptions
                {
                    ServiceName = "CosmosDB",
                    CallerName = "My API",
                    Verbosity = CosmosTrackingVerbosity.Detailed,
                    // xUnit v3: Xunit.TestContext — ambient, always available during test execution
                    // For other frameworks, see 'CurrentTestInfoFetcher by Framework' below
                    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
                },
                fakeHandler)));
});

The pipeline becomes:

CosmosClient → CosmosTrackingMessageHandler → FakeCosmosHandler → InMemoryContainer

The tracking handler intercepts and logs every operation, then forwards the request to FakeCosmosHandler which serves in-memory responses.

Option B: With CosmosDB.InMemoryEmulator — Single Container (Manual)

If you use InMemoryCosmos directly with a single container, use the wrapHandler parameter:

var trackingOptions = new CosmosTrackingMessageHandlerOptions
{
    ServiceName = "CosmosDB",
    CallerName = "My API",
    Verbosity = CosmosTrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
};

using var cosmos = InMemoryCosmos.Create("orders", "/customerId",
    wrapHandler: h => new CosmosTrackingMessageHandler(trackingOptions, h));

var client = cosmos.Client;
var container = cosmos.Container;

Option C: With CosmosDB.InMemoryEmulator — Multi-Container (Manual)

For enterprise codebases with multiple Cosmos containers, use the InMemoryCosmos.Builder() with .WrapHandler() to insert the tracking handler around the internal routing handler:

var trackingOptions = new CosmosTrackingMessageHandlerOptions
{
    ServiceName = "CosmosDB",
    CallerName = "My API",
    Verbosity = CosmosTrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
};

using var cosmos = InMemoryCosmos.Builder()
    .AddContainer("orders", "/customerId")
    .AddContainer("customers", "/id")
    .AddContainer("products", "/categoryId")
    .WrapHandler(h => new CosmosTrackingMessageHandler(trackingOptions, h))
    .Build();

var client = cosmos.Client;
var orders = cosmos.Containers["orders"];
var customers = cosmos.Containers["customers"];
var products = cosmos.Containers["products"];

The pipeline becomes:

CosmosClient → CosmosTrackingMessageHandler → RoutingHandler → FakeCosmosHandler[orders]    → InMemoryContainer
                                                              → FakeCosmosHandler[customers] → InMemoryContainer
                                                              → FakeCosmosHandler[products]  → InMemoryContainer

Key point: .WrapHandler() wraps the internal routing handler, not individual FakeCosmosHandler instances. This means all container traffic flows through a single tracking handler and appears in the diagrams with the correct container name in the label (e.g. Create: /colls/orders, Query: /customers).

Deferred HttpContextAccessor assignment (v2.26.3+): When using WrapHandler inside a collection fixture constructor, DI doesn't exist yet — IHttpContextAccessor is only available after the WebApplicationFactory builds its service provider. Assign it after the factory is ready:

// In fixture constructor — DI doesn't exist yet:
_trackingOptions = new CosmosTrackingMessageHandlerOptions
{
    ServiceName = "CosmosDB",
    CallerName = "My API",
    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
    // HttpContextAccessor NOT set here
};

_cosmos = InMemoryCosmos.Builder()
    .AddContainer("orders", "/id")
    .WrapHandler(h => new CosmosTrackingMessageHandler(_trackingOptions, h))
    .Build();

// Later, after WebApplicationFactory is built:
_trackingOptions.HttpContextAccessor = WebFactory.Services.GetRequiredService<IHttpContextAccessor>();

This works because the handler reads HttpContextAccessor from the options object at invocation time, not at construction time.

Option D: Real Cosmos DB or Microsoft Emulator — Manual Client

For use against a real Azure Cosmos DB account or the Microsoft Cosmos DB Emulator:

var trackingOptions = new CosmosTrackingMessageHandlerOptions
{
    ServiceName = "CosmosDB",
    CallerName = "My API",
    Verbosity = CosmosTrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
};

var clientOptions = new CosmosClientOptions();
clientOptions.WithTestTracking(trackingOptions);

var client = new CosmosClient(connectionString, clientOptions);

For the Microsoft Cosmos DB Emulator (which uses self-signed certificates):

var clientOptions = new CosmosClientOptions();
clientOptions.WithTestTrackingAndCustomSslValidation(trackingOptions);

var client = new CosmosClient(
    "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
    clientOptions);

WithTestTracking forces ConnectionMode.Gateway because Direct mode bypasses HttpMessageHandler.

WithTestTrackingAndCustomSslValidation additionally configures ServerCertificateCustomValidationCallback to accept self-signed certificates.

The pipeline becomes:

CosmosClient → CosmosTrackingMessageHandler → HttpClientHandler → Azure Cosmos DB (or Emulator)

Option E: Real Cosmos DB or Microsoft Emulator — DI with WebApplicationFactory

If your production code registers CosmosClient in DI and you're using WebApplicationFactory, replace the client registration in ConfigureTestServices:

builder.ConfigureTestServices(services =>
{
    // Remove the existing CosmosClient registration
    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(CosmosClient));
    if (descriptor is not null) services.Remove(descriptor);

    var trackingOptions = new CosmosTrackingMessageHandlerOptions
    {
        ServiceName = "CosmosDB",
        CallerName = "My API",
        Verbosity = CosmosTrackingVerbosity.Detailed,
        CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
    };

    // Option 1: Against a real Azure Cosmos DB account
    services.AddSingleton(_ =>
    {
        var clientOptions = new CosmosClientOptions();
        clientOptions.WithTestTracking(trackingOptions);
        return new CosmosClient(connectionString, clientOptions);
    });

    // Option 2: Against the Microsoft Cosmos DB Emulator (self-signed certs)
    services.AddSingleton(_ =>
    {
        var clientOptions = new CosmosClientOptions();
        clientOptions.WithTestTrackingAndCustomSslValidation(trackingOptions);
        return new CosmosClient(
            "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
            clientOptions);
    });
});

Tip: If you're using Testcontainers to spin up the Cosmos emulator, use the container's connection string in place of the hardcoded emulator endpoint above. The tracking integration is identical.

Option F: Testcontainers Cosmos DB Emulator

If you use Testcontainers to manage a Cosmos DB emulator container per test run:

public class CosmosFixture : IAsyncLifetime
{
    private readonly CosmosDbEmulatorContainer _container =
        new CosmosDbEmulatorBuilder().Build();

    public CosmosClient Client { get; private set; } = null!;

    public async ValueTask InitializeAsync()
    {
        await _container.StartAsync();

        var trackingOptions = new CosmosTrackingMessageHandlerOptions
        {
            ServiceName = "CosmosDB",
            CallerName = "My API",
            Verbosity = CosmosTrackingVerbosity.Detailed,
            CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
        };

        var clientOptions = new CosmosClientOptions();
        clientOptions.WithTestTrackingAndCustomSslValidation(trackingOptions);

        Client = new CosmosClient(_container.GetConnectionString(), clientOptions);
    }

    public async ValueTask DisposeAsync()
    {
        Client.Dispose();
        await _container.DisposeAsync();
    }
}

Then share it across tests via xUnit's IClassFixture<CosmosFixture> or ICollectionFixture<CosmosFixture>.


Configuration Reference

CosmosTrackingMessageHandlerOptions

Property Type Default Description
ServiceName string "CosmosDB" The participant name shown in the diagram for the Cosmos DB service
CallerName string "Caller" The participant name shown for the service making Cosmos calls
Verbosity CosmosTrackingVerbosity Detailed Controls how much detail appears in the diagram (Raw, Detailed, Summarised)
CurrentTestInfoFetcher Func<(string Name, string Id)>? null Returns the current test's name and ID. Required — if null, requests are forwarded but not logged
CurrentStepTypeFetcher Func<string?>? null Optional — returns the current BDD step type (Given/When/Then)
HttpContextAccessor IHttpContextAccessor? null Optional — enables dual-resolution of test identity from HTTP headers. Auto-resolved by DI extensions (v2.26.3+). See HTTP Tracking Setup#Dual-Resolution Test Identity (v2.23.0+)
ExcludedHeaders HashSet<string> See below Headers to exclude from diagrams in Raw/Detailed mode
SetupVerbosity CosmosTrackingVerbosity? null Verbosity override for the Setup phase. See Phase-Aware Tracking
ActionVerbosity CosmosTrackingVerbosity? null Verbosity override for the Action phase. See Phase-Aware Tracking
TrackDuringSetup bool true When false, tracking is suppressed during Setup. See Phase-Aware Tracking
TrackDuringAction bool true When false, tracking is suppressed during Action. See Phase-Aware Tracking
AutoCorrelateWrites bool true When true, tracked write operations (Create, Upsert, Replace) auto-populate TestCorrelationStore for parallel-safe background thread correlation (e.g. Change Feed Processor attribution). See Change Feed Correlation
ChangeFeedKeyExtractor Func<string, string, string>? null Optional custom key extractor for Change Feed correlation. When null, uses the default key format cosmos:{ServiceName}:{documentId}
LogResponseContent bool true When true, response arrows show the HTTP response body at all verbosity levels. Set to false for empty response arrows (previous behaviour)

v2.23.0+ Dual-Resolution: CosmosTrackingMessageHandler accepts an optional IHttpContextAccessor? httpContextAccessor constructor parameter. When provided, the handler resolves test identity from HTTP request headers first (propagated by TestTrackingMessageHandler through the SUT pipeline), then falls back to CurrentTestInfoFetcher. v2.26.3+: Set HttpContextAccessor on CosmosTrackingMessageHandlerOptions instead — the tracker reads it automatically. See HTTP Tracking Setup#Dual-Resolution Test Identity (v2.23.0+) for full details and examples.

Default Excluded Headers

The following Cosmos SDK headers are excluded by default (they add noise without diagnostic value):

  • Authorization
  • x-ms-date
  • x-ms-version
  • x-ms-session-token
  • User-Agent
  • Cache-Control
  • x-ms-cosmos-sdk-supportedcapabilities
  • x-ms-cosmos-internal-operation-type

Override ExcludedHeaders to customise:

new CosmosTrackingMessageHandlerOptions
{
    ExcludedHeaders = ["Authorization", "x-ms-date"] // Only exclude these two
}

CurrentTestInfoFetcher by Framework

Every framework package provides a CurrentTestInfo static class with a Fetcher property. The syntax is identical regardless of framework — just make sure you have the correct using directive for your framework package:

CurrentTestInfoFetcher = CurrentTestInfo.Fetcher
Framework Package / using directive
xUnit v3 using Kronikol.xUnit3;
xUnit v2 using Kronikol.xUnit2;
NUnit 4 using Kronikol.NUnit4;
MSTest using Kronikol.MSTest;
TUnit using Kronikol.TUnit;
LightBDD using Kronikol.LightBDD;
ReqNRoll using Kronikol.ReqNRoll;
BDDfy using Kronikol.BDDfy.xUnit3;

Note: If you're already using a framework-specific options class (e.g. XUnitTestTrackingMessageHandlerOptions) for your main HTTP tracking, CurrentTestInfoFetcher is wired automatically — you only need CurrentTestInfo.Fetcher for standalone extensions like CosmosDB that use their own options class.


Extension Methods

CosmosClientOptions.WithTestTracking()

public static CosmosClientOptions WithTestTracking(
    this CosmosClientOptions options,
    CosmosTrackingMessageHandlerOptions trackingOptions,
    HttpMessageHandler? innerHandler = null)

Configures CosmosClientOptions to use CosmosTrackingMessageHandler. Forces ConnectionMode.Gateway. The optional innerHandler parameter sets the inner handler (defaults to HttpClientHandler).

CosmosClientOptions.WithTestTrackingAndCustomSslValidation()

public static CosmosClientOptions WithTestTrackingAndCustomSslValidation(
    this CosmosClientOptions options,
    CosmosTrackingMessageHandlerOptions trackingOptions)

Same as WithTestTracking but configures ServerCertificateCustomValidationCallback to accept all certificates.


Architecture

Single Container / Direct Handler

┌──────────────┐                ┌───────────────────────────────┐              ┌──────────────────────┐
│  Cosmos SDK  │ ── HTTP ──►    │ CosmosTrackingMessageHandler  │ ── HTTP ──►  │  Inner Handler       │
│  (real)      │ ◄── HTTP ──    │  • Classifies operation       │ ◄── HTTP ──  │  (FakeCosmosHandler  │
│              │                │  • Logs to RequestResponseLog │              │   or HttpClientHandler)
└──────────────┘                └───────────────────────────────┘              └──────────────────────┘
                                         │
                                         ▼
                               ┌──────────────────────┐
                               │ RequestResponseLogger │
                               │  (shared with other   │
                               │   tracking handlers)  │
                               └──────────────────────┘

Multi-Container with Routing Handler

┌──────────────┐              ┌───────────────────────────────┐              ┌────────────────┐
│  Cosmos SDK  │ ── HTTP ──►  │ CosmosTrackingMessageHandler  │ ── HTTP ──►  │ RoutingHandler │
│  (real)      │ ◄── HTTP ──  │  • Classifies operation       │ ◄── HTTP ──  │  (routes by    │
│              │              │  • Logs to RequestResponseLog │              │   container)   │
└──────────────┘              └───────────────────────────────┘              └──────┬─────────┘
                                       │                                           │
                                       ▼                                    ┌──────┴───────┐
                             ┌──────────────────────┐              ┌────────┴────┐  ┌───────┴──────┐
                             │ RequestResponseLogger │              │FakeHandler  │  │FakeHandler   │
                             │  (shared with other   │              │ [orders]    │  │ [customers]  │
                             │   tracking handlers)  │              └─────────────┘  └──────────────┘
                             └──────────────────────┘

The CosmosTrackingMessageHandler logs to the same RequestResponseLogger used by TestTrackingMessageHandler. This means Cosmos operations appear alongside your HTTP API calls in the same sequence diagram.


Differences from TestTrackingMessageHandler

TestTrackingMessageHandler CosmosTrackingMessageHandler
Target HTTP calls between services Cosmos DB SDK internal HTTP calls
Method label HTTP method (GET, POST) Classified operation (Read, Create)
URI Real URL Clean path (database stripped, collection/resource path retained)
Service name resolution Port-based (PortsToServiceNames) Fixed (ServiceName property)
Header forwarding Propagates tracking headers downstream Does not forward headers (SDK manages its own headers)
DiagramFocus Supported Not supported (SDK-initiated calls aren't from user code)
Metadata filtering None Other operations skipped in Summarised mode

Invocation Validation

CosmosTrackingMessageHandler implements ITrackingComponent and auto-registers with TrackingComponentRegistry on construction. At report generation time, unused components are automatically detected and surfaced as console warnings and in the diagnostic report (when DiagnosticMode=true). This never throws or fails tests.

See Diagnostics and Debugging for full details on the TrackingComponentRegistry API.


Using with CosmosDB.InMemoryEmulator

CosmosDB.InMemoryEmulator provides a FakeCosmosHandler that operates at the HTTP message handler level — exactly where CosmosTrackingMessageHandler sits. The two libraries compose naturally via WrapHandler():

var builder = InMemoryCosmos.Builder();
builder.AddContainer("event-stream", "/streamId");
builder.AddContainer("materialized-view", "/streamId");

// Insert CosmosTrackingMessageHandler into the handler chain
builder.WrapHandler(handler => new CosmosTrackingMessageHandler(
    new CosmosTrackingMessageHandlerOptions
    {
        ServiceName = "CosmosDB",
        CallerName = "My Service",
        Verbosity = CosmosTrackingVerbosity.Summarised,
        CurrentTestInfoFetcher = CurrentTestInfo.Fetcher,
    },
    handler));  // handler = the FakeCosmosHandler from InMemoryEmulator

var result = builder.Build();
var cosmosClient = result.Client;

Two-Phase HttpContextAccessor Setup

When the CosmosClient is created before the WebApplicationFactory builds its DI container (the standard pattern with InMemoryEmulator), IHttpContextAccessor can't be set at construction time. Set it after the host is built:

// Phase 1: Create CosmosClient (before DI exists)
var trackingOptions = new CosmosTrackingMessageHandlerOptions
{
    ServiceName = "CosmosDB",
    CallerName = "My Service",
    Verbosity = CosmosTrackingVerbosity.Summarised,
    CurrentTestInfoFetcher = CurrentTestInfo.Fetcher,
    // HttpContextAccessor = ??? — can't set yet
};
builder.WrapHandler(handler => new CosmosTrackingMessageHandler(trackingOptions, handler));
var cosmos = builder.Build();

// Phase 2: After WebApplicationFactory is initialized
trackingOptions.HttpContextAccessor =
    WebFactory.Services.GetRequiredService<IHttpContextAccessor>();

Tip: CosmosTrackingMessageHandlerOptions is a mutable record — setting HttpContextAccessor after construction is the designed pattern for this scenario.

Recommended Verbosity

Use CosmosTrackingVerbosity.Summarised for InMemoryEmulator tests. The Detailed and Raw levels show HTTP bodies that are synthetic (generated by the emulator's handler), not real Cosmos wire protocol. Summarised shows clean operation labels (Read, Create, Query, etc.) without the noise.

Fault Injection Visibility

When using FakeCosmosHandler.FaultInjector to simulate failures (429 Too Many Requests, 503 Service Unavailable), the injected responses are fully visible in diagrams because CosmosTrackingMessageHandler wraps the fake handler — it sees both the outgoing request and the faulted response:

Test -> API: GET /merchants/{id}
API -> CosmosDB: Query: event-stream
API <-- CosmosDB: 429 Too Many Requests
Test <-- API: 500 Internal Server Error

To expose fault injection from a fixture, use InMemoryCosmosResult.GetHandler():

public class CosmosDbFixture
{
    private InMemoryCosmosResult? _cosmos;

    public void SetFaultInjector(Func<HttpRequestMessage, HttpResponseMessage?>? injector)
    {
        // GetHandler() returns the FakeCosmosHandler directly,
        // bypassing any tracking wrapper
        _cosmos?.GetHandler("orders").FaultInjector = injector;
    }
}

Then in tests:

[Fact]
public async Task Returns_500_when_Cosmos_throttles()
{
    _fixture.SetFaultInjector(_ =>
        new HttpResponseMessage((HttpStatusCode)429)
        {
            Content = new StringContent("{}"),
            Headers = { RetryAfter = new RetryConditionHeaderValue(TimeSpan.FromMilliseconds(1)) }
        });

    try
    {
        var response = await Client.GetAsync("/api/orders/123");
        response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
    }
    finally
    {
        _fixture.SetFaultInjector(null); // Always clear in finally
    }
}

Change Feed Correlation

If your application uses a Change Feed Processor, background polling generates tracking entries on threads where TestContext.Current is unavailable. This extension provides ChangeFeedCorrelation to attribute change items back to the originating test using TestCorrelationStore.

When AutoCorrelateWrites is true (the default), every tracked write (Create, Upsert, Replace) populates TestCorrelationStore with the document ID. When the Change Feed processes that document, ChangeFeedCorrelation looks up the test identity from the store.

ChangeFeedCorrelation.Wrap<T>()

Wraps a Change Feed Processor delegate to establish TestIdentityScope for each batch of change items:

var processor = container.GetChangeFeedProcessorBuilder<MyDocument>(
        "processor",
        ChangeFeedCorrelation.Wrap<MyDocument>(
            async (changes, ct) =>
            {
                foreach (var change in changes)
                    await ProcessChange(change);
            },
            serviceName: "CosmosDB",
            idSelector: doc => doc.Id))  // optional — defaults to "id" property
    .Build();

ChangeFeedCorrelation.WrapJson()

For JsonElement-based change feeds:

ChangeFeedCorrelation.WrapJson(
    async (changes, ct) => { /* process changes */ },
    serviceName: "CosmosDB")

See Background Thread Correlation and Parallel-Safe Background Correlation for more details on the correlation system.

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally