diff --git a/services/domain/Big.Tests/FlowableWorkflowClientTests.cs b/services/domain/Big.Tests/FlowableWorkflowClientTests.cs index b67d9d7..9f7b184 100644 --- a/services/domain/Big.Tests/FlowableWorkflowClientTests.cs +++ b/services/domain/Big.Tests/FlowableWorkflowClientTests.cs @@ -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(); + 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); + } }