chore: initialize from backend template 0a6dd0e
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
// JPA persistence adapter — merged RDBMS base + PostgreSQL vendor module.
|
||||
// Owns JPA entities, Spring Data repositories, mappers, transaction/audit/lock/outbox port
|
||||
// implementations, and the vendor-neutral SPI interfaces (OutboxClaimRepository /
|
||||
// SqlStateErrorMapping). The PostgreSQL driver, flyway-database-postgresql dialect, and vendor
|
||||
// Flyway migrations live only under the .postgresql subpackage (ArchUnit keeps the base neutral).
|
||||
sourceSets {
|
||||
postgresqlIntegrationTest {
|
||||
java.setSrcDirs(['src/postgresqlIntegrationTest/java'])
|
||||
resources.setSrcDirs(['src/postgresqlIntegrationTest/resources'])
|
||||
compileClasspath += sourceSets.main.output
|
||||
runtimeClasspath += output + compileClasspath
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
postgresqlIntegrationTestImplementation.extendsFrom testImplementation
|
||||
postgresqlIntegrationTestCompileOnly.extendsFrom testCompileOnly
|
||||
postgresqlIntegrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||
postgresqlIntegrationTestAnnotationProcessor.extendsFrom testAnnotationProcessor
|
||||
}
|
||||
|
||||
ext.jpaPostgreSqlEvidenceImage = 'postgres:16-alpine'
|
||||
|
||||
dependencies {
|
||||
implementation project(':application-core')
|
||||
implementation project(':shared-contract')
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
// feature-distributed-lock-contract: Spring Integration JDBC LockRegistry backs the
|
||||
// multi-instance distributedLockProvider. Version managed by Spring Boot BOM.
|
||||
implementation 'org.springframework.integration:spring-integration-jdbc'
|
||||
|
||||
// Vendor (PostgreSQL): Flyway migration API + PostgreSQL driver/dialect. Used only by the
|
||||
// .postgresql subpackage; the RDBMS base stays vendor-neutral (PERSISTENCE_RDBMS_STAYS_VENDOR_NEUTRAL).
|
||||
implementation 'org.springframework.boot:spring-boot-starter-flyway'
|
||||
runtimeOnly 'org.postgresql:postgresql'
|
||||
runtimeOnly 'org.flywaydb:flyway-database-postgresql'
|
||||
|
||||
// Vendor (H2): the local-profile driver. Used only by the .h2 subpackage, which reaches it
|
||||
// through JDBC/JPA rather than by importing org.h2 types — the same shape as the PostgreSQL
|
||||
// driver above. Not `developmentOnly`: local is a deployable profile of this artifact, and the
|
||||
// vendor selector, not the packaging, decides which driver a deployment loads.
|
||||
runtimeOnly 'com.h2database:h2'
|
||||
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
|
||||
|
||||
postgresqlIntegrationTestImplementation 'org.testcontainers:testcontainers-postgresql'
|
||||
postgresqlIntegrationTestImplementation 'org.testcontainers:testcontainers-junit-jupiter'
|
||||
postgresqlIntegrationTestRuntimeOnly 'org.postgresql:postgresql'
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' }
|
||||
|
||||
def registerPostgreSqlReadinessTest = { String taskName, String testClass ->
|
||||
tasks.register(taskName, Test) {
|
||||
group = 'verification'
|
||||
description = "Runs the no-skip real PostgreSQL readiness scenario ${testClass}."
|
||||
testClassesDirs = sourceSets.postgresqlIntegrationTest.output.classesDirs
|
||||
classpath = sourceSets.postgresqlIntegrationTest.runtimeClasspath
|
||||
useJUnitPlatform()
|
||||
filter {
|
||||
includeTestsMatching testClass
|
||||
}
|
||||
failOnNoDiscoveredTests = true
|
||||
outputs.upToDateWhen { false }
|
||||
jvmArgs(
|
||||
'-Duser.timezone=UTC',
|
||||
"-Djpa.evidence.postgresql.image=${jpaPostgreSqlEvidenceImage}")
|
||||
}
|
||||
}
|
||||
|
||||
def postgresqlLifecycleIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlLifecycleIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlLifecycleIntegrationTest')
|
||||
def postgresqlSecurityBaselineIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlSecurityBaselineIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlSecurityBaselineIntegrationTest')
|
||||
def postgresqlMigrationIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlMigrationIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlMigrationIntegrationTest')
|
||||
def postgresqlTransactionIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlTransactionIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlTransactionIntegrationTest')
|
||||
def postgresqlAggregateIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlAggregateIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlAggregateIntegrationTest')
|
||||
def postgresqlQueryIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlQueryIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlQueryIntegrationTest')
|
||||
def postgresqlIdempotencyIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlIdempotencyIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlIdempotencyIntegrationTest')
|
||||
def postgresqlOutboxStorageIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlOutboxStorageIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlOutboxStorageIntegrationTest')
|
||||
def postgresqlOutboxPollingIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlOutboxPollingIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlOutboxPollingIntegrationTest')
|
||||
def postgresqlInboxIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlInboxIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlInboxIntegrationTest')
|
||||
def postgresqlFileserverMigrationIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlFileserverMigrationIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlFileserverMigrationIntegrationTest')
|
||||
def postgresqlFileserverMetadataIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlFileserverMetadataIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlFileserverMetadataStoreIntegrationTest')
|
||||
def postgresqlFileserverReclamationIntegrationTest = registerPostgreSqlReadinessTest(
|
||||
'postgresqlFileserverReclamationIntegrationTest',
|
||||
'dev.caskeleton.adapter.outbound.persistence.readiness.PostgreSqlFileserverReclamationIntegrationTest')
|
||||
|
||||
def verifyJpaSqlConstructionSafety = tasks.register('verifyJpaSqlConstructionSafety') {
|
||||
group = 'verification'
|
||||
description = 'Rejects concatenated SQL construction and non-parameterized PostgreSQL timeout configuration.'
|
||||
File vendorSource = file('src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql')
|
||||
inputs.dir(vendorSource)
|
||||
doLast {
|
||||
List<String> violations = []
|
||||
vendorSource.eachFileRecurse { File source ->
|
||||
if (!source.name.endsWith('.java')) {
|
||||
return
|
||||
}
|
||||
String text = source.getText('UTF-8')
|
||||
def concatenatedSql = text =~ /(?s)(createNativeQuery|queryForObject|update)\s*\([^;]*"\s*\+/
|
||||
if (concatenatedSql.find()) {
|
||||
violations << "${source}: concatenated SQL construction"
|
||||
}
|
||||
source.readLines().eachWithIndex { String line, int index ->
|
||||
if (line.contains("set_config('") && !line.contains('?')) {
|
||||
violations << "${source}:${index + 1}: set_config value is not parameterized"
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!violations.isEmpty()) {
|
||||
throw new GradleException(
|
||||
"verifyJpaSqlConstructionSafety: ${violations.size()} violation(s):\n " +
|
||||
violations.join('\n '))
|
||||
}
|
||||
logger.lifecycle(
|
||||
'verifyJpaSqlConstructionSafety: OK — no concatenated SQL construction and all set_config values are parameterized.')
|
||||
}
|
||||
}
|
||||
|
||||
def verifyJpaSecurityFixtures = tasks.register('verifyJpaSecurityFixtures') {
|
||||
group = 'verification'
|
||||
description = 'Verifies the no-skip PostgreSQL security fixture covers runtime-role namespace denial.'
|
||||
File fixture = file(
|
||||
'src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/readiness/PostgreSqlSecurityBaselineIntegrationTest.java')
|
||||
inputs.file(fixture)
|
||||
doLast {
|
||||
if (!fixture.isFile()) {
|
||||
throw new GradleException("verifyJpaSecurityFixtures: missing ${fixture}")
|
||||
}
|
||||
String text = fixture.getText('UTF-8')
|
||||
['runtimeRoleCannotCreateInApplicationSchema', 'assertDockerAvailable', '42501'].each {
|
||||
String required ->
|
||||
if (!text.contains(required)) {
|
||||
throw new GradleException(
|
||||
"verifyJpaSecurityFixtures: ${fixture} is missing '${required}'")
|
||||
}
|
||||
}
|
||||
logger.lifecycle(
|
||||
'verifyJpaSecurityFixtures: OK — no-skip Docker and runtime-role namespace denial fixtures are present.')
|
||||
}
|
||||
}
|
||||
|
||||
postgresqlSecurityBaselineIntegrationTest.configure {
|
||||
dependsOn verifyJpaSqlConstructionSafety
|
||||
dependsOn verifyJpaSecurityFixtures
|
||||
dependsOn project(':adapter:inbound:web').tasks.named('jpaPersistenceRedactionContractTest')
|
||||
}
|
||||
|
||||
apply from: rootProject.file('gradle/jpa-evidence.gradle')
|
||||
Reference in New Issue
Block a user