From 9c46c91aab0f47bd398eca4a08b54822560dd529 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 12 Aug 2026 00:14:51 +0900
Subject: [PATCH 1/5] test(coverage): reject empty JaCoCo production selection
---
.../JaCoCoCoverageConfigurationTest.java | 185 ++++++++++++++++++
1 file changed, 185 insertions(+)
create mode 100644 etl-service/src/test/java/com/xtrmetl/etl/documentation/JaCoCoCoverageConfigurationTest.java
diff --git a/etl-service/src/test/java/com/xtrmetl/etl/documentation/JaCoCoCoverageConfigurationTest.java b/etl-service/src/test/java/com/xtrmetl/etl/documentation/JaCoCoCoverageConfigurationTest.java
new file mode 100644
index 00000000..88043151
--- /dev/null
+++ b/etl-service/src/test/java/com/xtrmetl/etl/documentation/JaCoCoCoverageConfigurationTest.java
@@ -0,0 +1,185 @@
+package com.xtrmetl.etl.documentation;
+
+import org.junit.jupiter.api.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Guards the JaCoCo durable-job coverage gate against an empty production-class selection that can
+ * otherwise satisfy zero-missed counters vacuously.
+ */
+class JaCoCoCoverageConfigurationTest {
+
+ private static final Path PROJECT_ROOT = projectRoot();
+ private static final Set EXPECTED_CLASS_FILE_PATTERNS = Set.of(
+ "com/xtrmetl/etl/job/*.class",
+ "com/xtrmetl/etl/controller/EtlJobController*.class"
+ );
+
+ @Test
+ void reportAndCheckSelectCompiledClassFilesInsteadOfReusingAgentClassNames() throws Exception {
+ Element plugin = jacocoPlugin(parsePom());
+ Element pluginConfiguration = directChild(plugin, "configuration");
+ assertTrue(
+ pluginConfiguration == null || directChild(pluginConfiguration, "includes") == null,
+ "Plugin-level includes leak one filter syntax into prepare-agent, report, and check"
+ );
+
+ assertEquals(
+ EXPECTED_CLASS_FILE_PATTERNS,
+ executionIncludes(plugin, "report-durable-job-coverage")
+ );
+ assertEquals(
+ EXPECTED_CLASS_FILE_PATTERNS,
+ executionIncludes(plugin, "check-durable-job-coverage")
+ );
+ }
+
+ @Test
+ void coverageCheckRequiresAtLeastOneAnalyzedProductionClass() throws Exception {
+ Element plugin = jacocoPlugin(parsePom());
+ Element checkExecution = execution(plugin, "check-durable-job-coverage");
+ Element configuration = requireDirectChild(checkExecution, "configuration");
+ Element rules = requireDirectChild(configuration, "rules");
+ Element rule = requireDirectChild(rules, "rule");
+
+ assertEquals("BUNDLE", directChildText(rule, "element"));
+
+ Element limits = requireDirectChild(rule, "limits");
+ boolean foundNonEmptyGuard = false;
+ for (Element limit : directChildren(limits, "limit")) {
+ if ("CLASS".equals(directChildText(limit, "counter"))
+ && "TOTALCOUNT".equals(directChildText(limit, "value"))
+ && "1".equals(directChildText(limit, "minimum"))) {
+ foundNonEmptyGuard = true;
+ }
+ }
+ assertTrue(
+ foundNonEmptyGuard,
+ "Coverage must fail closed when the selected production bundle contains zero classes"
+ );
+ }
+
+ @Test
+ void intendedCoverageTargetContainsCompiledProductionClasses() {
+ Path classes = PROJECT_ROOT.resolve("etl-service/target/classes");
+ assertTrue(
+ Files.isRegularFile(classes.resolve("com/xtrmetl/etl/job/EtlJobService.class")),
+ "EtlJobService must be a real compiled production coverage target"
+ );
+ assertTrue(
+ Files.isRegularFile(classes.resolve("com/xtrmetl/etl/controller/EtlJobController.class")),
+ "EtlJobController must be a real compiled production coverage target"
+ );
+ }
+
+ private static Document parsePom() throws Exception {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ factory.setXIncludeAware(false);
+ factory.setExpandEntityReferences(false);
+ return factory.newDocumentBuilder().parse(PROJECT_ROOT.resolve("etl-service/pom.xml").toFile());
+ }
+
+ private static Element jacocoPlugin(Document document) {
+ NodeList plugins = document.getElementsByTagName("plugin");
+ for (int index = 0; index < plugins.getLength(); index++) {
+ Element plugin = (Element) plugins.item(index);
+ if ("org.jacoco".equals(directChildText(plugin, "groupId"))
+ && "jacoco-maven-plugin".equals(directChildText(plugin, "artifactId"))) {
+ return plugin;
+ }
+ }
+ throw new AssertionError("etl-service POM is missing jacoco-maven-plugin");
+ }
+
+ private static Set executionIncludes(Element plugin, String executionId) {
+ Element configuration = requireDirectChild(execution(plugin, executionId), "configuration");
+ Element includes = requireDirectChild(configuration, "includes");
+ Set values = new LinkedHashSet<>();
+ for (Element include : directChildren(includes, "include")) {
+ values.add(include.getTextContent().trim());
+ }
+ assertFalse(values.isEmpty(), "JaCoCo class-file selection must not be empty");
+ return values;
+ }
+
+ private static Element execution(Element plugin, String executionId) {
+ Element executions = requireDirectChild(plugin, "executions");
+ for (Element execution : directChildren(executions, "execution")) {
+ if (executionId.equals(directChildText(execution, "id"))) {
+ return execution;
+ }
+ }
+ throw new AssertionError("Missing JaCoCo execution: " + executionId);
+ }
+
+ private static Element requireDirectChild(Element parent, String name) {
+ Element child = directChild(parent, name);
+ assertNotNull(child, () -> "Missing <" + name + "> under <" + parent.getTagName() + ">");
+ return child;
+ }
+
+ private static Element directChild(Element parent, String name) {
+ NodeList children = parent.getChildNodes();
+ for (int index = 0; index < children.getLength(); index++) {
+ Node child = children.item(index);
+ if (child instanceof Element element && name.equals(element.getTagName())) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ private static Set directChildren(Element parent, String name) {
+ Set matches = new LinkedHashSet<>();
+ NodeList children = parent.getChildNodes();
+ for (int index = 0; index < children.getLength(); index++) {
+ Node child = children.item(index);
+ if (child instanceof Element element && name.equals(element.getTagName())) {
+ matches.add(element);
+ }
+ }
+ return matches;
+ }
+
+ private static String directChildText(Element parent, String name) {
+ Element child = directChild(parent, name);
+ return child == null ? null : child.getTextContent().trim();
+ }
+
+ /** Finds the repository root from root- or module-scoped Maven execution. */
+ private static Path projectRoot() {
+ Path current = Paths.get(System.getProperty("user.dir")).toAbsolutePath();
+ Path lastPomParent = null;
+ while (current != null) {
+ if (Files.exists(current.resolve(".git"))) {
+ return current;
+ }
+ if (Files.exists(current.resolve("pom.xml"))) {
+ lastPomParent = current;
+ }
+ current = current.getParent();
+ }
+ if (lastPomParent != null) {
+ return lastPomParent;
+ }
+ throw new IllegalStateException("Could not find project root");
+ }
+}
From d6b6759f04c59087db861baefeb0e0cba59e81c9 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 12 Aug 2026 00:20:58 +0900
Subject: [PATCH 2/5] fix(coverage): make JaCoCo production selection
non-vacuous
---
etl-service/pom.xml | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/etl-service/pom.xml b/etl-service/pom.xml
index 00695de2..3d840a28 100644
--- a/etl-service/pom.xml
+++ b/etl-service/pom.xml
@@ -98,12 +98,6 @@
org.jacoco
jacoco-maven-plugin
0.8.15
-
-
- com.xtrmetl.etl.job.*
- com.xtrmetl.etl.controller.EtlJobController*
-
-
prepare-durable-job-coverage
@@ -118,6 +112,12 @@
report
+
+
+ com/xtrmetl/etl/job/*.class
+ com/xtrmetl/etl/controller/EtlJobController*.class
+
+
check-durable-job-coverage
@@ -126,14 +126,19 @@
check
+
+ com/xtrmetl/etl/job/*.class
+ com/xtrmetl/etl/controller/EtlJobController*.class
+
- CLASS
-
- com.xtrmetl.etl.job.*
- com.xtrmetl.etl.controller.EtlJobController*
-
+ BUNDLE
+
+ CLASS
+ TOTALCOUNT
+ 1
+
INSTRUCTION
MISSEDCOUNT
From a11c0ab2b5047bd55544b2b2e40d21c1258a6115 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 12 Aug 2026 03:05:15 +0900
Subject: [PATCH 3/5] test(coverage): align durable-job policy with non-vacuous
bundle gate
---
.../etl/job/EtlJobCoveragePolicyTest.java | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobCoveragePolicyTest.java b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobCoveragePolicyTest.java
index 4a728d75..beb63c91 100644
--- a/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobCoveragePolicyTest.java
+++ b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobCoveragePolicyTest.java
@@ -14,13 +14,15 @@
* Keeps the durable-job production slice bound to an executable 100% coverage policy.
*
* The policy is intentionally scoped to the production classes introduced by the durable-job
- * intake slice. It requires current Java-compatible JaCoCo instrumentation and zero missed
- * instructions, lines, methods, or branches while the ordinary {@code mvn test} lifecycle runs.
+ * intake slice. It requires current Java-compatible JaCoCo instrumentation, a non-empty selected
+ * production bundle, and zero missed instructions, lines, methods, or branches while the ordinary
+ * {@code mvn test} lifecycle runs.
*/
class EtlJobCoveragePolicyTest {
/**
- * Requires the ETL module build to fail when any durable-job production path is untested.
+ * Requires the ETL module build to fail when the durable-job production slice is empty or any
+ * selected production path is untested.
*
* @throws IOException when the module build descriptor cannot be read
*/
@@ -36,10 +38,14 @@ void etlModuleEnforcesCompleteInstructionAndBranchCoverageForTheDurableJobSlice(
assertTrue(modulePom.contains("test"));
assertTrue(modulePom.contains("report"));
assertTrue(modulePom.contains("check"));
- assertTrue(modulePom.contains("com.xtrmetl.etl.job.*"));
+ assertTrue(modulePom.contains("com/xtrmetl/etl/job/*.class"));
assertTrue(modulePom.contains(
- "com.xtrmetl.etl.controller.EtlJobController*"
+ "com/xtrmetl/etl/controller/EtlJobController*.class"
));
+ assertTrue(modulePom.contains("BUNDLE"));
+ assertTrue(modulePom.contains("CLASS"));
+ assertTrue(modulePom.contains("TOTALCOUNT"));
+ assertTrue(modulePom.contains("1"));
assertTrue(modulePom.contains("INSTRUCTION"));
assertTrue(modulePom.contains("LINE"));
assertTrue(modulePom.contains("METHOD"));
From 5850abb399a6e0ae2b418965e6542c6967b9bbf3 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 12 Aug 2026 04:14:35 +0900
Subject: [PATCH 4/5] test(coverage): cover durable-job defensive branches
---
.../etl/job/EtlJobServiceBoundaryTest.java | 94 +++++++++++++++++++
.../job/EtlJobServiceDefensiveBranchTest.java | 85 +++++++++++++++++
2 files changed, 179 insertions(+)
create mode 100644 etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceDefensiveBranchTest.java
diff --git a/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceBoundaryTest.java b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceBoundaryTest.java
index 6ce140d6..2d254b0b 100644
--- a/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceBoundaryTest.java
+++ b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceBoundaryTest.java
@@ -10,10 +10,18 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionSynchronizationManager;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.HashMap;
+import java.util.Map;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -69,6 +77,44 @@ void returnsImmediateConflictWhenAnotherSubmissionOwnsTheTryLock() {
verifyNoInteractions(jdbcTemplate);
}
+ @Test
+ void reportsPlatformFailureWhenRequiredSha256DigestIsUnavailable() {
+ JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
+ EtlRequestLock requestLock = mock(EtlRequestLock.class);
+ EtlJobService service = service(jdbcTemplate, requestLock, new EtlBatchProperties());
+ Provider[] originalProviders = Security.getProviders();
+ Provider[] sha256Providers = Security.getProviders("MessageDigest.SHA-256");
+ assertNotNull(sha256Providers);
+ assertTrue(sha256Providers.length > 0);
+
+ Map originalPositions = new HashMap<>();
+ for (int index = 0; index < originalProviders.length; index++) {
+ originalPositions.put(originalProviders[index].getName(), index + 1);
+ }
+
+ try {
+ for (Provider provider : sha256Providers) {
+ Security.removeProvider(provider.getName());
+ }
+ assertThrows(NoSuchAlgorithmException.class, () -> MessageDigest.getInstance("SHA-256"));
+
+ IllegalStateException exception = assertThrows(
+ IllegalStateException.class,
+ () -> service.findOwned(UUID.randomUUID(), "tenant_alpha")
+ );
+
+ assertEquals("SHA-256 is required by the Java platform", exception.getMessage());
+ assertEquals(NoSuchAlgorithmException.class, exception.getCause().getClass());
+ } finally {
+ for (Provider provider : originalProviders) {
+ if (Security.getProvider(provider.getName()) == null) {
+ Security.insertProviderAt(provider, originalPositions.get(provider.getName()));
+ }
+ }
+ }
+ verifyNoInteractions(requestLock, jdbcTemplate);
+ }
+
@Test
void rejectsInvalidKeysAndPrincipalScopesBeforeTransactionOrDatabaseAccess() {
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
@@ -110,6 +156,10 @@ void rejectsEveryCoveredPayloadAdmissionFailureBeforePersistence() {
EtlRequestError.INVALID_JSON,
() -> service.submit(null, IDEMPOTENCY_KEY, "tenant_alpha")
);
+ assertError(
+ EtlRequestError.INVALID_JSON,
+ () -> service.submit(" ", IDEMPOTENCY_KEY, "tenant_alpha")
+ );
assertError(
EtlRequestError.INVALID_JSON,
() -> service.submit("null", IDEMPOTENCY_KEY, "tenant_alpha")
@@ -154,10 +204,50 @@ void rejectsEveryCoveredPayloadAdmissionFailureBeforePersistence() {
EtlRequestError.INVALID_RECORD,
() -> service.submit("[{\"id\":\" record_alpha\"}]", IDEMPOTENCY_KEY, "tenant_alpha")
);
+ assertError(
+ EtlRequestError.INVALID_RECORD,
+ () -> service.submit(
+ payloadWithIdentifier("\u00a0record_alpha"),
+ IDEMPOTENCY_KEY,
+ "tenant_alpha"
+ )
+ );
+ assertError(
+ EtlRequestError.INVALID_RECORD,
+ () -> service.submit(
+ payloadWithIdentifier("x".repeat(257)),
+ IDEMPOTENCY_KEY,
+ "tenant_alpha"
+ )
+ );
assertError(
EtlRequestError.INVALID_RECORD,
() -> service.submit("[{\"id\":\"record\\u0000alpha\"}]", IDEMPOTENCY_KEY, "tenant_alpha")
);
+ assertError(
+ EtlRequestError.INVALID_RECORD,
+ () -> service.submit(
+ payloadWithIdentifier("record" + Character.toString(0x200e) + "alpha"),
+ IDEMPOTENCY_KEY,
+ "tenant_alpha"
+ )
+ );
+ assertError(
+ EtlRequestError.INVALID_RECORD,
+ () -> service.submit(
+ payloadWithIdentifier("record" + Character.toString(0x2028) + "alpha"),
+ IDEMPOTENCY_KEY,
+ "tenant_alpha"
+ )
+ );
+ assertError(
+ EtlRequestError.INVALID_RECORD,
+ () -> service.submit(
+ payloadWithIdentifier("record" + Character.toString(0x2029) + "alpha"),
+ IDEMPOTENCY_KEY,
+ "tenant_alpha"
+ )
+ );
assertError(
EtlRequestError.INVALID_RECORD,
() -> service.submit(
@@ -203,6 +293,10 @@ void validatesConstructorAndLookupRequiredValues() {
verifyNoInteractions(requestLock, jdbcTemplate);
}
+ private static String payloadWithIdentifier(String identifier) {
+ return "[{\"id\":\"" + identifier + "\"}]";
+ }
+
private static EtlJobService service(
JdbcTemplate jdbcTemplate,
EtlRequestLock requestLock,
diff --git a/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceDefensiveBranchTest.java b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceDefensiveBranchTest.java
new file mode 100644
index 00000000..556eb460
--- /dev/null
+++ b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobServiceDefensiveBranchTest.java
@@ -0,0 +1,85 @@
+package com.xtrmetl.etl.job;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.xtrmetl.etl.service.EtlBatchProperties;
+import com.xtrmetl.etl.service.EtlRequestError;
+import com.xtrmetl.etl.service.EtlRequestException;
+import com.xtrmetl.etl.service.EtlRequestLock;
+import org.junit.jupiter.api.Test;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Exercises defensive JSON-tree outcomes that ordinary Jackson parsing cannot reliably manufacture.
+ *
+ * The service accepts an application-provided {@link ObjectMapper}, copies it, and therefore owns
+ * the boundary between parser output and durable admission. These tests keep that boundary fail-closed
+ * when a compatible mapper reports end-of-input without a tree or exposes a Java {@code null} array
+ * element instead of Jackson's usual {@code NullNode}. Neither case may reach transaction locks or
+ * persistence.
+ */
+class EtlJobServiceDefensiveBranchTest {
+
+ private static final String IDEMPOTENCY_KEY = "550e8400-e29b-41d4-a716-446655440000";
+ private static final String PRINCIPAL_SCOPE = "tenant_alpha";
+
+ @Test
+ void rejectsParserEndOfInputWhenMapperProducesNoTree() throws Exception {
+ JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
+ EtlRequestLock requestLock = mock(EtlRequestLock.class);
+ ObjectMapper sourceMapper = mock(ObjectMapper.class);
+ ObjectMapper copiedMapper = mock(ObjectMapper.class);
+ when(sourceMapper.copy()).thenReturn(copiedMapper);
+ when(copiedMapper.readTree("parser-end-of-input")).thenReturn(null);
+ EtlJobService service = new EtlJobService(
+ jdbcTemplate,
+ sourceMapper,
+ new EtlBatchProperties(),
+ requestLock
+ );
+
+ EtlRequestException exception = assertThrows(
+ EtlRequestException.class,
+ () -> service.submit("parser-end-of-input", IDEMPOTENCY_KEY, PRINCIPAL_SCOPE)
+ );
+
+ assertEquals(EtlRequestError.INVALID_JSON, exception.error());
+ verifyNoInteractions(requestLock, jdbcTemplate);
+ }
+
+ @Test
+ void rejectsNullParserArrayElementBeforeTransactionOrPersistence() throws Exception {
+ JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
+ EtlRequestLock requestLock = mock(EtlRequestLock.class);
+ ObjectMapper sourceMapper = mock(ObjectMapper.class);
+ ObjectMapper copiedMapper = mock(ObjectMapper.class);
+ JsonNode root = mock(JsonNode.class);
+ when(sourceMapper.copy()).thenReturn(copiedMapper);
+ when(copiedMapper.readTree("parser-null-array-element")).thenReturn(root);
+ when(root.isArray()).thenReturn(true);
+ when(root.size()).thenReturn(1);
+ when(root.iterator()).thenReturn(Collections.singletonList((JsonNode) null).iterator());
+ EtlJobService service = new EtlJobService(
+ jdbcTemplate,
+ sourceMapper,
+ new EtlBatchProperties(),
+ requestLock
+ );
+
+ EtlRequestException exception = assertThrows(
+ EtlRequestException.class,
+ () -> service.submit("parser-null-array-element", IDEMPOTENCY_KEY, PRINCIPAL_SCOPE)
+ );
+
+ assertEquals(EtlRequestError.INVALID_RECORD, exception.error());
+ verifyNoInteractions(requestLock, jdbcTemplate);
+ }
+}
From 1d1f62da4aa2bf86ea547f5bb672168a3e7c34e6 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 12 Aug 2026 06:12:02 +0900
Subject: [PATCH 5/5] test(coverage): close current durable-job model branches
---
.../job/EtlJobModelCoverageBoundaryTest.java | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
create mode 100644 etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobModelCoverageBoundaryTest.java
diff --git a/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobModelCoverageBoundaryTest.java b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobModelCoverageBoundaryTest.java
new file mode 100644
index 00000000..07405c1c
--- /dev/null
+++ b/etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobModelCoverageBoundaryTest.java
@@ -0,0 +1,79 @@
+package com.xtrmetl.etl.job;
+
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Closes branch-coverage gaps in immutable durable-job model validation that are part of the
+ * non-vacuous JaCoCo production slice.
+ *
+ * These cases exercise each distinct short-circuit path in origin-relative status URL and
+ * lifecycle-dependent failure-code validation. They are intentionally model-boundary tests: no
+ * database, transaction, network, or controller fixture is needed to prove the record invariants.
+ */
+class EtlJobModelCoverageBoundaryTest {
+
+ private static final UUID JOB_RECORD_ID = UUID.fromString(
+ "cf4f083f-8c90-4f34-a8b6-b53761de44ef"
+ );
+ private static final Instant CREATED_AT = Instant.parse("2026-08-04T10:00:00Z");
+ private static final Instant UPDATED_AT = Instant.parse("2026-08-04T10:00:01Z");
+
+ @Test
+ void rejectsEveryUnsafeAcceptedResponseStatusUrlForm() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new EtlJobAcceptedResponse(
+ JOB_RECORD_ID,
+ EtlJobStatus.PENDING,
+ " "
+ )
+ );
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new EtlJobAcceptedResponse(
+ JOB_RECORD_ID,
+ EtlJobStatus.PENDING,
+ "api/etl/jobs/" + JOB_RECORD_ID
+ )
+ );
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new EtlJobAcceptedResponse(
+ JOB_RECORD_ID,
+ EtlJobStatus.PENDING,
+ "//attacker.example/api/etl/jobs/" + JOB_RECORD_ID
+ )
+ );
+ }
+
+ @Test
+ void coversBothFailedStateFailureCodeShortCircuitForms() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new EtlJobSnapshot(
+ JOB_RECORD_ID,
+ EtlJobStatus.FAILED,
+ 1,
+ " ",
+ CREATED_AT,
+ UPDATED_AT
+ )
+ );
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> new EtlJobStatusResponse(
+ JOB_RECORD_ID,
+ EtlJobStatus.FAILED,
+ 1,
+ null,
+ CREATED_AT,
+ UPDATED_AT
+ )
+ );
+ }
+}