1
0

Implementierung für M2 vorläufig abgeschlossen

This commit is contained in:
2026-03-31 21:52:48 +02:00
parent 301f1acf08
commit 9d66a446b3
29 changed files with 1892 additions and 52 deletions

View File

@@ -0,0 +1,175 @@
package de.gecheckt.pdf.umbenenner.domain.model;
import org.junit.jupiter.api.Test;
import java.time.Instant;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for {@link BatchRunContext}.
* <p>
* Verifies correct modeling of batch run lifecycle including initialization,
* timestamp tracking, completion marking, and state validation.
*/
class BatchRunContextTest {
@Test
void constructor_createsContextWithRunIdAndStartTime() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.now();
BatchRunContext context = new BatchRunContext(runId, startTime);
assertNotNull(context);
assertEquals(runId, context.runId());
assertEquals(startTime, context.startInstant());
}
@Test
void constructor_throwsNullPointerExceptionWhenRunIdIsNull() {
Instant startTime = Instant.now();
assertThrows(NullPointerException.class, () -> new BatchRunContext(null, startTime),
"Constructor should throw NullPointerException when RunId is null");
}
@Test
void constructor_throwsNullPointerExceptionWhenStartInstantIsNull() {
RunId runId = new RunId("test-run");
assertThrows(NullPointerException.class, () -> new BatchRunContext(runId, null),
"Constructor should throw NullPointerException when start instant is null");
}
@Test
void runId_returnsTheConstructorRunId() {
RunId expectedRunId = new RunId("run-123");
Instant startTime = Instant.now();
BatchRunContext context = new BatchRunContext(expectedRunId, startTime);
assertEquals(expectedRunId, context.runId());
}
@Test
void startInstant_returnsTheConstructorStartTime() {
RunId runId = new RunId("test-run");
Instant expectedStartTime = Instant.parse("2026-03-31T20:00:00Z");
BatchRunContext context = new BatchRunContext(runId, expectedStartTime);
assertEquals(expectedStartTime, context.startInstant());
}
@Test
void endInstant_isNullImmediatelyAfterConstruction() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.now();
BatchRunContext context = new BatchRunContext(runId, startTime);
assertNull(context.endInstant(), "End instant should be null immediately after construction");
}
@Test
void isCompleted_returnsFalseWhenEndInstantNotSet() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.now();
BatchRunContext context = new BatchRunContext(runId, startTime);
assertFalse(context.isCompleted(), "isCompleted should return false when end instant is not set");
}
@Test
void setEndInstant_setsTheEndTime() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.parse("2026-03-31T20:00:00Z");
Instant endTime = Instant.parse("2026-03-31T20:01:00Z");
BatchRunContext context = new BatchRunContext(runId, startTime);
context.setEndInstant(endTime);
assertEquals(endTime, context.endInstant());
}
@Test
void setEndInstant_throwsNullPointerExceptionWhenEndInstantIsNull() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.now();
BatchRunContext context = new BatchRunContext(runId, startTime);
assertThrows(NullPointerException.class, () -> context.setEndInstant(null),
"setEndInstant should throw NullPointerException when end instant is null");
}
@Test
void setEndInstant_throwsIllegalStateExceptionWhenAlreadySet() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.now();
Instant firstEndTime = Instant.now().plusSeconds(60);
Instant secondEndTime = Instant.now().plusSeconds(120);
BatchRunContext context = new BatchRunContext(runId, startTime);
context.setEndInstant(firstEndTime);
assertThrows(IllegalStateException.class, () -> context.setEndInstant(secondEndTime),
"setEndInstant should throw IllegalStateException when called a second time");
}
@Test
void isCompleted_returnsTrueAfterSetEndInstant() {
RunId runId = new RunId("test-run");
Instant startTime = Instant.now();
Instant endTime = Instant.now().plusSeconds(60);
BatchRunContext context = new BatchRunContext(runId, startTime);
context.setEndInstant(endTime);
assertTrue(context.isCompleted(), "isCompleted should return true after setting end instant");
}
@Test
void toString_containsRunIdAndTimestamps() {
RunId runId = new RunId("test-run-001");
Instant startTime = Instant.parse("2026-03-31T20:00:00Z");
BatchRunContext context = new BatchRunContext(runId, startTime);
String representation = context.toString();
assertTrue(representation.contains("test-run-001"), "toString should contain RunId");
assertTrue(representation.contains("2026-03-31"), "toString should contain timestamp information");
}
@Test
void contextLifecycle_startToCompletion() {
RunId runId = new RunId("test-run-lifecycle");
Instant startTime = Instant.parse("2026-03-31T20:00:00Z");
Instant endTime = Instant.parse("2026-03-31T20:05:00Z");
// Create context
BatchRunContext context = new BatchRunContext(runId, startTime);
assertFalse(context.isCompleted());
// Complete context
context.setEndInstant(endTime);
assertTrue(context.isCompleted());
// Verify all data
assertEquals(runId, context.runId());
assertEquals(startTime, context.startInstant());
assertEquals(endTime, context.endInstant());
}
@Test
void multipleContextsAreIndependent() {
RunId runId1 = new RunId("run-1");
RunId runId2 = new RunId("run-2");
Instant startTime1 = Instant.parse("2026-03-31T20:00:00Z");
Instant startTime2 = Instant.parse("2026-03-31T21:00:00Z");
BatchRunContext context1 = new BatchRunContext(runId1, startTime1);
BatchRunContext context2 = new BatchRunContext(runId2, startTime2);
Instant endTime1 = Instant.parse("2026-03-31T20:05:00Z");
context1.setEndInstant(endTime1);
assertTrue(context1.isCompleted());
assertFalse(context2.isCompleted(), "Context2 should not be affected by Context1's completion");
}
}

View File

@@ -0,0 +1,91 @@
package de.gecheckt.pdf.umbenenner.domain.model;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for {@link ProcessingStatus} enumeration.
* <p>
* Verifies that all required status values are present and correctly defined
* for M2 and future milestones.
*/
class ProcessingStatusTest {
@Test
void allRequiredStatusValuesExist() {
// Verify all status values required by the architecture are present
assertNotNull(ProcessingStatus.SUCCESS);
assertNotNull(ProcessingStatus.FAILED_RETRYABLE);
assertNotNull(ProcessingStatus.FAILED_FINAL);
assertNotNull(ProcessingStatus.SKIPPED_ALREADY_PROCESSED);
assertNotNull(ProcessingStatus.SKIPPED_FINAL_FAILURE);
assertNotNull(ProcessingStatus.PROCESSING);
}
@Test
void successStatus_isDefinedAndAccessible() {
ProcessingStatus status = ProcessingStatus.SUCCESS;
assertEquals(ProcessingStatus.SUCCESS, status);
}
@Test
void failedRetryableStatus_isDefinedAndAccessible() {
ProcessingStatus status = ProcessingStatus.FAILED_RETRYABLE;
assertEquals(ProcessingStatus.FAILED_RETRYABLE, status);
}
@Test
void failedFinalStatus_isDefinedAndAccessible() {
ProcessingStatus status = ProcessingStatus.FAILED_FINAL;
assertEquals(ProcessingStatus.FAILED_FINAL, status);
}
@Test
void skippedAlreadyProcessedStatus_isDefinedAndAccessible() {
ProcessingStatus status = ProcessingStatus.SKIPPED_ALREADY_PROCESSED;
assertEquals(ProcessingStatus.SKIPPED_ALREADY_PROCESSED, status);
}
@Test
void skippedFinalFailureStatus_isDefinedAndAccessible() {
ProcessingStatus status = ProcessingStatus.SKIPPED_FINAL_FAILURE;
assertEquals(ProcessingStatus.SKIPPED_FINAL_FAILURE, status);
}
@Test
void processingStatus_isDefinedAndAccessible() {
ProcessingStatus status = ProcessingStatus.PROCESSING;
assertEquals(ProcessingStatus.PROCESSING, status);
}
@Test
void statusEquality_worksByReference() {
// Enums have identity-based equality
assertTrue(ProcessingStatus.SUCCESS == ProcessingStatus.SUCCESS);
assertFalse(ProcessingStatus.SUCCESS == ProcessingStatus.FAILED_FINAL);
}
@Test
void statusCanBeUsedInSwitch() {
ProcessingStatus status = ProcessingStatus.FAILED_RETRYABLE;
String result = "";
switch (status) {
case SUCCESS -> result = "success";
case FAILED_RETRYABLE -> result = "retryable";
case FAILED_FINAL -> result = "final";
case SKIPPED_ALREADY_PROCESSED -> result = "skip-processed";
case SKIPPED_FINAL_FAILURE -> result = "skip-failed";
case PROCESSING -> result = "processing";
}
assertEquals("retryable", result);
}
@Test
void statusValues_areSixInTotal() {
ProcessingStatus[] values = ProcessingStatus.values();
assertEquals(6, values.length, "ProcessingStatus should have exactly 6 values");
}
}

View File

@@ -0,0 +1,146 @@
package de.gecheckt.pdf.umbenenner.domain.model;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for {@link RunId} value object.
* <p>
* Verifies immutability, value semantics, validation, and correct behavior
* as a unique identifier for batch runs.
*/
class RunIdTest {
@Test
void constructor_createsRunIdWithValidValue() {
RunId runId = new RunId("test-run-123");
assertNotNull(runId);
assertEquals("test-run-123", runId.value());
}
@Test
void constructor_throwsNullPointerExceptionWhenValueIsNull() {
assertThrows(NullPointerException.class, () -> new RunId(null),
"Constructor should throw NullPointerException for null value");
}
@Test
void constructor_throwsIllegalArgumentExceptionWhenValueIsEmpty() {
assertThrows(IllegalArgumentException.class, () -> new RunId(""),
"Constructor should throw IllegalArgumentException for empty value");
}
@Test
void constructor_throwsIllegalArgumentExceptionWhenValueIsOnlyWhitespace() {
assertThrows(IllegalArgumentException.class, () -> new RunId(" "),
"Constructor should throw IllegalArgumentException for whitespace-only value");
}
@Test
void value_returnsTheConstructorValue() {
String expectedValue = "run-id-456";
RunId runId = new RunId(expectedValue);
assertEquals(expectedValue, runId.value());
}
@Test
void equals_returnsTrueForIdenticalValues() {
RunId runId1 = new RunId("same-id");
RunId runId2 = new RunId("same-id");
assertEquals(runId1, runId2);
}
@Test
void equals_returnsFalseForDifferentValues() {
RunId runId1 = new RunId("id-1");
RunId runId2 = new RunId("id-2");
assertNotEquals(runId1, runId2);
}
@Test
void equals_returnsFalseWhenComparedWithNull() {
RunId runId = new RunId("test-id");
assertNotEquals(runId, null);
assertFalse(runId.equals(null));
}
@Test
void equals_returnsFalseWhenComparedWithDifferentType() {
RunId runId = new RunId("test-id");
assertNotEquals(runId, "test-id");
assertFalse(runId.equals("test-id"));
}
@Test
void hashCode_isSameForIdenticalValues() {
RunId runId1 = new RunId("same-id");
RunId runId2 = new RunId("same-id");
assertEquals(runId1.hashCode(), runId2.hashCode());
}
@Test
void hashCode_isDifferentForDifferentValues() {
RunId runId1 = new RunId("id-1");
RunId runId2 = new RunId("id-2");
// Note: this is not guaranteed for different values, but likely
assertNotEquals(runId1.hashCode(), runId2.hashCode());
}
@Test
void toString_returnsTheValue() {
String value = "run-id-789";
RunId runId = new RunId(value);
assertEquals(value, runId.toString());
}
@Test
void runIdCanBeUsedInCollections() {
RunId runId1 = new RunId("id-1");
RunId runId2 = new RunId("id-1");
RunId runId3 = new RunId("id-2");
var set = new java.util.HashSet<RunId>();
set.add(runId1);
set.add(runId2); // Should not add duplicate
set.add(runId3);
// runId1 and runId2 are equal, so set should have 2 elements
assertEquals(2, set.size());
}
@Test
void runIdCanBeMapKey() {
RunId runId1 = new RunId("id-1");
RunId runId2 = new RunId("id-1");
var map = new java.util.HashMap<RunId, String>();
map.put(runId1, "first");
map.put(runId2, "second");
// runId1 and runId2 are equal, so second put should overwrite
assertEquals(1, map.size());
assertEquals("second", map.get(runId1));
}
@Test
void constructor_acceptsUuidFormatValue() {
String uuidValue = "550e8400-e29b-41d4-a716-446655440000";
RunId runId = new RunId(uuidValue);
assertEquals(uuidValue, runId.value());
}
@Test
void constructor_acceptsTimestampFormatValue() {
String timestampValue = "2026-03-31T21:41:48.000Z";
RunId runId = new RunId(timestampValue);
assertEquals(timestampValue, runId.value());
}
@Test
void constructor_acceptsSequentialNumberValue() {
String sequentialValue = "00001";
RunId runId = new RunId(sequentialValue);
assertEquals(sequentialValue, runId.value());
}
}