test(backend): type-state Aanvraag builder, illegal fixtures unrepresentable (WP-70)

RuleTests/OpenZaakZaakSourceTests hand-built Aanvraag fixtures by initializer,
keeping Submitted/Referentie/SubmittedAt/BesluitStatus consistent by hand. A
type-state builder (Given.Concept().Submitted().Decided()) makes an illegal
sequence a compile error instead, and delegates the toelichting-required rule
to the real BeoordelingRules so it can't drift from production. Adds
BesluitLifecycleTests covering the WP-68 besluit invariants end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-08-18 15:30:57 +02:00
co-authored by Claude Sonnet 5
parent 868fb55783
commit 2eea860efe
4 changed files with 293 additions and 47 deletions
+8 -19
View File
@@ -1,10 +1,10 @@
using BigRegister.Api.Data;
using BigRegister.Domain.Applications;
using BigRegister.Domain.Beoordeling;
using BigRegister.Domain.Diplomas;
using BigRegister.Domain.Documents;
using BigRegister.Domain.Registrations;
using BigRegister.Domain.Submissions;
using BigRegister.Tests.Builders;
namespace BigRegister.Tests;
@@ -207,35 +207,24 @@ public class BeoordelingRuleTests
// StatusAt, and CanDecide refuses a further besluit regardless of which one. Pins the
// domain statement "Afgewezen/Goedgekeurd → no further besluit" independent of the
// endpoint's own (integration-level) Already_decided_case_rejects_a_further_besluit.
private static Aanvraag Decided(Besluit besluit) => new()
{
Id = "x",
Type = "registratie",
Owner = "test",
Submitted = true,
Referentie = "BIG-2026-1",
SubmittedAt = DateTimeOffset.UtcNow,
CreatedAt = DateTimeOffset.UtcNow,
UpdatedAt = DateTimeOffset.UtcNow,
BesluitStatus = besluit,
BesluitToelichting = besluit == Besluit.Goedkeuren ? null : "toelichting",
};
// WP-70: built via Given, not a hand-rolled Aanvraag literal — Decided(Besluit.Afwijzen) with
// no toelichting simply couldn't compile as a fixture here.
[Theory]
[InlineData(Besluit.Goedkeuren)]
[InlineData(Besluit.Afwijzen)]
public void A_terminal_decision_refuses_any_further_besluit(Besluit recorded)
{
var now = DateTimeOffset.UtcNow;
var tag = Decided(recorded).StatusAt(now).Tag!.Value;
Assert.False(BeoordelingRules.CanDecide(tag));
var toelichting = recorded == Besluit.Goedkeuren ? null : "toelichting";
var aanvraag = Given.Concept(owner: "test").Submitted().Decided(recorded, toelichting).Build();
Assert.False(BeoordelingRules.CanDecide(aanvraag.StatusAt(now).Tag!.Value));
}
[Fact]
public void MeerInfoOpvragen_is_not_terminal_a_further_besluit_is_still_legal()
{
var now = DateTimeOffset.UtcNow;
var tag = Decided(Besluit.MeerInfoOpvragen).StatusAt(now).Tag!.Value;
Assert.True(BeoordelingRules.CanDecide(tag));
var aanvraag = Given.Concept(owner: "test").Submitted().Decided(Besluit.MeerInfoOpvragen, "toelichting").Build();
Assert.True(BeoordelingRules.CanDecide(aanvraag.StatusAt(now).Tag!.Value));
}
}