diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java index b5ee8136..5b1f3827 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramControllerIT.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -24,11 +25,13 @@ class DiagramControllerIT extends AbstractPostgresIT { private TestSecurityHelper securityHelper; private HttpHeaders authHeaders; + private HttpHeaders viewerHeaders; @BeforeEach void setUp() { String jwt = securityHelper.registerTestAgent("test-agent-diagram-it"); authHeaders = securityHelper.authHeaders(jwt); + viewerHeaders = securityHelper.authHeadersNoBody(securityHelper.viewerToken()); } @Test @@ -68,11 +71,15 @@ class DiagramControllerIT extends AbstractPostgresIT { new HttpEntity<>(json, authHeaders), String.class); - await().atMost(10, SECONDS).untilAsserted(() -> { - Integer count = jdbcTemplate.queryForObject( - "SELECT count(*) FROM route_diagrams WHERE route_id = 'diagram-flush-route'", - Integer.class); - assertThat(count).isGreaterThanOrEqualTo(1); + // route_diagrams lives in ClickHouse; drive the visibility check + // through the env-scoped diagram-render endpoint, never raw SQL. + await().atMost(15, SECONDS).untilAsserted(() -> { + ResponseEntity r = restTemplate.exchange( + "/api/v1/environments/default/apps/test-group/routes/diagram-flush-route/diagram", + HttpMethod.GET, + new HttpEntity<>(viewerHeaders), + String.class); + assertThat(r.getStatusCode()).isEqualTo(HttpStatus.OK); }); } diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java index 814563dd..db154a39 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/controller/DiagramRenderControllerIT.java @@ -2,6 +2,8 @@ package com.cameleer.server.app.controller; import com.cameleer.server.app.AbstractPostgresIT; import com.cameleer.server.app.TestSecurityHelper; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -17,8 +19,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; /** - * Integration tests for {@link DiagramRenderController}. - * Seeds a diagram via the ingestion endpoint, then tests rendering. + * Integration tests for {@link DiagramRenderController}. The env-scoped + * endpoint only serves JSON — SVG rendering is only available via the + * flat content-hash endpoint. We seed the diagram plus an execution for + * the same route, then pull the content hash from the execution-detail + * REST response to drive the flat-endpoint render tests. */ class DiagramRenderControllerIT extends AbstractPostgresIT { @@ -28,19 +33,18 @@ class DiagramRenderControllerIT extends AbstractPostgresIT { @Autowired private TestSecurityHelper securityHelper; + private final ObjectMapper objectMapper = new ObjectMapper(); + private String jwt; private String viewerJwt; private String contentHash; - /** - * Seed a diagram and compute its content hash for render tests. - */ @BeforeEach void seedDiagram() { jwt = securityHelper.registerTestAgent("test-agent-diagram-render-it"); viewerJwt = securityHelper.viewerToken(); - String json = """ + String diagramJson = """ { "routeId": "render-test-route", "description": "Render test", @@ -56,18 +60,57 @@ class DiagramRenderControllerIT extends AbstractPostgresIT { ] } """; - restTemplate.postForEntity( "/api/v1/data/diagrams", - new HttpEntity<>(json, securityHelper.authHeaders(jwt)), + new HttpEntity<>(diagramJson, securityHelper.authHeaders(jwt)), String.class); - // Wait for flush to storage and retrieve the content hash - await().atMost(10, SECONDS).untilAsserted(() -> { - String hash = jdbcTemplate.queryForObject( - "SELECT content_hash FROM route_diagrams WHERE route_id = 'render-test-route' LIMIT 1", + // Post an execution for the same route so the ingestion pipeline + // stamps diagramContentHash on it — that's our path to fetching the + // hash without reading route_diagrams directly. + String execJson = """ + { + "exchangeId": "render-probe-exchange", + "applicationId": "test-group", + "instanceId": "test-agent-diagram-render-it", + "routeId": "render-test-route", + "correlationId": "render-probe-corr", + "status": "COMPLETED", + "startTime": "2026-03-11T10:00:00Z", + "endTime": "2026-03-11T10:00:01Z", + "durationMs": 1000, + "chunkSeq": 0, + "final": true, + "processors": [] + } + """; + restTemplate.postForEntity( + "/api/v1/data/executions", + new HttpEntity<>(execJson, securityHelper.authHeaders(jwt)), + String.class); + + // Wait for both to land, then read the hash off the execution detail. + await().atMost(20, SECONDS).untilAsserted(() -> { + HttpHeaders headers = securityHelper.authHeadersNoBody(viewerJwt); + ResponseEntity search = restTemplate.exchange( + "/api/v1/environments/default/executions?correlationId=render-probe-corr", + HttpMethod.GET, + new HttpEntity<>(headers), String.class); - assertThat(hash).isNotNull(); + assertThat(search.getStatusCode()).isEqualTo(HttpStatus.OK); + JsonNode body = objectMapper.readTree(search.getBody()); + assertThat(body.get("total").asLong()).isGreaterThanOrEqualTo(1); + String execId = body.get("data").get(0).get("executionId").asText(); + + ResponseEntity detail = restTemplate.exchange( + "/api/v1/executions/" + execId, + HttpMethod.GET, + new HttpEntity<>(headers), + String.class); + assertThat(detail.getStatusCode()).isEqualTo(HttpStatus.OK); + JsonNode detailBody = objectMapper.readTree(detail.getBody()); + String hash = detailBody.path("diagramContentHash").asText(); + assertThat(hash).isNotEmpty(); contentHash = hash; }); } @@ -108,6 +151,8 @@ class DiagramRenderControllerIT extends AbstractPostgresIT { @Test void getNonExistentHash_returns404() { + // Only test the flat content-hash endpoint here — 404 on bogus hash + // doesn't need a valid hash, so no SQL lookup is required. HttpHeaders headers = securityHelper.authHeadersNoBody(viewerJwt); headers.set("Accept", "image/svg+xml");