Koto.Testing.Integration
Integration-test building blocks: header test auth, Eventually assertions, a raw-JSON Kafka producer.
What's included
| Type | Purpose |
|---|---|
AddHeaderTestAuthentication() / WithTestUser(...) | Replace real auth (JWKS, JWT) with a header-driven scheme |
Eventually.AssertAsync(...) | Polling assertion for asynchronous effects |
RawJsonKafkaProducer | Publish raw-JSON integration contracts from a test |
It has no Testcontainers dependency of its own: container lifecycle stays in your fixture, so these pieces compose with any host layout — single service, multi-service saga, or an in-memory TestServer.
Header test authentication
Replaces the host's real authentication with a scheme driven by the request headers X-Test-UserId / X-Test-Role, so tests never mint real JWTs:
Factory = new WebApplicationFactory<Program>().WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services => services.AddHeaderTestAuthentication());
});
using var client = Factory.CreateClient().WithTestUser(userId); // participant
using var admin = Factory.CreateClient().WithTestUser(adminId, "Admin"); // role check
Eventually
Polling assertion for asynchronous effects — Kafka consumers, projections, sagas. The description lands in the timeout message, so a failure tells you what never happened:
await Eventually.AssertAsync(async () =>
{
var detail = await client.GetFromJsonAsync<JsonElement>($"/meetups/{id}");
return detail.GetProperty("status").GetString() == "Settled";
}, TimeSpan.FromSeconds(45), "meetup becomes Settled");
RawJsonKafkaProducer
Publishes plain web-cased JSON — exactly what PublishIntegrationEvents (Koto.Messaging.Wolverine) produces — so a test can simulate an upstream service or a redelivery. The topic comes from the contract's public const string Topic (the shared convention via IntegrationEventTopics in Koto.Application):
using var kafka = new RawJsonKafkaProducer(kafkaContainer.GetBootstrapAddress());
await kafka.PublishAsync(new UserRegisteredV1(userId, null, "Ivan", ["ru"])); // topic resolved from the type
await kafka.PublishAsync("explicit.topic", payload); // or explicit