89 lines
4.2 KiB
C#
89 lines
4.2 KiB
C#
using System.Net.Http.Json;
|
|
using ItWorx.KnowledgeHub.Contracts.Knowledge;
|
|
using ItWorx.KnowledgeHub.Domain.Automation;
|
|
using ItWorx.KnowledgeHub.Infrastructure.Persistence;
|
|
using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace ItWorx.KnowledgeHub.IntegrationTests.Automation;
|
|
|
|
/// <summary>Every mutation this milestone wires into the outbox must produce a
|
|
/// durable, correctly-typed row in the same transaction as the mutation itself
|
|
/// (docs/24-mcp-readiness.md "Outbox events", M6-A06) — proven here by reading the
|
|
/// table directly, the same "no consumer exists yet, but the record must be there and
|
|
/// correct for one later" contract the outbox is designed around.</summary>
|
|
[Collection(PostgresTestGroup.Name)]
|
|
public sealed class OutboxTests(PostgresContainerFixture postgres) : OwnerSetupTestBase(postgres)
|
|
{
|
|
[Fact]
|
|
public async Task Creating_a_page_records_a_page_created_outbox_event_in_the_same_transaction()
|
|
{
|
|
using var client = Factory.CreateClient();
|
|
await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword);
|
|
|
|
var createResponse = await SetupHelper.PostWithCsrfAsync(
|
|
client, "/api/v1/pages", new CreatePageRequest(null, "Outbox-testpagina", "WikiPage", "Inhoud.", null));
|
|
var page = await createResponse.Content.ReadFromJsonAsync<PageDetail>();
|
|
Assert.NotNull(page);
|
|
|
|
using var scope = Factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<KnowledgeHubDbContext>();
|
|
var messages = await db.OutboxMessages
|
|
.Where(message => message.EventType == "page.created.v1" && message.AggregateId == page.Id.ToString())
|
|
.ToListAsync();
|
|
|
|
var message = Assert.Single(messages);
|
|
Assert.False(message.IsProcessed);
|
|
Assert.Contains(page.Id.ToString(), message.PayloadJson, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Publishing_a_page_edit_records_a_page_updated_outbox_event()
|
|
{
|
|
using var client = Factory.CreateClient();
|
|
await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword);
|
|
|
|
var createResponse = await SetupHelper.PostWithCsrfAsync(
|
|
client, "/api/v1/pages", new CreatePageRequest(null, "Bij te werken pagina", "WikiPage", "Inhoud.", null));
|
|
var page = await createResponse.Content.ReadFromJsonAsync<PageDetail>();
|
|
Assert.NotNull(page);
|
|
|
|
await SetupHelper.PutWithCsrfAsync(
|
|
client, $"/api/v1/pages/{page.Id}",
|
|
new UpdatePageRequest("Bijgewerkte titel", "Nieuwe inhoud", "WikiPage", "Active", null), page.ETag);
|
|
|
|
using var scope = Factory.Services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<KnowledgeHubDbContext>();
|
|
var messages = await db.OutboxMessages
|
|
.Where(message => message.EventType == "page.updated.v1" && message.AggregateId == page.Id.ToString())
|
|
.ToListAsync();
|
|
|
|
Assert.Single(messages);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Unprocessed_outbox_messages_are_queryable_oldest_first_a_future_dispatcher_could_replay_them()
|
|
{
|
|
using var client = Factory.CreateClient();
|
|
await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword);
|
|
|
|
await SetupHelper.PostWithCsrfAsync(
|
|
client, "/api/v1/pages", new CreatePageRequest(null, "Herstel-test pagina 1", "WikiPage", "I.", null));
|
|
await SetupHelper.PostWithCsrfAsync(
|
|
client, "/api/v1/pages", new CreatePageRequest(null, "Herstel-test pagina 2", "WikiPage", "I.", null));
|
|
|
|
using var scope = Factory.Services.CreateScope();
|
|
var outbox = scope.ServiceProvider.GetRequiredService<ItWorx.KnowledgeHub.Application.Automation.IOutboxRepository>();
|
|
var unprocessed = await outbox.GetUnprocessedAsync(500, CancellationToken.None);
|
|
|
|
Assert.True(unprocessed.Count >= 2);
|
|
Assert.All(unprocessed, message => Assert.False(message.IsProcessed));
|
|
// Oldest first: monotonically non-decreasing AvailableAt.
|
|
for (var i = 1; i < unprocessed.Count; i++)
|
|
{
|
|
Assert.True(unprocessed[i].AvailableAt >= unprocessed[i - 1].AvailableAt);
|
|
}
|
|
}
|
|
}
|