Docs

Koto.Testing.Integration

Integration-test building blocks: header test auth, Eventually assertions, a raw-JSON Kafka producer.

What's included

TypePurpose
AddHeaderTestAuthentication() / WithTestUser(...)Replace real auth (JWKS, JWT) with a header-driven scheme
Eventually.AssertAsync(...)Polling assertion for asynchronous effects
RawJsonKafkaProducerPublish 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