Skip to content

Integration S3 Extension

Aryeh Citron edited this page Apr 21, 2026 · 10 revisions

The TestTrackingDiagrams.Extensions.S3 package adds Amazon S3 operation tracking to your test diagrams. Instead of showing raw HTTP requests like PUT https://my-bucket.s3.us-east-1.amazonaws.com/path/to/file.json, your sequence diagrams show classified operations like PutObject with a clean s3://my-bucket/path/to/file.json URI.


How It Works

The AWS SDK for .NET translates all S3 operations into HTTP requests to the S3 REST API. S3TrackingMessageHandler is a DelegatingHandler that intercepts these HTTP requests, classifies each one into an S3 operation (PutObject, GetObject, DeleteObject, ListObjectsV2, etc.) using the HTTP method, URL path, query parameters, and headers, then logs it to RequestResponseLogger with a human-readable label.

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

The classifier handles both path-style (s3.region.amazonaws.com/bucket/key) and virtual-hosted-style (bucket.s3.region.amazonaws.com/key) URL formats.


Install

dotnet add package TestTrackingDiagrams.Extensions.S3

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 (PUT, GET, etc.) Full original URI All except excluded set Full content Full content
Detailed Classified operation (PutObject) s3://bucket/key Filtered (noisy AWS headers excluded) Full content Full content
Summarised Classified operation (PutObject) s3://bucket/ None None None

The default is Detailed.

Diagram Label Examples

Operation Raw Detailed Summarised
Upload object PUT: https://...amazonaws.com/bucket/file.json PutObject: s3://bucket/file.json PutObject: s3://bucket/
Download object GET: https://...amazonaws.com/bucket/file.json GetObject: s3://bucket/file.json GetObject: s3://bucket/
Delete object DELETE: https://...amazonaws.com/bucket/file.json DeleteObject: s3://bucket/file.json DeleteObject: s3://bucket/
List objects GET: https://...amazonaws.com/bucket?list-type=2 ListObjectsV2: s3://bucket/ ListObjectsV2: s3://bucket/
Copy object PUT: https://...amazonaws.com/bucket/dest.json CopyObject: s3://bucket/dest.json CopyObject: s3://bucket/
Multipart initiate POST: https://...amazonaws.com/bucket/file.zip?uploads CreateMultipartUpload: s3://bucket/file.zip CreateMultipartUpload: s3://bucket/
Unrecognised PATCH: https://...amazonaws.com/bucket/file Other: s3://bucket/file (skipped)

Classified Operations

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

Operation HTTP Pattern
PutObject PUT /{bucket}/{key} (no copy header, no partNumber or tagging query)
GetObject GET /{bucket}/{key} (no tagging query)
DeleteObject DELETE /{bucket}/{key} (no uploadId or tagging query)
HeadObject HEAD /{bucket}/{key}
CopyObject PUT /{bucket}/{key} with x-amz-copy-source header
DeleteObjects POST /{bucket}?delete
ListObjectsV2 GET /{bucket}?list-type=2
ListBuckets GET / (no bucket)
CreateBucket PUT /{bucket} (no key)
DeleteBucket DELETE /{bucket} (no key)
GetBucketLocation GET /{bucket}?location
ListObjectVersions GET /{bucket}?versions
CreateMultipartUpload POST /{bucket}/{key}?uploads
UploadPart PUT /{bucket}/{key}?partNumber=N&uploadId=X
CompleteMultipartUpload POST /{bucket}/{key}?uploadId=X
AbortMultipartUpload DELETE /{bucket}/{key}?uploadId=X
PutObjectTagging PUT /{bucket}/{key}?tagging
GetObjectTagging GET /{bucket}/{key}?tagging
DeleteObjectTagging DELETE /{bucket}/{key}?tagging
Other Unrecognised or SDK metadata requests

In Summarised mode, Other operations are silently skipped.


Setup

Option A: With AmazonS3Config Extension (Recommended)

var trackingOptions = new S3TrackingMessageHandlerOptions
{
    ServiceName = "S3",
    CallingServiceName = "My API",
    Verbosity = S3TrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = () =>
    {
        var test = TestContext.Current.Test;
        return test is not null
            ? (test.TestDisplayName, test.UniqueID)
            : ("Unknown", "unknown");
    }
};

var config = new AmazonS3Config
{
    RegionEndpoint = RegionEndpoint.USEast1
};
config.WithTestTracking(trackingOptions);

var client = new AmazonS3Client(credentials, config);

WithTestTracking sets the HttpClientFactory property on AmazonS3Config to route all HTTP requests through the tracking handler. This requires no production code changes.

Option B: Manual HttpClient Construction

If you need more control over the HTTP pipeline:

var trackingOptions = new S3TrackingMessageHandlerOptions
{
    ServiceName = "S3",
    CallingServiceName = "My API",
    Verbosity = S3TrackingVerbosity.Detailed,
    CurrentTestInfoFetcher = () =>
    {
        var test = TestContext.Current.Test;
        return test is not null
            ? (test.TestDisplayName, test.UniqueID)
            : ("Unknown", "unknown");
    }
};

var trackingHandler = new S3TrackingMessageHandler(trackingOptions);
// Use trackingHandler with your own HttpClient / AWS pipeline customization

Option C: In DI-based Tests (WebApplicationFactory)

builder.ConfigureTestServices(services =>
{
    services.AddSingleton<IAmazonS3>(sp =>
    {
        var trackingOptions = new S3TrackingMessageHandlerOptions
        {
            ServiceName = "S3",
            CallingServiceName = "My API",
            Verbosity = S3TrackingVerbosity.Detailed,
            CurrentTestInfoFetcher = () =>
            {
                var test = TestContext.Current.Test;
                return test is not null
                    ? (test.TestDisplayName, test.UniqueID)
                    : ("Unknown", "unknown");
            }
        };

        var config = new AmazonS3Config
        {
            RegionEndpoint = RegionEndpoint.USEast1,
            ServiceURL = "http://localhost:4566" // e.g. LocalStack
        };
        config.WithTestTracking(trackingOptions);

        return new AmazonS3Client(new BasicAWSCredentials("test", "test"), config);
    });
});

Configuration Reference

S3TrackingMessageHandlerOptions

Property Type Default Description
ServiceName string "S3" The participant name shown in the diagram for the S3 service
CallingServiceName string "Caller" The participant name shown for the service making S3 calls
Verbosity S3TrackingVerbosity 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)
ExcludedHeaders HashSet<string> See below Headers to exclude from diagrams in Raw/Detailed mode

Default Excluded Headers

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

  • Authorization
  • x-amz-date
  • x-amz-security-token
  • x-amz-content-sha256
  • User-Agent
  • amz-sdk-invocation-id
  • amz-sdk-request

Override ExcludedHeaders to customise:

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

CurrentTestInfoFetcher by Framework

The CurrentTestInfoFetcher delegate must return the current test's name and unique ID. How you obtain this depends on your test framework:

xUnit v3:

CurrentTestInfoFetcher = () =>
{
    var test = TestContext.Current.Test;
    return test is not null
        ? (test.TestDisplayName, test.UniqueID)
        : ("Unknown", "unknown");
}

xUnit v2 (with TestTrackingDiagrams.xUnit2):

using TestTrackingDiagrams.xUnit2;

CurrentTestInfoFetcher = () => XUnit2TestTrackingContext.GetCurrentTestInfo()

NUnit:

CurrentTestInfoFetcher = () =>
{
    var test = TestContext.CurrentContext.Test;
    return (test.FullName, test.ID);
}

Extension Methods

AmazonS3Config.WithTestTracking()

public static AmazonS3Config WithTestTracking(
    this AmazonS3Config config,
    S3TrackingMessageHandlerOptions trackingOptions,
    HttpMessageHandler? innerHandler = null)

Configures AmazonS3Config to use S3TrackingMessageHandler. Sets config.HttpClientFactory to a tracking factory that creates an HttpClient wrapping the tracking handler. The optional innerHandler parameter sets the inner handler (defaults to HttpClientHandler).


URL Format Support

S3 uses two URL styles, and the classifier handles both:

Style URL Format Example
Path-style https://s3.{region}.amazonaws.com/{bucket}/{key} https://s3.us-east-1.amazonaws.com/my-bucket/file.json
Virtual-hosted https://{bucket}.s3.{region}.amazonaws.com/{key} https://my-bucket.s3.us-east-1.amazonaws.com/file.json
Legacy global https://{bucket}.s3.amazonaws.com/{key} https://my-bucket.s3.amazonaws.com/file.json
Legacy dash https://{bucket}.s3-{region}.amazonaws.com/{key} https://my-bucket.s3-us-west-2.amazonaws.com/file.json

Virtual-hosted-style is the default for most regions since 2023. The classifier auto-detects the format from the hostname.


Invocation Validation

S3TrackingMessageHandler 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.

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally