Files
register-referentie/services/domain/Big.Tests/RegistrationTests.cs
Niek Otten 1c185e6686
All checks were successful
CI / lint (push) Successful in 1m25s
CI / build (push) Successful in 1m13s
CI / unit (push) Successful in 1m26s
CI / frontend (push) Successful in 2m35s
CI / mutation (push) Successful in 6m6s
CI / verify-stack (push) Successful in 7m34s
S-09b: Approval flow — temp admin endpoint + status transition to projection (#77)
## What & why

S-09b (#75, split from #10) — the **approval flow** that completes the walking skeleton. A behandelaar can now approve a submitted registration; the entry flips from `INGEDIEND` to `INGESCHREVEN` in the public register. Flow: `POST /registrations/{id}/approve` (domain) → ACL sets the zaak eindstatus (ZGW `/statussen`) → OpenZaak → NRC → event-subscriber → projection → openbaar.

## Changes (bottom-up, each red→green TDD)

- **Domain** — `RegistrationStatus.Ingeschreven` + `Registration.Approve()` (guards: opened zaak, only from INGEDIEND); `ApproveRegistration` use case (idempotent) + temp `POST /registrations/{id}/approve` endpoint; `IAclClient.ApproveZaakAsync`.
- **ACL** — resolves the zaaktype's **eindstatus** from the catalogus (`isEindstatus` / highest volgnummer) and POSTs a ZGW status; exposed as `POST /statussen`. Unit + real-OpenZaak integration test.
- **Event-subscriber** — binds NRC `hoofdObject`, projects a `status`/`create` as `INGESCHREVEN` keyed on the zaak (updates the existing row), **without reading OpenZaak** (§8.1). Retains the ZGW `resource` in the log (new column + EF migration) so a rebuild reproduces the status.
- **e2e** — extended: submit → public INGEDIEND → approve → public INGESCHREVEN.
- **Docs** — ADR-0011 (the two non-obvious decisions + the walking-skeleton assumption) + demo note.

## Key decisions (see ADR-0011)

- **ACL discovers the eindstatus** (chosen over injecting a statustype URL): no new config/seed plumbing, domain stays ZGW-ignorant.
- **Any post-creation status-set ⇒ INGESCHREVEN**: in the walking skeleton the only status ever set after creation is the approval, and the subscriber may not read ZGW — documented to tighten when more transitions arrive (S-12+).

## Verification

- All .NET unit suites green locally (domain 47, acl 11, event-subscriber 14, bff 16, acceptance 7); Release build + `dotnet format` clean.
- No new compose config (the eindstatus-discovery approach avoided it).
- The real-OpenZaak integration test (ACL status-set) and the full submit→approve→visible e2e run in CI `verify-stack` (live NRC→projection + selectielijst egress, not reproducible locally).

closes #75

Reviewed-on: #77
2026-07-14 09:04:57 +00:00

125 lines
4.0 KiB
C#

using Big.Domain;
namespace Big.Tests;
public class RegistrationTests
{
[Fact]
public void Submitting_a_registration_starts_in_ingediend()
{
var registration = Registration.Submit("123456782");
Assert.Equal(RegistrationStatus.Ingediend, registration.Status);
Assert.Equal("123456782", registration.Bsn);
Assert.NotEqual(Guid.Empty, registration.Id.Value);
Assert.Null(registration.ZaakUrl);
Assert.Null(registration.ProcessInstanceId);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null)]
public void Submitting_without_a_bsn_is_rejected(string? bsn)
=> Assert.ThrowsAny<ArgumentException>(() => Registration.Submit(bsn!));
[Fact]
public void Recording_the_started_process_keeps_its_instance_id()
{
var registration = Registration.Submit("123456782");
registration.RecordProcessStarted("proc-42");
Assert.Equal("proc-42", registration.ProcessInstanceId);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData(null)]
public void Recording_an_empty_process_instance_id_is_rejected(string? processInstanceId)
{
var registration = Registration.Submit("123456782");
Assert.ThrowsAny<ArgumentException>(() => registration.RecordProcessStarted(processInstanceId!));
}
[Fact]
public void Attaching_a_zaak_records_its_url_and_keeps_the_status_ingediend()
{
var registration = Registration.Submit("123456782");
var zaak = new Uri("http://openzaak/zaken/api/v1/zaken/abc");
registration.AttachZaak(zaak);
Assert.Equal(zaak, registration.ZaakUrl);
Assert.Equal(RegistrationStatus.Ingediend, registration.Status);
}
[Fact]
public void Attaching_a_null_zaak_is_rejected()
{
var registration = Registration.Submit("123456782");
Assert.Throws<ArgumentNullException>(() => registration.AttachZaak(null!));
}
[Fact]
public void Re_attaching_the_same_zaak_is_idempotent()
{
var registration = Registration.Submit("123456782");
var zaak = new Uri("http://openzaak/zaken/api/v1/zaken/abc");
registration.AttachZaak(zaak);
registration.AttachZaak(zaak);
Assert.Equal(zaak, registration.ZaakUrl);
}
[Fact]
public void Attaching_a_conflicting_zaak_is_rejected()
{
var registration = Registration.Submit("123456782");
registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
var ex = Assert.Throws<InvalidOperationException>(
() => registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/other")));
Assert.Contains("/zaken/abc", ex.Message);
Assert.Contains("/zaken/other", ex.Message);
}
[Fact]
public void Approving_a_registration_with_a_zaak_sets_it_ingeschreven()
{
var registration = Registration.Submit("123456782");
registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
registration.Approve();
Assert.Equal(RegistrationStatus.Ingeschreven, registration.Status);
}
[Fact]
public void Approving_before_the_zaak_is_opened_is_rejected()
{
var registration = Registration.Submit("123456782");
var ex = Assert.Throws<InvalidOperationException>(() => registration.Approve());
Assert.Contains("no zaak", ex.Message, StringComparison.OrdinalIgnoreCase);
Assert.Equal(RegistrationStatus.Ingediend, registration.Status);
}
[Fact]
public void Approving_an_already_ingeschreven_registration_is_rejected()
{
var registration = Registration.Submit("123456782");
registration.AttachZaak(new Uri("http://openzaak/zaken/api/v1/zaken/abc"));
registration.Approve();
var ex = Assert.Throws<InvalidOperationException>(() => registration.Approve());
Assert.Contains("only an INGEDIEND", ex.Message);
Assert.Equal(RegistrationStatus.Ingeschreven, registration.Status);
}
}