feat(workflow): document-wait task + 30-day timeout cancellation (S-10a, closes #102) #105
@@ -426,4 +426,106 @@ public class FlowableWorkflowClientTests
|
||||
capture.Seen.RequestUri!.ToString());
|
||||
Assert.Contains("\"workerId\":\"worker-x\"", capture.Body);
|
||||
}
|
||||
|
||||
// ── S-10a (#102): document-wait timeout → RegistratieVerlopen (ADR-0017) ──────────────────────
|
||||
// A 30-day interrupting boundary timer on WachtOpDocumenten spawns a RegistratieVerlopen
|
||||
// external-worker job carrying the registration id; the worker expires the registration and
|
||||
// completes the job. Separately, "documents received" completes the WachtOpDocumenten user task.
|
||||
|
||||
[Fact]
|
||||
public async Task Acquire_verlopen_jobs_posts_the_topic_and_parses_jobs_with_their_registration_id()
|
||||
{
|
||||
var rid = RegistrationId.New();
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.OK,
|
||||
$$"""[{"id":"job-9","variables":[{"name":"registrationId","type":"string","value":"{{rid}}"}]}]"""));
|
||||
|
||||
var jobs = await client.AcquireRegistratieVerlopenJobsAsync(3);
|
||||
|
||||
var job = Assert.Single(jobs);
|
||||
Assert.Equal("job-9", job.JobId);
|
||||
Assert.Equal(rid, job.RegistrationId);
|
||||
Assert.Equal("http://flowable/flowable-rest/external-job-api/acquire/jobs",
|
||||
capture.Seen!.RequestUri!.ToString());
|
||||
Assert.Contains("\"topic\":\"RegistratieVerlopen\"", capture.Body);
|
||||
Assert.Contains("\"numberOfTasks\":3", capture.Body);
|
||||
Assert.Contains("\"workerId\":\"worker-x\"", capture.Body);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("[]")]
|
||||
[InlineData("null")]
|
||||
public async Task Acquire_verlopen_jobs_returns_empty_when_none_are_parked(string body)
|
||||
{
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.OK, body));
|
||||
|
||||
Assert.Empty(await client.AcquireRegistratieVerlopenJobsAsync(1));
|
||||
Assert.NotNull(capture.Seen);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Complete_verlopen_job_posts_to_the_job_complete_endpoint()
|
||||
{
|
||||
var capture = new RequestCapture();
|
||||
var client = Client(capture.Responds(HttpStatusCode.NoContent));
|
||||
|
||||
await client.CompleteRegistratieVerlopenJobAsync("job-9");
|
||||
|
||||
Assert.Equal(HttpMethod.Post, capture.Seen!.Method);
|
||||
Assert.Equal("http://flowable/flowable-rest/external-job-api/acquire/jobs/job-9/complete",
|
||||
capture.Seen.RequestUri!.ToString());
|
||||
Assert.Contains("\"workerId\":\"worker-x\"", capture.Body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Provide_documents_completes_the_wacht_op_documenten_task_in_the_instance()
|
||||
{
|
||||
var requests = new List<(HttpMethod Method, string Url, string? Body)>();
|
||||
var client = Client(new StubHandler(async req =>
|
||||
{
|
||||
requests.Add((req.Method, req.RequestUri!.ToString(),
|
||||
req.Content is null ? null : await req.Content.ReadAsStringAsync()));
|
||||
return req.RequestUri!.AbsoluteUri.EndsWith("service/query/tasks")
|
||||
? new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent("""{"data":[{"id":"task-3"}],"total":1}""",
|
||||
Encoding.UTF8, "application/json"),
|
||||
}
|
||||
: new HttpResponseMessage(HttpStatusCode.OK);
|
||||
}));
|
||||
|
||||
await client.CompleteDocumentWaitAsync("pi-1");
|
||||
|
||||
// 1. Find the still-open WachtOpDocumenten task in this process instance.
|
||||
var query = requests.Single(r => r.Url.EndsWith("service/query/tasks"));
|
||||
Assert.Equal(HttpMethod.Post, query.Method);
|
||||
Assert.Contains("\"processInstanceId\":\"pi-1\"", query.Body);
|
||||
Assert.Contains("\"taskDefinitionKey\":\"WachtOpDocumenten\"", query.Body);
|
||||
// 2. Complete that task so the process leaves the wait state.
|
||||
var complete = requests.Single(r => r.Url.EndsWith("service/runtime/tasks/task-3"));
|
||||
Assert.Equal(HttpMethod.Post, complete.Method);
|
||||
Assert.Contains("\"action\":\"complete\"", complete.Body);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Provide_documents_is_a_no_op_when_the_wait_task_is_no_longer_open()
|
||||
{
|
||||
// The process already left WachtOpDocumenten (e.g. timed out): nothing to complete, no throw.
|
||||
var methods = new List<HttpMethod>();
|
||||
var client = Client(new StubHandler(req =>
|
||||
{
|
||||
methods.Add(req.Method);
|
||||
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent("""{"data":[],"total":0}""", Encoding.UTF8, "application/json"),
|
||||
});
|
||||
}));
|
||||
|
||||
await client.CompleteDocumentWaitAsync("pi-1");
|
||||
|
||||
// Only the query ran; no task-completion POST followed.
|
||||
Assert.DoesNotContain(methods, m => m == HttpMethod.Put || m == HttpMethod.Delete);
|
||||
Assert.Single(methods);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user