test(02-04): add failing DiagramLinkingIT for diagram hash linking
- Test 1: diagram_content_hash populated with SHA-256 when RouteGraph exists - Test 2: diagram_content_hash empty when no RouteGraph exists for route Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,152 @@
|
|||||||
|
package com.cameleer3.server.app.storage;
|
||||||
|
|
||||||
|
import com.cameleer3.server.app.AbstractClickHouseIT;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
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.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test proving that diagram_content_hash is populated during
|
||||||
|
* execution ingestion when a RouteGraph exists for the same route+agent.
|
||||||
|
*/
|
||||||
|
class DiagramLinkingIT extends AbstractClickHouseIT {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void diagramHashPopulated_whenRouteGraphExistsBeforeExecution() {
|
||||||
|
// 1. Ingest a RouteGraph for route "diagram-link-route" via the diagrams endpoint
|
||||||
|
String graphJson = """
|
||||||
|
{
|
||||||
|
"routeId": "diagram-link-route",
|
||||||
|
"description": "Linking test diagram",
|
||||||
|
"version": 1,
|
||||||
|
"nodes": [
|
||||||
|
{"id": "n1", "type": "ENDPOINT", "label": "direct:start"},
|
||||||
|
{"id": "n2", "type": "BEAN", "label": "myBean"}
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{"source": "n1", "target": "n2", "edgeType": "FLOW"}
|
||||||
|
],
|
||||||
|
"processorNodeMapping": {}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.set("X-Cameleer-Protocol-Version", "1");
|
||||||
|
|
||||||
|
ResponseEntity<String> diagramResponse = restTemplate.postForEntity(
|
||||||
|
"/api/v1/data/diagrams",
|
||||||
|
new HttpEntity<>(graphJson, headers),
|
||||||
|
String.class);
|
||||||
|
assertThat(diagramResponse.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
|
||||||
|
|
||||||
|
// 2. Wait for diagram to be flushed to ClickHouse before ingesting execution
|
||||||
|
await().atMost(10, SECONDS).untilAsserted(() -> {
|
||||||
|
String hash = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT content_hash FROM route_diagrams WHERE route_id = 'diagram-link-route' LIMIT 1",
|
||||||
|
String.class);
|
||||||
|
assertThat(hash).isNotNull().isNotEmpty();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Ingest a RouteExecution for the same routeId
|
||||||
|
String executionJson = """
|
||||||
|
{
|
||||||
|
"routeId": "diagram-link-route",
|
||||||
|
"exchangeId": "ex-diag-link-1",
|
||||||
|
"correlationId": "corr-diag-link-1",
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"startTime": "2026-03-11T10:00:00Z",
|
||||||
|
"endTime": "2026-03-11T10:00:01Z",
|
||||||
|
"durationMs": 1000,
|
||||||
|
"processors": [
|
||||||
|
{
|
||||||
|
"processorId": "proc-1",
|
||||||
|
"processorType": "bean",
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"startTime": "2026-03-11T10:00:00Z",
|
||||||
|
"endTime": "2026-03-11T10:00:00.500Z",
|
||||||
|
"durationMs": 500,
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
ResponseEntity<String> execResponse = restTemplate.postForEntity(
|
||||||
|
"/api/v1/data/executions",
|
||||||
|
new HttpEntity<>(executionJson, headers),
|
||||||
|
String.class);
|
||||||
|
assertThat(execResponse.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
|
||||||
|
|
||||||
|
// 4. Verify diagram_content_hash is a non-empty SHA-256 hash (64 hex chars)
|
||||||
|
await().atMost(10, SECONDS).untilAsserted(() -> {
|
||||||
|
String hash = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT diagram_content_hash FROM route_executions WHERE route_id = 'diagram-link-route'",
|
||||||
|
String.class);
|
||||||
|
assertThat(hash)
|
||||||
|
.isNotNull()
|
||||||
|
.isNotEmpty()
|
||||||
|
.hasSize(64) // SHA-256 hex = 64 characters
|
||||||
|
.matches("[a-f0-9]{64}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void diagramHashEmpty_whenNoRouteGraphExists() {
|
||||||
|
// Ingest a RouteExecution for a route with NO prior diagram
|
||||||
|
String executionJson = """
|
||||||
|
{
|
||||||
|
"routeId": "no-diagram-route",
|
||||||
|
"exchangeId": "ex-no-diag-1",
|
||||||
|
"correlationId": "corr-no-diag-1",
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"startTime": "2026-03-11T10:00:00Z",
|
||||||
|
"endTime": "2026-03-11T10:00:01Z",
|
||||||
|
"durationMs": 1000,
|
||||||
|
"processors": [
|
||||||
|
{
|
||||||
|
"processorId": "proc-no-diag",
|
||||||
|
"processorType": "log",
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"startTime": "2026-03-11T10:00:00Z",
|
||||||
|
"endTime": "2026-03-11T10:00:00.500Z",
|
||||||
|
"durationMs": 500,
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
headers.set("X-Cameleer-Protocol-Version", "1");
|
||||||
|
|
||||||
|
ResponseEntity<String> response = restTemplate.postForEntity(
|
||||||
|
"/api/v1/data/executions",
|
||||||
|
new HttpEntity<>(executionJson, headers),
|
||||||
|
String.class);
|
||||||
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
|
||||||
|
|
||||||
|
// Verify diagram_content_hash is empty string (graceful fallback)
|
||||||
|
await().atMost(10, SECONDS).untilAsserted(() -> {
|
||||||
|
String hash = jdbcTemplate.queryForObject(
|
||||||
|
"SELECT diagram_content_hash FROM route_executions WHERE route_id = 'no-diagram-route'",
|
||||||
|
String.class);
|
||||||
|
assertThat(hash)
|
||||||
|
.isNotNull()
|
||||||
|
.isEmpty();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user