Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions etl-service/src/main/java/com/xtrmetl/etl/job/EtlJobSnapshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
* @param jobRecordId opaque durable job identifier
* @param jobStatus current stable lifecycle state
* @param attemptCount number of worker claims recorded for this job
* @param failureCode stable terminal failure code, or {@code null} before failure
* @param failureCode non-blank stable terminal failure code when {@code jobStatus} is
* {@link EtlJobStatus#FAILED}; otherwise {@code null}
* @param createdAt creation timestamp
* @param updatedAt most recent state-change timestamp
*/
Expand All @@ -30,12 +31,12 @@ public record EtlJobSnapshot(
) {

/**
* Validates the immutable status representation.
* Validates the immutable status representation and its lifecycle-dependent failure metadata.
*
* @param jobRecordId opaque durable job identifier
* @param jobStatus current stable lifecycle state
* @param attemptCount non-negative number of worker claims
* @param failureCode stable terminal failure code, or {@code null}
* @param failureCode non-blank stable failure code exactly when the job has failed
* @param createdAt creation timestamp
* @param updatedAt most recent state-change timestamp
*/
Expand All @@ -47,5 +48,16 @@ public record EtlJobSnapshot(
if (attemptCount < 0) {
throw new IllegalArgumentException("attemptCount must not be negative");
}
if (jobStatus == EtlJobStatus.FAILED) {
if (failureCode == null || failureCode.isBlank()) {
throw new IllegalArgumentException(
"failureCode must be non-blank when jobStatus is FAILED"
);
}
} else if (failureCode != null) {
throw new IllegalArgumentException(
"failureCode must be null unless jobStatus is FAILED"
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
* @param jobRecordId opaque durable job identifier
* @param jobStatus current stable lifecycle state
* @param attemptCount number of worker claims recorded for this job
* @param failureCode stable terminal failure code, omitted before failure
* @param failureCode non-blank stable terminal failure code when {@code jobStatus} is
* {@link EtlJobStatus#FAILED}; otherwise omitted as {@code null}
* @param createdAt creation timestamp serialized as an ISO-8601 string
* @param updatedAt most recent state-change timestamp serialized as an ISO-8601 string
*/
Expand All @@ -32,12 +33,12 @@ public record EtlJobStatusResponse(
) {

/**
* Validates the immutable status response.
* Validates the immutable status response and its lifecycle-dependent failure metadata.
*
* @param jobRecordId opaque durable job identifier
* @param jobStatus current stable lifecycle state
* @param attemptCount non-negative worker claim count
* @param failureCode stable terminal failure code, or {@code null}
* @param failureCode non-blank stable failure code exactly when the job has failed
* @param createdAt creation timestamp
* @param updatedAt most recent state-change timestamp
*/
Expand All @@ -49,6 +50,17 @@ public record EtlJobStatusResponse(
if (attemptCount < 0) {
throw new IllegalArgumentException("attemptCount must not be negative");
}
if (jobStatus == EtlJobStatus.FAILED) {
if (failureCode == null || failureCode.isBlank()) {
throw new IllegalArgumentException(
"failureCode must be non-blank when jobStatus is FAILED"
);
}
} else if (failureCode != null) {
throw new IllegalArgumentException(
"failureCode must be null unless jobStatus is FAILED"
);
}
}

/**
Expand Down
68 changes: 68 additions & 0 deletions etl-service/src/test/java/com/xtrmetl/etl/job/EtlJobModelTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ void retainsValidSnapshotsAndMapsThemToStatusResponses() {
assertEquals(UPDATED_AT, response.updatedAt());
}

@Test
void retainsValidFailedSnapshotsAndStatusResponses() {
EtlJobSnapshot snapshot = new EtlJobSnapshot(
JOB_RECORD_ID,
EtlJobStatus.FAILED,
1,
"target_write_failed",
CREATED_AT,
UPDATED_AT
);
EtlJobStatusResponse response = EtlJobStatusResponse.from(snapshot);

assertEquals(EtlJobStatus.FAILED, response.jobStatus());
assertEquals("target_write_failed", response.failureCode());
}

@Test
void rejectsNullRequiredModelValues() {
assertThrows(
Expand Down Expand Up @@ -144,4 +160,56 @@ void rejectsNegativeAttemptCounts() {
)
);
}

@Test
void rejectsFailureCodeOutsideFailedState() {
assertThrows(
IllegalArgumentException.class,
() -> new EtlJobSnapshot(
JOB_RECORD_ID,
EtlJobStatus.SUCCEEDED,
1,
"target_write_failed",
CREATED_AT,
UPDATED_AT
)
);
assertThrows(
IllegalArgumentException.class,
() -> new EtlJobStatusResponse(
JOB_RECORD_ID,
EtlJobStatus.RUNNING,
1,
"target_write_failed",
CREATED_AT,
UPDATED_AT
)
);
}

@Test
void requiresNonBlankFailureCodeForFailedState() {
assertThrows(
IllegalArgumentException.class,
() -> new EtlJobSnapshot(
JOB_RECORD_ID,
EtlJobStatus.FAILED,
1,
null,
CREATED_AT,
UPDATED_AT
)
);
assertThrows(
IllegalArgumentException.class,
() -> new EtlJobStatusResponse(
JOB_RECORD_ID,
EtlJobStatus.FAILED,
1,
" ",
CREATED_AT,
UPDATED_AT
)
);
}
}
Loading