fix(test): rewrite DetailControllerIT seed to ExecutionChunk + REST-driven lookup

POST /api/v1/data/executions is owned by ChunkIngestionController (the
legacy ExecutionController path is @ConditionalOnMissingBean(ChunkAccumulator)
and never binds). The old RouteExecution-shaped seed was silently parsed
as an empty ExecutionChunk and nothing landed in ClickHouse.

Rewrote the seed as a single final ExecutionChunk with chunkSeq=0 /
final=true and a flat processors[] carrying seq + parentSeq to preserve
the 3-level tree (DetailService.buildTree reconstructs the nested shape
for the API response). Execution-id lookup now goes through the search
REST API filtered by correlationId, per the no-raw-SQL preference.

Template for the other Cluster B ITs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-21 22:04:00 +02:00
parent 36571013c1
commit dfacedb0ca

View File

@@ -40,6 +40,12 @@ class DetailControllerIT extends AbstractPostgresIT {
/**
* Seed a route execution with a 3-level processor tree:
* root -> [child1, child2], child2 -> [grandchild]
*
* Uses the chunked ingestion envelope (POST /api/v1/data/executions →
* ChunkIngestionController), which is the only active ingestion path.
* The processor tree is flattened into FlatProcessorRecord[] with
* seq / parentSeq; DetailService.buildTree reconstructs the nested
* shape for the API response.
*/
@BeforeAll
void seedTestData() {
@@ -48,67 +54,66 @@ class DetailControllerIT extends AbstractPostgresIT {
String json = """
{
"routeId": "detail-test-route",
"exchangeId": "detail-ex-1",
"applicationId": "test-group",
"instanceId": "test-agent-detail-it",
"routeId": "detail-test-route",
"correlationId": "detail-corr-1",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00Z",
"endTime": "2026-03-10T10:00:01Z",
"durationMs": 1000,
"errorMessage": "",
"errorStackTrace": "",
"chunkSeq": 0,
"final": true,
"processors": [
{
"seq": 1,
"processorId": "root-proc",
"processorType": "split",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00Z",
"endTime": "2026-03-10T10:00:01Z",
"durationMs": 1000,
"inputBody": "root-input-body",
"outputBody": "root-output-body",
"inputHeaders": {"Content-Type": "application/json"},
"outputHeaders": {"X-Result": "ok"},
"children": [
{
"processorId": "child1-proc",
"processorType": "log",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00.100Z",
"endTime": "2026-03-10T10:00:00.200Z",
"durationMs": 100,
"inputBody": "child1-input",
"outputBody": "child1-output",
"inputHeaders": {},
"outputHeaders": {}
},
{
"processorId": "child2-proc",
"processorType": "bean",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00.200Z",
"endTime": "2026-03-10T10:00:00.800Z",
"durationMs": 600,
"inputBody": "child2-input",
"outputBody": "child2-output",
"inputHeaders": {},
"outputHeaders": {},
"children": [
{
"processorId": "grandchild-proc",
"processorType": "to",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00.300Z",
"endTime": "2026-03-10T10:00:00.700Z",
"durationMs": 400,
"inputBody": "gc-input",
"outputBody": "gc-output",
"inputHeaders": {"X-GC": "true"},
"outputHeaders": {}
}
]
}
]
"outputHeaders": {"X-Result": "ok"}
},
{
"seq": 2,
"parentSeq": 1,
"parentProcessorId": "root-proc",
"processorId": "child1-proc",
"processorType": "log",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00.100Z",
"durationMs": 100,
"inputBody": "child1-input",
"outputBody": "child1-output"
},
{
"seq": 3,
"parentSeq": 1,
"parentProcessorId": "root-proc",
"processorId": "child2-proc",
"processorType": "bean",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00.200Z",
"durationMs": 600,
"inputBody": "child2-input",
"outputBody": "child2-output"
},
{
"seq": 4,
"parentSeq": 3,
"parentProcessorId": "child2-proc",
"processorId": "grandchild-proc",
"processorType": "to",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00.300Z",
"durationMs": 400,
"inputBody": "gc-input",
"outputBody": "gc-output",
"inputHeaders": {"X-GC": "true"}
}
]
}
@@ -116,17 +121,21 @@ class DetailControllerIT extends AbstractPostgresIT {
ingest(json);
// Wait for flush and get the execution_id
await().atMost(10, SECONDS).untilAsserted(() -> {
Integer count = jdbcTemplate.queryForObject(
"SELECT count(*) FROM executions WHERE route_id = 'detail-test-route'",
Integer.class);
assertThat(count).isGreaterThanOrEqualTo(1);
// Wait for async ingestion + flush, then pull the CH-assigned execution_id
// back through the REST search API. Executions live in ClickHouse; always
// drive CH assertions through REST so the test still covers the full
// controller→service→store wiring.
await().atMost(20, SECONDS).untilAsserted(() -> {
ResponseEntity<String> r = restTemplate.exchange(
"/api/v1/environments/default/executions?correlationId=detail-corr-1",
HttpMethod.GET,
new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
String.class);
assertThat(r.getStatusCode()).isEqualTo(HttpStatus.OK);
JsonNode body = objectMapper.readTree(r.getBody());
assertThat(body.get("total").asLong()).isGreaterThanOrEqualTo(1);
seededExecutionId = body.get("data").get(0).get("executionId").asText();
});
seededExecutionId = jdbcTemplate.queryForObject(
"SELECT execution_id FROM executions WHERE route_id = 'detail-test-route' LIMIT 1",
String.class);
}
@Test