test(infra): Flowable client acquires/completes RegistratieVerlopen + completes wait task (refs #102)
RED: the Workflow Client posts the RegistratieVerlopen topic and parses the correlated registration id, completes the expiry job, and (documents-in-time) completes the WachtOpDocumenten user task in the instance — best-effort no-op if that task is no longer open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -426,4 +426,106 @@ public class FlowableWorkflowClientTests
|
|||||||
capture.Seen.RequestUri!.ToString());
|
capture.Seen.RequestUri!.ToString());
|
||||||
Assert.Contains("\"workerId\":\"worker-x\"", capture.Body);
|
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