using Big.Application;
using Big.Domain;
using Big.Infrastructure;
namespace Acceptance.Support;
/// An in-memory Workflow Client for the domain acceptance scenario: it records which
/// registration a process was started for and returns a fixed instance id. The acceptance test
/// drives the use case without a running Flowable (live verification is the verify-domain check).
public sealed class InMemoryWorkflowClient : IWorkflowClient
{
public const string StartedProcessInstanceId = "proc-acc-1";
public RegistrationId? StartedFor { get; private set; }
public string? WithdrawnProcessInstanceId { get; private set; }
public Task StartRegistrationProcessAsync(RegistrationId registrationId, CancellationToken ct = default)
{
StartedFor = registrationId;
return Task.FromResult(StartedProcessInstanceId);
}
public Task WithdrawProcessAsync(string processInstanceId, CancellationToken ct = default)
{
WithdrawnProcessInstanceId = processInstanceId;
return Task.CompletedTask;
}
}
/// An in-memory ACL stand-in: records the bsn it opened a zaak for and returns a fixed URL,
/// so the scenario verifies the domain crosses the ACL boundary (§8.1) without a running OpenZaak.
public sealed class InMemoryAclClient : IAclClient
{
public static readonly Uri OpenedZaakUrl = new("http://openzaak/zaken/api/v1/zaken/acc-zaak");
public string? OpenedForBsn { get; private set; }
public string? OpenedWithReference { get; private set; }
public Uri? ApprovedZaakUrl { get; private set; }
public Task OpenZaakAsync(string bsn, string reference, CancellationToken ct = default)
{
OpenedForBsn = bsn;
OpenedWithReference = reference;
return Task.FromResult(OpenedZaakUrl);
}
public Task ApproveZaakAsync(Uri zaakUrl, CancellationToken ct = default)
{
ApprovedZaakUrl = zaakUrl;
return Task.CompletedTask;
}
}
/// An in-memory user-task client for the beoordeling acceptance scenario: it holds one open
/// Beoordelen task per registration and records the besluit each is completed with.
public sealed class InMemoryUserTaskClient : IUserTaskClient
{
private readonly List _open = [];
public (string TaskId, BeoordelingsBesluit Besluit)? Completed { get; private set; }
public void Open(RegistrationId registrationId) => _open.Add(new BeoordelingTask($"task-{registrationId}", registrationId));
public Task> GetOpenBeoordelingenAsync(CancellationToken ct = default)
=> Task.FromResult>(_open);
public Task ClaimAsync(string taskId, string behandelaar, CancellationToken ct = default) => Task.CompletedTask;
public Task CompleteBeoordelingAsync(string taskId, BeoordelingsBesluit besluit, CancellationToken ct = default)
{
Completed = (taskId, besluit);
_open.RemoveAll(t => t.TaskId == taskId);
return Task.CompletedTask;
}
}
/// An in-memory Flowable stand-in for the beoordeling-escalation scenario (S-14): it models
/// one Beoordelen task per process instance — its candidate group and whether it is still open — and
/// the escalation jobs the non-interrupting 14-day boundary timer parks. It drives the escalation
/// worker's behaviour without a running Flowable; the timer firing live is the verify-domain check.
public sealed class InMemoryEscalatieClient : IBeoordelingEscalatieClient
{
private sealed class ParkedTask
{
public string CandidateGroup { get; set; } = "behandelaar";
public bool IsOpen { get; set; } = true;
}
private readonly Dictionary _tasks = [];
private readonly List _parked = [];
private int _seq;
/// A registration parks at Beoordelen, claimable by the behandelaar group.
public string ParkBeoordeling()
{
var pid = $"pi-{++_seq}";
_tasks[pid] = new ParkedTask();
return pid;
}
/// The behandelaar completes the beoordeling before the timer fires: the task closes.
public void CompleteBeoordeling(string processInstanceId) => _tasks[processInstanceId].IsOpen = false;
/// The 14-day boundary timer fires: a non-interrupting token parks an escalation job.
public void FireEscalationTimer(string processInstanceId)
=> _parked.Add(new EscalatieJob($"job-{++_seq}", processInstanceId));
/// The candidate group that may currently pick the task up.
public string CandidateGroupFor(string processInstanceId) => _tasks[processInstanceId].CandidateGroup;
public Task> AcquireBeoordelingEscalatieJobsAsync(int maxJobs, CancellationToken ct = default)
=> Task.FromResult>(_parked.Take(maxJobs).ToList());
public Task ReassignBeoordelingToTeamleadAsync(string processInstanceId, CancellationToken ct = default)
{
// No-op if the behandelaar already completed it — the timer/completion race (§8.6).
var task = _tasks[processInstanceId];
if (task.IsOpen)
task.CandidateGroup = "teamlead";
return Task.CompletedTask;
}
public Task CompleteBeoordelingEscalatieJobAsync(string jobId, CancellationToken ct = default)
{
_parked.RemoveAll(j => j.JobId == jobId);
return Task.CompletedTask;
}
}
/// An in-memory registration store for the domain acceptance scenario.
public sealed class InMemoryRegistrationStore : IRegistrationStore
{
private readonly Dictionary _byId = [];
public Task SaveAsync(Registration registration, CancellationToken ct = default)
{
_byId[registration.Id] = registration;
return Task.CompletedTask;
}
public Task GetAsync(RegistrationId id, CancellationToken ct = default)
=> Task.FromResult(_byId.GetValueOrDefault(id));
}