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