feat(02-04): populate diagram_content_hash during ingestion and fix test stability

- Inject DiagramRepository into ClickHouseExecutionRepository for hash lookup
- Replace empty string placeholder with actual SHA-256 diagram hash in insertBatch
- Add Surefire/Failsafe forkCount=1 reuseForks=false for classloader isolation
- Add failsafe-plugin integration-test/verify goals for IT execution
- Create DiagramLinkingIT with positive (hash populated) and negative (empty fallback) cases
- Fix flaky awaitility assertions with ignoreExceptions for EmptyResultDataAccess
- Increase IngestionSchemaIT timeouts to 30s for reliable batch flush waits
- Adjust SearchControllerIT pagination assertion to match correct seed data count

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-11 17:36:33 +01:00
parent ea9d81213f
commit 34c831040a
5 changed files with 40 additions and 10 deletions

View File

@@ -327,12 +327,12 @@ class SearchControllerIT extends AbstractClickHouseIT {
@Test
void pagination_worksCorrectly() throws Exception {
// First, get total count of COMPLETED executions (8 from our seed data,
// but may include data from other test classes)
// First, get total count of COMPLETED executions (7 from our seed data:
// exec 1 + execs 5-10; execs 2,4 are FAILED, exec 3 is RUNNING)
ResponseEntity<String> countResponse = searchGet("?status=COMPLETED&limit=1");
JsonNode countBody = objectMapper.readTree(countResponse.getBody());
long totalCompleted = countBody.get("total").asLong();
assertThat(totalCompleted).isGreaterThanOrEqualTo(8);
assertThat(totalCompleted).isGreaterThanOrEqualTo(7);
// Now test pagination with offset=2, limit=3
ResponseEntity<String> response = searchPost("""

View File

@@ -91,7 +91,7 @@ class DiagramLinkingIT extends AbstractClickHouseIT {
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(() -> {
await().atMost(10, SECONDS).ignoreExceptions().untilAsserted(() -> {
String hash = jdbcTemplate.queryForObject(
"SELECT diagram_content_hash FROM route_executions WHERE route_id = 'diagram-link-route'",
String.class);
@@ -140,7 +140,7 @@ class DiagramLinkingIT extends AbstractClickHouseIT {
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
// Verify diagram_content_hash is empty string (graceful fallback)
await().atMost(10, SECONDS).untilAsserted(() -> {
await().atMost(10, SECONDS).ignoreExceptions().untilAsserted(() -> {
String hash = jdbcTemplate.queryForObject(
"SELECT diagram_content_hash FROM route_executions WHERE route_id = 'no-diagram-route'",
String.class);

View File

@@ -84,7 +84,7 @@ class IngestionSchemaIT extends AbstractClickHouseIT {
postExecution(json);
await().atMost(10, SECONDS).untilAsserted(() -> {
await().atMost(30, SECONDS).ignoreExceptions().untilAsserted(() -> {
// Use individual typed queries to avoid ClickHouse Array cast issues
var depths = queryArray(
"SELECT processor_depths FROM route_executions WHERE route_id = 'schema-test-tree'");
@@ -160,7 +160,7 @@ class IngestionSchemaIT extends AbstractClickHouseIT {
postExecution(json);
await().atMost(10, SECONDS).untilAsserted(() -> {
await().atMost(30, SECONDS).ignoreExceptions().untilAsserted(() -> {
// Bodies should contain all sources
String bodies = jdbcTemplate.queryForObject(
"SELECT exchange_bodies FROM route_executions WHERE route_id = 'schema-test-bodies'",
@@ -206,7 +206,7 @@ class IngestionSchemaIT extends AbstractClickHouseIT {
postExecution(json);
await().atMost(10, SECONDS).untilAsserted(() -> {
await().atMost(30, SECONDS).ignoreExceptions().untilAsserted(() -> {
// Empty but not null
String bodies = jdbcTemplate.queryForObject(
"SELECT exchange_bodies FROM route_executions WHERE route_id = 'schema-test-null-snap'",