-
Notifications
You must be signed in to change notification settings - Fork 1
Integration S3 Extension
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.
Using a shared library or abstraction layer? If your code doesn't use the S3 SDK directly — e.g. it goes through a shared storage 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>, andMessageTracker.
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.
dotnet add package TestTrackingDiagrams.Extensions.S3The 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.
| 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) |
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.
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.
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 customizationbuilder.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);
});
});| 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 |
SetupVerbosity |
S3TrackingVerbosity? |
null |
Verbosity override for the Setup phase. See Phase-Aware Tracking |
ActionVerbosity |
S3TrackingVerbosity? |
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
|
The following AWS headers are excluded by default (they add noise without diagnostic value):
Authorizationx-amz-datex-amz-security-tokenx-amz-content-sha256User-Agentamz-sdk-invocation-idamz-sdk-request
Override ExcludedHeaders to customise:
new S3TrackingMessageHandlerOptions
{
ExcludedHeaders = ["Authorization", "x-amz-date"] // Only exclude these two
}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);
}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).
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.
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.
Getting Started
Common Tasks
Integration Guides
- Integration xUnit3
- Integration xUnit2
- Integration NUnit
- Integration MSTest
- Integration TUnit
- Integration BDDfy xUnit3
- Integration LightBDD xUnit2
- Integration LightBDD xUnit3
- Integration LightBDD TUnit
- Integration ReqNRoll xUnit2
- Integration ReqNRoll xUnit3
- Integration ReqNRoll TUnit
Extensions
- Integration AtlasDataApi Extension
- Integration BigQuery Extension
- Integration Bigtable Extension
- Integration BlobStorage Extension
- Integration ClickHouse Extension
- Integration CloudStorage Extension
- Integration CosmosDB Extension
- Integration Dapper Extension
- Integration DynamoDB Extension
- Integration EF Core Relational Extension
- Integration Elasticsearch Extension
- Integration EventBridge Extension
- Integration EventHubs Extension
- Integration Grpc Extension
- Integration Kafka Extension
- Integration MassTransit Extension
- Integration MongoDB Extension
- Integration MySqlConnector Extension
- Integration Npgsql Extension
- Integration Oracle Extension
- Integration PubSub Extension
- Integration Redis Extension
- Integration S3 Extension
- Integration ServiceBus Extension
- Integration SNS Extension
- Integration Spanner Extension
- Integration SqlClient Extension
- Integration Sqlite Extension
- Integration SQS Extension
- Integration StorageQueues Extension
- Integration OpenTelemetry Extension
- Integration DispatchProxy Extension
- Integration MediatR Extension
- Integration PlantUML IKVM
Configuration
- Tracking Dependencies
- Tracking Custom Dependencies
- HTTP Tracking Setup
- Report Configuration
- Diagram Customisation
- Phase-Aware Tracking
- Content Formatting
- PlantUML Server Configuration
Features
- Generated Reports
- Search Syntax
- Component Diagrams
- PlantUML Browser Rendering
- Inline SVG Rendering
- Internal Flow Tracking
- Tags and Attributes
- Excluding Requests
- Excluded Headers
- Multi-Host Test Architectures
- Event-Driven Architecture Testing
- Service Bus Tracking Patterns
- Background Thread Correlation
- Parallel-Safe Background Correlation
- Event & Message Tracking
- Assertion Tracking
- Step Tracking
- Tabular Attributes
- Large Response and Diagram Handling
- Diagnostics and Debugging
- CI Summary Integration
- CI Artifact Upload
- Merging Parallel Reports
Reference