fix(test): REST-drive Diagram / DiagramRender ITs for CH assertions
DiagramControllerIT.postDiagram_dataAppearsAfterFlush now verifies via
GET /api/v1/environments/{env}/apps/{app}/routes/{route}/diagram instead
of a PG SELECT against the ClickHouse route_diagrams table.
DiagramRenderControllerIT seeds both a diagram and an execution on the
same route, then reads the stamped diagramContentHash off the execution-
detail REST response to drive the flat /api/v1/diagrams/{hash}/render
tests. The env-scoped endpoint only serves JSON, so SVG tests still hit
the content-hash endpoint — but the hash comes from REST now, not SQL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
@@ -24,11 +25,13 @@ class DiagramControllerIT extends AbstractPostgresIT {
|
|||||||
private TestSecurityHelper securityHelper;
|
private TestSecurityHelper securityHelper;
|
||||||
|
|
||||||
private HttpHeaders authHeaders;
|
private HttpHeaders authHeaders;
|
||||||
|
private HttpHeaders viewerHeaders;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
String jwt = securityHelper.registerTestAgent("test-agent-diagram-it");
|
String jwt = securityHelper.registerTestAgent("test-agent-diagram-it");
|
||||||
authHeaders = securityHelper.authHeaders(jwt);
|
authHeaders = securityHelper.authHeaders(jwt);
|
||||||
|
viewerHeaders = securityHelper.authHeadersNoBody(securityHelper.viewerToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -68,11 +71,15 @@ class DiagramControllerIT extends AbstractPostgresIT {
|
|||||||
new HttpEntity<>(json, authHeaders),
|
new HttpEntity<>(json, authHeaders),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
await().atMost(10, SECONDS).untilAsserted(() -> {
|
// route_diagrams lives in ClickHouse; drive the visibility check
|
||||||
Integer count = jdbcTemplate.queryForObject(
|
// through the env-scoped diagram-render endpoint, never raw SQL.
|
||||||
"SELECT count(*) FROM route_diagrams WHERE route_id = 'diagram-flush-route'",
|
await().atMost(15, SECONDS).untilAsserted(() -> {
|
||||||
Integer.class);
|
ResponseEntity<String> r = restTemplate.exchange(
|
||||||
assertThat(count).isGreaterThanOrEqualTo(1);
|
"/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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.cameleer.server.app.controller;
|
|||||||
|
|
||||||
import com.cameleer.server.app.AbstractPostgresIT;
|
import com.cameleer.server.app.AbstractPostgresIT;
|
||||||
import com.cameleer.server.app.TestSecurityHelper;
|
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.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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;
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for {@link DiagramRenderController}.
|
* Integration tests for {@link DiagramRenderController}. The env-scoped
|
||||||
* Seeds a diagram via the ingestion endpoint, then tests rendering.
|
* 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 {
|
class DiagramRenderControllerIT extends AbstractPostgresIT {
|
||||||
|
|
||||||
@@ -28,19 +33,18 @@ class DiagramRenderControllerIT extends AbstractPostgresIT {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TestSecurityHelper securityHelper;
|
private TestSecurityHelper securityHelper;
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
private String jwt;
|
private String jwt;
|
||||||
private String viewerJwt;
|
private String viewerJwt;
|
||||||
private String contentHash;
|
private String contentHash;
|
||||||
|
|
||||||
/**
|
|
||||||
* Seed a diagram and compute its content hash for render tests.
|
|
||||||
*/
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void seedDiagram() {
|
void seedDiagram() {
|
||||||
jwt = securityHelper.registerTestAgent("test-agent-diagram-render-it");
|
jwt = securityHelper.registerTestAgent("test-agent-diagram-render-it");
|
||||||
viewerJwt = securityHelper.viewerToken();
|
viewerJwt = securityHelper.viewerToken();
|
||||||
|
|
||||||
String json = """
|
String diagramJson = """
|
||||||
{
|
{
|
||||||
"routeId": "render-test-route",
|
"routeId": "render-test-route",
|
||||||
"description": "Render test",
|
"description": "Render test",
|
||||||
@@ -56,18 +60,57 @@ class DiagramRenderControllerIT extends AbstractPostgresIT {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
""";
|
""";
|
||||||
|
|
||||||
restTemplate.postForEntity(
|
restTemplate.postForEntity(
|
||||||
"/api/v1/data/diagrams",
|
"/api/v1/data/diagrams",
|
||||||
new HttpEntity<>(json, securityHelper.authHeaders(jwt)),
|
new HttpEntity<>(diagramJson, securityHelper.authHeaders(jwt)),
|
||||||
String.class);
|
String.class);
|
||||||
|
|
||||||
// Wait for flush to storage and retrieve the content hash
|
// Post an execution for the same route so the ingestion pipeline
|
||||||
await().atMost(10, SECONDS).untilAsserted(() -> {
|
// stamps diagramContentHash on it — that's our path to fetching the
|
||||||
String hash = jdbcTemplate.queryForObject(
|
// hash without reading route_diagrams directly.
|
||||||
"SELECT content_hash FROM route_diagrams WHERE route_id = 'render-test-route' LIMIT 1",
|
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<String> search = restTemplate.exchange(
|
||||||
|
"/api/v1/environments/default/executions?correlationId=render-probe-corr",
|
||||||
|
HttpMethod.GET,
|
||||||
|
new HttpEntity<>(headers),
|
||||||
String.class);
|
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<String> 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;
|
contentHash = hash;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -108,6 +151,8 @@ class DiagramRenderControllerIT extends AbstractPostgresIT {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void getNonExistentHash_returns404() {
|
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);
|
HttpHeaders headers = securityHelper.authHeadersNoBody(viewerJwt);
|
||||||
headers.set("Accept", "image/svg+xml");
|
headers.set("Accept", "image/svg+xml");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user