Command: static release-registry/task-tag/test-assertion provenance for collection-fetch-pagination Working directory: /shared/codebase/clean-architecture-backend-template Executed at: 2026-08-29T08:22:42Z Source revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5 Observation boundary: Static provenance only. Shows which Gradle tag the registry task selects and what the named collection-fetch test actually asserts; does not execute PostgreSQL. --- stdout/stderr --- === registry gate === { "name": "runtime-role-no-ddl", "task": ":adapter:outbound:persistence-jpa:jpaPlatformSecurityTest", "blocking": true }, { "name": "collection-fetch-pagination", "task": ":adapter:outbound:persistence-jpa:jpaPlatformQueryPlanTest", "blocking": true } ] } === lane registration === // leaf's existing Docker-backed source set; the mapping is recorded in // docs/jpa/repository-adaptation.md §3. // // Every lane fails closed. `failOnNoDiscoveredTests` matters more here than usual: a selected lane // that discovers nothing reports success, and a contract suite that silently stopped running is // indistinguishable from one that passes. Closure registerJpaPlatformLane = { String taskName, String tag, String description -> tasks.register(taskName, Test) { group = 'verification' it.description = description testClassesDirs = sourceSets.postgresqlIntegrationTest.output.classesDirs classpath = sourceSets.postgresqlIntegrationTest.runtimeClasspath useJUnitPlatform { includeTags tag } failOnNoDiscoveredTests = true outputs.upToDateWhen { false } jvmArgs('-Duser.timezone=UTC') // The Stable matrix selection. An unknown or empty value is an error in // PostgreSqlVersion.parseSelection rather than an empty run. systemProperty 'jpa.matrix.versions', (project.findProperty('jpa.matrix.versions') ?: '16').toString() } } def jpaPlatformContractTest = registerJpaPlatformLane( 'jpaPlatformContractTest', 'jpa-contract', 'Runs the JPA platform contract suite against real PostgreSQL (design §40).') def jpaPlatformMigrationTest = registerJpaPlatformLane( 'jpaPlatformMigrationTest', 'jpa-migration', 'Runs the Flyway upgrade snapshot scenarios (design §31).') def jpaPlatformFailureTest = registerJpaPlatformLane( 'jpaPlatformFailureTest', 'jpa-failure', 'Reproduces deadlock, serialization, and commit-ambiguity failures (design §39).') def jpaPlatformQueryPlanTest = registerJpaPlatformLane( 'jpaPlatformQueryPlanTest', 'jpa-queryplan', 'Asserts query plan structure and planner estimate error (design §33).') def jpaPlatformSecurityTest = registerJpaPlatformLane( 'jpaPlatformSecurityTest', 'jpa-security', 'Verifies runtime role privileges and search_path safety (design §36).') === tags === src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/platform/HibernateCollectionFetchPaginationContractTest.java:26:@Tag("jpa-contract") src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/platform/PostgreSqlQueryPlanContractTest.java:27:@Tag("jpa-queryplan") === collection-fetch test === package dev.caskeleton.adapter.outbound.persistence.platform; import static org.assertj.core.api.Assertions.assertThat; import dev.caskeleton.adapter.outbound.persistence.hibernate.HibernateStatisticsSnapshot; import dev.caskeleton.adapter.outbound.persistence.testkit.fetch.FetchPaginationExpectation; import dev.caskeleton.adapter.outbound.persistence.testkit.fetch.PagedChild; import dev.caskeleton.adapter.outbound.persistence.testkit.fetch.PagedParent; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; /** * The collection-fetch pagination gate (design §26). * *

The failure this exists to catch is in-memory pagination: older providers fetched every * matching parent row and applied the page in Java, logging a warning and returning correct results * while reading the whole table. Correct output, unbounded work, and nothing failing anywhere. * *

The assertion is therefore on the generated SQL, not on the returned page size. The * page is identical either way; only the SQL says where the limit was applied. */ @Tag("jpa-contract") class HibernateCollectionFetchPaginationContractTest { private static final int PARENTS = 50; private static final int CHILDREN_PER_PARENT = 4; private static final int PAGE_SIZE = 20; private static JpaPlatformContractSupport support; private static JpaPlatformEntityManagerSupport jpa; @BeforeAll static void startServerAndSeed() { support = JpaPlatformContractSupport.start(); jpa = JpaPlatformEntityManagerSupport.open(support, PagedParent.class, PagedChild.class); jpa.inTransactionDo( entityManager -> { for (int parent = 0; parent < PARENTS; parent++) { var paged = new PagedParent("parent-" + parent); for (int child = 0; child < CHILDREN_PER_PARENT; child++) { paged.addChild(new PagedChild("child-" + parent + '-' + child)); } entityManager.persist(paged); } }); } @AfterAll static void stopServer() { if (jpa != null) { jpa.close(); } if (support != null) { support.close(); } } @Test @DisplayName("a paged collection fetch bounds the parent selection in SQL") void oneCollectionPageIsBoundedInSql() { var expected = FetchPaginationExpectation.hibernate74PostgreSql(PAGE_SIZE); List page = jpa.inTransaction( entityManager -> entityManager .createQuery( "select distinct p from PagedParent p left join fetch p.children" + " order by p.id", PagedParent.class) .setMaxResults(PAGE_SIZE) .getResultList()); assertThat(page).hasSizeLessThanOrEqualTo(expected.maxReturnedParents()); assertThat(expected.requiresDatabaseLimit()).isTrue(); } @Test @DisplayName("the fetched page issues one statement, not one per parent") void pagedFetchIssuesOneStatement() { HibernateStatisticsSnapshot before = jpa.statistics().snapshot(); jpa.inTransactionDo( entityManager -> { List page = entityManager .createQuery( "select distinct p from PagedParent p left join fetch p.children" + " order by p.id", PagedParent.class) .setMaxResults(PAGE_SIZE) .getResultList(); page.forEach(parent -> assertThat(parent.children()).isNotNull()); }); HibernateStatisticsSnapshot delta = jpa.statistics().snapshot().minus(before); assertThat(delta.preparedStatements()) .as("a join fetch must not degrade into one statement per parent") .isLessThanOrEqualTo(2L); } @Test @DisplayName("without a fetch join the same access is an N+1") void withoutFetchJoinTheAccessIsAnNplusOne() { HibernateStatisticsSnapshot before = jpa.statistics().snapshot(); jpa.inTransactionDo( entityManager -> { List page = entityManager .createQuery("select p from PagedParent p order by p.id", PagedParent.class) === query-plan producer test === package dev.caskeleton.adapter.outbound.persistence.platform; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import dev.caskeleton.adapter.outbound.persistence.testkit.queryplan.NormalizedPlan; import dev.caskeleton.adapter.outbound.persistence.testkit.queryplan.PostgreSqlExplainRunner; import dev.caskeleton.adapter.outbound.persistence.testkit.queryplan.QueryPlanAssertions; import dev.caskeleton.adapter.outbound.persistence.testkit.queryplan.QueryPlanExpectation; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; /** * Captures a real plan and asserts on its structure (design §33). * *

Structure, not cost: costs and timings differ on every execution and every machine, so a * snapshot including them fails for reasons that have nothing to do with the query. What is stable * is which node types appear, how far the planner's estimate was from reality, and whether a sort * spilled to disk. */ @Tag("jpa-queryplan") class PostgreSqlQueryPlanContractTest { private static JpaPlatformContractSupport support; private static PostgreSqlExplainRunner runner; private final QueryPlanAssertions assertions = new QueryPlanAssertions(); @BeforeAll static void startServer() throws SQLException { support = JpaPlatformContractSupport.start(); runner = new PostgreSqlExplainRunner(support.dataSource()); try (Connection connection = support.connection(); Statement statement = connection.createStatement()) { statement.execute("create table plan_row (id bigint primary key, bucket int not null)"); statement.execute( "insert into plan_row(id, bucket) select generate_series(1, 5000)," + " (random() * 10)::int"); statement.execute("create index ix_plan_row_bucket on plan_row(bucket)"); statement.execute("analyze plan_row"); } } @AfterAll static void stopServer() { if (support != null) { support.close(); } } @Test @DisplayName("an indexed lookup produces a plan whose estimate is close to reality") void indexedLookupHasAccurateEstimate() { NormalizedPlan plan = runner.explain("select id from plan_row where id = ?", 42L); assertions.assertMatches( plan, QueryPlanExpectation.estimateOnly(10.0d).withDiskSortForbidden()); assertThat(plan.distinctNodeTypes()).isNotEmpty(); } @Test @DisplayName("a forbidden node type is reported with the whole plan") void forbiddenNodeTypeIsReported() { NormalizedPlan plan = runner.explain("select id from plan_row where bucket = ?", 3); assertThatThrownBy( () -> assertions.assertMatches( plan, QueryPlanExpectation.estimateOnly(10.0d) .forbidding( "Bitmap Heap Scan", "Index Scan", "Seq Scan", "Index Only Scan"))) .isInstanceOf(AssertionError.class) .hasMessageContaining("plan "); } @Test @DisplayName("EXPLAIN ANALYZE refuses anything that is not a SELECT") void refusesNonSelect() { assertThatThrownBy(() -> runner.explain("update plan_row set bucket = 1")) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("only SELECT"); } } Exit code: 0