fix(test): rewrite SearchControllerIT seed to chunks + fix GET auth scope

Largest Cluster B test: seeded 10 executions via the legacy RouteExecution
shape which ChunkIngestionController silently degenerates to empty chunks,
then verified via a Postgres SELECT against a ClickHouse table. Both
failure modes addressed:
- All 10 seed payloads are now ExecutionChunk envelopes (chunkSeq=0,
  final=true, flat processors[]).
- Pipeline visibility probe is the env-scoped search REST endpoint
  (polling for the last corr-page-10 row).
- searchGet() helper was using the AGENT token; env-scoped read
  endpoints require VIEWER+, so it now uses viewerJwt (matches what
  searchPost already did).

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

View File

@@ -50,22 +50,24 @@ class SearchControllerIT extends AbstractPostgresIT {
// Execution 1: COMPLETED, short duration, no errors
ingest("""
{
"routeId": "search-route-1",
"exchangeId": "ex-search-1",
"applicationId": "test-group",
"instanceId": "test-agent-search-it",
"routeId": "search-route-1",
"correlationId": "corr-alpha",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00Z",
"endTime": "2026-03-10T10:00:00.050Z",
"durationMs": 50,
"errorMessage": "",
"errorStackTrace": "",
"chunkSeq": 0,
"final": true,
"processors": [
{
"seq": 1,
"processorId": "proc-1",
"processorType": "log",
"status": "COMPLETED",
"startTime": "2026-03-10T10:00:00Z",
"endTime": "2026-03-10T10:00:00.050Z",
"durationMs": 50,
"inputBody": "customer-123 order data",
"outputBody": "processed customer-123",
@@ -79,8 +81,10 @@ class SearchControllerIT extends AbstractPostgresIT {
// Execution 2: FAILED with NullPointerException, medium duration
ingest("""
{
"routeId": "search-route-2",
"exchangeId": "ex-search-2",
"applicationId": "test-group",
"instanceId": "test-agent-search-it",
"routeId": "search-route-2",
"correlationId": "corr-beta",
"status": "FAILED",
"startTime": "2026-03-10T12:00:00Z",
@@ -88,6 +92,8 @@ class SearchControllerIT extends AbstractPostgresIT {
"durationMs": 200,
"errorMessage": "NullPointerException in OrderService",
"errorStackTrace": "java.lang.NullPointerException\\n at com.example.OrderService.process(OrderService.java:42)",
"chunkSeq": 0,
"final": true,
"processors": []
}
""");
@@ -95,15 +101,17 @@ class SearchControllerIT extends AbstractPostgresIT {
// Execution 3: RUNNING, long duration, different time window
ingest("""
{
"routeId": "search-route-3",
"exchangeId": "ex-search-3",
"applicationId": "test-group",
"instanceId": "test-agent-search-it",
"routeId": "search-route-3",
"correlationId": "corr-gamma",
"status": "RUNNING",
"startTime": "2026-03-11T08:00:00Z",
"endTime": "2026-03-11T08:00:01Z",
"durationMs": 1000,
"errorMessage": "",
"errorStackTrace": "",
"chunkSeq": 0,
"final": true,
"processors": []
}
""");
@@ -111,8 +119,10 @@ class SearchControllerIT extends AbstractPostgresIT {
// Execution 4: FAILED with MyException in stack trace
ingest("""
{
"routeId": "search-route-4",
"exchangeId": "ex-search-4",
"applicationId": "test-group",
"instanceId": "test-agent-search-it",
"routeId": "search-route-4",
"correlationId": "corr-delta",
"status": "FAILED",
"startTime": "2026-03-10T14:00:00Z",
@@ -120,18 +130,17 @@ class SearchControllerIT extends AbstractPostgresIT {
"durationMs": 300,
"errorMessage": "Processing failed",
"errorStackTrace": "com.example.MyException: something broke\\n at com.example.Handler.handle(Handler.java:10)",
"chunkSeq": 0,
"final": true,
"processors": [
{
"seq": 1,
"processorId": "proc-4",
"processorType": "bean",
"status": "FAILED",
"startTime": "2026-03-10T14:00:00Z",
"endTime": "2026-03-10T14:00:00.300Z",
"durationMs": 300,
"inputBody": "",
"outputBody": "",
"inputHeaders": {"Content-Type": "text/plain"},
"outputHeaders": {}
"inputHeaders": {"Content-Type": "text/plain"}
}
]
}
@@ -141,28 +150,25 @@ class SearchControllerIT extends AbstractPostgresIT {
for (int i = 5; i <= 10; i++) {
ingest(String.format("""
{
"routeId": "search-route-%d",
"exchangeId": "ex-search-%d",
"applicationId": "test-group",
"instanceId": "test-agent-search-it",
"routeId": "search-route-%d",
"correlationId": "corr-page-%d",
"status": "COMPLETED",
"startTime": "2026-03-10T15:00:%02d.000Z",
"endTime": "2026-03-10T15:00:%02d.100Z",
"durationMs": 100,
"errorMessage": "",
"errorStackTrace": "",
"chunkSeq": 0,
"final": true,
"processors": []
}
""", i, i, i, i, i));
}
// Verify all data is in PostgreSQL (synchronous writes)
Integer count = jdbcTemplate.queryForObject(
"SELECT count(*) FROM executions WHERE route_id LIKE 'search-route-%'",
Integer.class);
assertThat(count).isEqualTo(10);
// Wait for async search indexing (debounce + index time)
// Check for last seeded execution specifically to avoid false positives from other test classes
// Wait for async ingestion + search indexing via REST (no raw SQL).
// Probe the last seeded execution to avoid false positives from
// other test classes that may have written into the shared CH tables.
await().atMost(30, SECONDS).untilAsserted(() -> {
ResponseEntity<String> r = searchGet("?correlationId=corr-page-10");
JsonNode body = objectMapper.readTree(r.getBody());
@@ -373,7 +379,9 @@ class SearchControllerIT extends AbstractPostgresIT {
}
private ResponseEntity<String> searchGet(String queryString) {
HttpHeaders headers = securityHelper.authHeadersNoBody(jwt);
// GET /api/v1/environments/*/executions/** requires VIEWER+ — use the
// viewer token, not the agent token (agent would get 403 FORBIDDEN).
HttpHeaders headers = securityHelper.authHeadersNoBody(viewerJwt);
return restTemplate.exchange(
"/api/v1/environments/default/executions" + queryString,
HttpMethod.GET,