Files
register-referentie/services/domain/Big.Tests/Fakes.cs
Niek Otten 9c3da48d8e
All checks were successful
CI / lint (push) Successful in 1m26s
CI / build (push) Successful in 1m19s
CI / unit (push) Successful in 1m14s
CI / frontend (push) Successful in 2m37s
CI / mutation (push) Successful in 5m54s
CI / verify-stack (push) Successful in 7m18s
feat(#13): S-12c-1 — behandel BFF auth + werkbak (ADR-0013) (#85)
## What & why

First half of **S-12c** (behandel-portal backend), per **ADR-0013** (decisions recorded in #84):

- **BFF multi-realm auth.** A second JWT bearer scheme (`medewerker`) alongside the default `digid` scheme. On validation it lifts Keycloak's `realm_access.roles` onto the principal, and a `behandelaar` policy (medewerker scheme + `behandelaar` role) gates `/behandel/*`. Self-service keeps the digid scheme.
- **Werkbak = Flowable tasks.** The domain `Werkbak` query reads the open `Beoordelen` tasks (§8.2, S-12b's `IUserTaskClient`) and enriches each with its aggregate's bsn + status; `GET /behandel/werkbak` (domain) is proxied by the BFF `GET /behandel/werkbak` behind the behandelaar policy. The read projection stays the anonymous openbaar model (no premature `IN_BEHANDELING`/personal-data plumbing — deferred in ADR-0008).

Behavior: `/behandel/werkbak` is **401** without a token, **403** for a medewerker lacking the role, **200 + werkbak** for a behandelaar.

**S-12c-2** (next): `POST /behandel/registrations/{id}/decide` → domain decision + complete the Flowable task.

## Definition of Done

- [x] Linked issue: #13 (umbrella, `refs`); closes the adr-proposal #84
- [x] Tests first; red → green per layer
- [x] Unit + acceptance green (`make unit`): domain 78, bff 23, acceptance 9 (+ acl/event-subscriber unaffected)
- [x] api-client `test` green; openapi.json regenerated (drift guard passes)
- [x] Mutation ≥ break(90): **domain 100%, bff 100%**
- [x] ADR-0013 added; `Keycloak__MedewerkerAuthority` wired into compose
- [ ] CI green (pending)

Part of #13. closes #84

Reviewed-on: #85
2026-07-15 09:54:01 +00:00

97 lines
3.7 KiB
C#

using Big.Application;
using Big.Domain;
namespace Big.Tests;
/// <summary>An in-memory <see cref="IRegistrationStore"/> for the application-layer tests. Upserts
/// keyed on the registration id, mirroring the production store (kept distinct from the production
/// <c>InMemoryRegistrationStore</c> so tests can seed and inspect save counts).</summary>
internal sealed class FakeRegistrationStore : IRegistrationStore
{
private readonly Dictionary<RegistrationId, Registration> _byId = [];
public int SaveCount { get; private set; }
public Task SaveAsync(Registration registration, CancellationToken ct = default)
{
SaveCount++;
_byId[registration.Id] = registration;
return Task.CompletedTask;
}
public Task<Registration?> GetAsync(RegistrationId id, CancellationToken ct = default)
=> Task.FromResult(_byId.GetValueOrDefault(id));
public void Seed(Registration registration) => _byId[registration.Id] = registration;
}
/// <summary>A fake Workflow Client that records the registration it was asked to start a process for
/// and returns a fixed process-instance id. An optional callback runs at start time, letting a test
/// assert ordering (e.g. that the registration was persisted before the process started).</summary>
internal sealed class FakeWorkflowClient(string processInstanceId = "proc-1", Action<RegistrationId>? onStart = null)
: IWorkflowClient
{
public RegistrationId? StartedFor { get; private set; }
public Task<string> StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default)
{
onStart?.Invoke(registrationId);
StartedFor = registrationId;
return Task.FromResult(processInstanceId);
}
}
/// <summary>A fake user-task client for the werkbak/decision use cases: returns a scripted set of
/// open beoordeling tasks and records claim/complete calls.</summary>
internal sealed class FakeUserTaskClient(IReadOnlyList<BeoordelingTask> open) : IUserTaskClient
{
public (string TaskId, string Behandelaar)? Claimed { get; private set; }
public (string TaskId, BeoordelingsBesluit Besluit)? Completed { get; private set; }
public Task<IReadOnlyList<BeoordelingTask>> GetOpenBeoordelingenAsync(CancellationToken ct = default)
=> Task.FromResult(open);
public Task ClaimAsync(string taskId, string behandelaar, CancellationToken ct = default)
{
Claimed = (taskId, behandelaar);
return Task.CompletedTask;
}
public Task CompleteBeoordelingAsync(string taskId, BeoordelingsBesluit besluit, CancellationToken ct = default)
{
Completed = (taskId, besluit);
return Task.CompletedTask;
}
}
/// <summary>A fake ACL client that records the bsn it was asked to open a zaak for and returns a
/// fixed zaak URL.</summary>
internal sealed class FakeAclClient(Uri? zaakUrl = null) : IAclClient
{
public static readonly Uri DefaultZaakUrl = new("http://openzaak/zaken/api/v1/zaken/abc");
private readonly Uri _zaakUrl = zaakUrl ?? DefaultZaakUrl;
public string? OpenedForBsn { get; private set; }
public string? OpenedWithReference { get; private set; }
public int CallCount { get; private set; }
public Uri? ApprovedZaakUrl { get; private set; }
public int ApproveCallCount { get; private set; }
public Task<Uri> OpenZaakAsync(string bsn, string reference, CancellationToken ct = default)
{
CallCount++;
OpenedForBsn = bsn;
OpenedWithReference = reference;
return Task.FromResult(_zaakUrl);
}
public Task ApproveZaakAsync(Uri zaakUrl, CancellationToken ct = default)
{
ApproveCallCount++;
ApprovedZaakUrl = zaakUrl;
return Task.CompletedTask;
}
}