test(acceptance): beoordeling escalation — both branches (S-14, refs #15)
Gherkin scenarios for the two branches: a beoordeling left unclaimed for 14 days escalates to the teamlead, and one completed before the timer fires does not. Driven through the escalation worker against an in-memory Flowable stand-in. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Big.Application;
|
||||
using Big.Domain;
|
||||
using Big.Infrastructure;
|
||||
|
||||
namespace Acceptance.Support;
|
||||
|
||||
@@ -73,6 +74,59 @@ public sealed class InMemoryUserTaskClient : IUserTaskClient
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>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.</summary>
|
||||
public sealed class InMemoryEscalatieClient : IBeoordelingEscalatieClient
|
||||
{
|
||||
private sealed class ParkedTask
|
||||
{
|
||||
public string CandidateGroup { get; set; } = "behandelaar";
|
||||
public bool IsOpen { get; set; } = true;
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, ParkedTask> _tasks = [];
|
||||
private readonly List<EscalatieJob> _parked = [];
|
||||
private int _seq;
|
||||
|
||||
/// <summary>A registration parks at Beoordelen, claimable by the behandelaar group.</summary>
|
||||
public string ParkBeoordeling()
|
||||
{
|
||||
var pid = $"pi-{++_seq}";
|
||||
_tasks[pid] = new ParkedTask();
|
||||
return pid;
|
||||
}
|
||||
|
||||
/// <summary>The behandelaar completes the beoordeling before the timer fires: the task closes.</summary>
|
||||
public void CompleteBeoordeling(string processInstanceId) => _tasks[processInstanceId].IsOpen = false;
|
||||
|
||||
/// <summary>The 14-day boundary timer fires: a non-interrupting token parks an escalation job.</summary>
|
||||
public void FireEscalationTimer(string processInstanceId)
|
||||
=> _parked.Add(new EscalatieJob($"job-{++_seq}", processInstanceId));
|
||||
|
||||
/// <summary>The candidate group that may currently pick the task up.</summary>
|
||||
public string CandidateGroupFor(string processInstanceId) => _tasks[processInstanceId].CandidateGroup;
|
||||
|
||||
public Task<IReadOnlyList<EscalatieJob>> AcquireBeoordelingEscalatieJobsAsync(int maxJobs, CancellationToken ct = default)
|
||||
=> Task.FromResult<IReadOnlyList<EscalatieJob>>(_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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>An in-memory registration store for the domain acceptance scenario.</summary>
|
||||
public sealed class InMemoryRegistrationStore : IRegistrationStore
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user