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: * Seed a route execution with a 3-level processor tree:
* root -> [child1, child2], child2 -> [grandchild] * 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 @BeforeAll
void seedTestData() { void seedTestData() {
@@ -48,67 +54,66 @@ class DetailControllerIT extends AbstractPostgresIT {
String json = """ String json = """
{ {
"routeId": "detail-test-route",
"exchangeId": "detail-ex-1", "exchangeId": "detail-ex-1",
"applicationId": "test-group",
"instanceId": "test-agent-detail-it",
"routeId": "detail-test-route",
"correlationId": "detail-corr-1", "correlationId": "detail-corr-1",
"status": "COMPLETED", "status": "COMPLETED",
"startTime": "2026-03-10T10:00:00Z", "startTime": "2026-03-10T10:00:00Z",
"endTime": "2026-03-10T10:00:01Z", "endTime": "2026-03-10T10:00:01Z",
"durationMs": 1000, "durationMs": 1000,
"errorMessage": "", "chunkSeq": 0,
"errorStackTrace": "", "final": true,
"processors": [ "processors": [
{ {
"seq": 1,
"processorId": "root-proc", "processorId": "root-proc",
"processorType": "split", "processorType": "split",
"status": "COMPLETED", "status": "COMPLETED",
"startTime": "2026-03-10T10:00:00Z", "startTime": "2026-03-10T10:00:00Z",
"endTime": "2026-03-10T10:00:01Z",
"durationMs": 1000, "durationMs": 1000,
"inputBody": "root-input-body", "inputBody": "root-input-body",
"outputBody": "root-output-body", "outputBody": "root-output-body",
"inputHeaders": {"Content-Type": "application/json"}, "inputHeaders": {"Content-Type": "application/json"},
"outputHeaders": {"X-Result": "ok"}, "outputHeaders": {"X-Result": "ok"}
"children": [ },
{ {
"processorId": "child1-proc", "seq": 2,
"processorType": "log", "parentSeq": 1,
"status": "COMPLETED", "parentProcessorId": "root-proc",
"startTime": "2026-03-10T10:00:00.100Z", "processorId": "child1-proc",
"endTime": "2026-03-10T10:00:00.200Z", "processorType": "log",
"durationMs": 100, "status": "COMPLETED",
"inputBody": "child1-input", "startTime": "2026-03-10T10:00:00.100Z",
"outputBody": "child1-output", "durationMs": 100,
"inputHeaders": {}, "inputBody": "child1-input",
"outputHeaders": {} "outputBody": "child1-output"
}, },
{ {
"processorId": "child2-proc", "seq": 3,
"processorType": "bean", "parentSeq": 1,
"status": "COMPLETED", "parentProcessorId": "root-proc",
"startTime": "2026-03-10T10:00:00.200Z", "processorId": "child2-proc",
"endTime": "2026-03-10T10:00:00.800Z", "processorType": "bean",
"durationMs": 600, "status": "COMPLETED",
"inputBody": "child2-input", "startTime": "2026-03-10T10:00:00.200Z",
"outputBody": "child2-output", "durationMs": 600,
"inputHeaders": {}, "inputBody": "child2-input",
"outputHeaders": {}, "outputBody": "child2-output"
"children": [ },
{ {
"processorId": "grandchild-proc", "seq": 4,
"processorType": "to", "parentSeq": 3,
"status": "COMPLETED", "parentProcessorId": "child2-proc",
"startTime": "2026-03-10T10:00:00.300Z", "processorId": "grandchild-proc",
"endTime": "2026-03-10T10:00:00.700Z", "processorType": "to",
"durationMs": 400, "status": "COMPLETED",
"inputBody": "gc-input", "startTime": "2026-03-10T10:00:00.300Z",
"outputBody": "gc-output", "durationMs": 400,
"inputHeaders": {"X-GC": "true"}, "inputBody": "gc-input",
"outputHeaders": {} "outputBody": "gc-output",
} "inputHeaders": {"X-GC": "true"}
]
}
]
} }
] ]
} }
@@ -116,17 +121,21 @@ class DetailControllerIT extends AbstractPostgresIT {
ingest(json); ingest(json);
// Wait for flush and get the execution_id // Wait for async ingestion + flush, then pull the CH-assigned execution_id
await().atMost(10, SECONDS).untilAsserted(() -> { // back through the REST search API. Executions live in ClickHouse; always
Integer count = jdbcTemplate.queryForObject( // drive CH assertions through REST so the test still covers the full
"SELECT count(*) FROM executions WHERE route_id = 'detail-test-route'", // controller→service→store wiring.
Integer.class); await().atMost(20, SECONDS).untilAsserted(() -> {
assertThat(count).isGreaterThanOrEqualTo(1); 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 @Test