M4 Nachbearbeitung Bootstrap Tests verifiziert und ergänzt
This commit is contained in:
@@ -200,6 +200,50 @@ class PdfTextExtractionPortAdapterTest {
|
||||
assertFalse(error.errorMessage().isBlank(), "TechnicalError message must not be blank");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExtractingLargePdfReturnsSuccess() throws Exception {
|
||||
// Create a large PDF with many pages
|
||||
Path largePdfFile = tempDir.resolve("large.pdf");
|
||||
createMultiPagePdf(largePdfFile, 50);
|
||||
|
||||
SourceDocumentCandidate candidate = new SourceDocumentCandidate(
|
||||
"large.pdf",
|
||||
Files.size(largePdfFile),
|
||||
new SourceDocumentLocator(largePdfFile.toAbsolutePath().toString())
|
||||
);
|
||||
|
||||
PdfExtractionResult result = adapter.extractTextAndPageCount(candidate);
|
||||
|
||||
assertInstanceOf(PdfExtractionSuccess.class, result);
|
||||
PdfExtractionSuccess success = (PdfExtractionSuccess) result;
|
||||
assertEquals(50, success.pageCount().value());
|
||||
assertNotNull(success.extractedText());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPartiallyCorruptedPdfStillReturnsError() throws Exception {
|
||||
// Create a file that starts like PDF but has corrupted content
|
||||
Path partialCorruptFile = tempDir.resolve("partial-corrupt.pdf");
|
||||
byte[] pdfSignature = new byte[] {0x25, 0x50, 0x44, 0x46}; // %PDF in hex
|
||||
byte[] corruptData = "This is corrupted PDF content that will fail parsing".getBytes();
|
||||
byte[] combined = new byte[pdfSignature.length + corruptData.length];
|
||||
System.arraycopy(pdfSignature, 0, combined, 0, pdfSignature.length);
|
||||
System.arraycopy(corruptData, 0, combined, pdfSignature.length, corruptData.length);
|
||||
Files.write(partialCorruptFile, combined);
|
||||
|
||||
SourceDocumentCandidate candidate = new SourceDocumentCandidate(
|
||||
"partial-corrupt.pdf",
|
||||
Files.size(partialCorruptFile),
|
||||
new SourceDocumentLocator(partialCorruptFile.toAbsolutePath().toString())
|
||||
);
|
||||
|
||||
PdfExtractionResult result = adapter.extractTextAndPageCount(candidate);
|
||||
|
||||
assertInstanceOf(PdfExtractionTechnicalError.class, result);
|
||||
PdfExtractionTechnicalError error = (PdfExtractionTechnicalError) result;
|
||||
assertNotNull(error.errorMessage());
|
||||
}
|
||||
|
||||
// --- Helper methods to create test PDFs ---
|
||||
|
||||
/**
|
||||
|
||||
@@ -399,4 +399,158 @@ class SqliteDocumentRecordRepositoryAdapterTest {
|
||||
assertThat(foundRecord.failureCounters().transientErrorCount()).isEqualTo(3);
|
||||
assertThat(foundRecord.lastFailureInstant()).isEqualTo(failureInstant);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByFingerprint_shouldThrowNullPointerException_whenFingerprintIsNull() {
|
||||
// Given
|
||||
DocumentFingerprint nullFingerprint = null;
|
||||
|
||||
// When / Then
|
||||
assertThatThrownBy(() -> repository.findByFingerprint(nullFingerprint))
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_shouldThrowNullPointerException_whenRecordIsNull() {
|
||||
// When / Then
|
||||
assertThatThrownBy(() -> repository.create(null))
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void update_shouldThrowNullPointerException_whenRecordIsNull() {
|
||||
// When / Then
|
||||
assertThatThrownBy(() -> repository.update(null))
|
||||
.isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByFingerprint_shouldReturnDocumentKnownProcessable_whenStatusIsSkippedAlreadyProcessed() {
|
||||
// Given
|
||||
DocumentFingerprint fingerprint = new DocumentFingerprint(
|
||||
"9999999999999999999999999999999999999999999999999999999999999999");
|
||||
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
|
||||
DocumentRecord record = new DocumentRecord(
|
||||
fingerprint,
|
||||
new SourceDocumentLocator("/source/skipped.pdf"),
|
||||
"skipped.pdf",
|
||||
ProcessingStatus.SKIPPED_ALREADY_PROCESSED,
|
||||
FailureCounters.zero(),
|
||||
null,
|
||||
null,
|
||||
now,
|
||||
now
|
||||
);
|
||||
repository.create(record);
|
||||
|
||||
// When
|
||||
DocumentRecordLookupResult result = repository.findByFingerprint(fingerprint);
|
||||
|
||||
// Then: SKIPPED_ALREADY_PROCESSED is not terminal, should be DocumentKnownProcessable
|
||||
assertThat(result).isInstanceOf(DocumentKnownProcessable.class);
|
||||
DocumentKnownProcessable known = (DocumentKnownProcessable) result;
|
||||
assertThat(known.record().overallStatus()).isEqualTo(ProcessingStatus.SKIPPED_ALREADY_PROCESSED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByFingerprint_shouldReturnDocumentKnownProcessable_whenStatusIsSkippedFinalFailure() {
|
||||
// Given
|
||||
DocumentFingerprint fingerprint = new DocumentFingerprint(
|
||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
|
||||
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
|
||||
DocumentRecord record = new DocumentRecord(
|
||||
fingerprint,
|
||||
new SourceDocumentLocator("/source/final-skipped.pdf"),
|
||||
"final-skipped.pdf",
|
||||
ProcessingStatus.SKIPPED_FINAL_FAILURE,
|
||||
new FailureCounters(2, 0),
|
||||
now.minusSeconds(60),
|
||||
null,
|
||||
now,
|
||||
now
|
||||
);
|
||||
repository.create(record);
|
||||
|
||||
// When
|
||||
DocumentRecordLookupResult result = repository.findByFingerprint(fingerprint);
|
||||
|
||||
// Then: SKIPPED_FINAL_FAILURE is not terminal, should be DocumentKnownProcessable
|
||||
assertThat(result).isInstanceOf(DocumentKnownProcessable.class);
|
||||
DocumentKnownProcessable known = (DocumentKnownProcessable) result;
|
||||
assertThat(known.record().overallStatus()).isEqualTo(ProcessingStatus.SKIPPED_FINAL_FAILURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void create_and_update_shouldPreserveNullTimestamps() {
|
||||
// Given: create with null timestamps
|
||||
DocumentFingerprint fingerprint = new DocumentFingerprint(
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
|
||||
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
|
||||
DocumentRecord record = new DocumentRecord(
|
||||
fingerprint,
|
||||
new SourceDocumentLocator("/source/no-timestamps.pdf"),
|
||||
"no-timestamps.pdf",
|
||||
ProcessingStatus.PROCESSING,
|
||||
FailureCounters.zero(),
|
||||
null, // lastFailureInstant is null
|
||||
null, // lastSuccessInstant is null
|
||||
now,
|
||||
now
|
||||
);
|
||||
repository.create(record);
|
||||
|
||||
// When
|
||||
DocumentRecordLookupResult result = repository.findByFingerprint(fingerprint);
|
||||
|
||||
// Then
|
||||
assertThat(result).isInstanceOf(DocumentKnownProcessable.class);
|
||||
DocumentKnownProcessable known = (DocumentKnownProcessable) result;
|
||||
assertThat(known.record().lastFailureInstant()).isNull();
|
||||
assertThat(known.record().lastSuccessInstant()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void update_shouldPreserveCreatedAtTimestamp() {
|
||||
// Given: create with specific createdAt
|
||||
DocumentFingerprint fingerprint = new DocumentFingerprint(
|
||||
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc");
|
||||
Instant createdAt = Instant.now().minusSeconds(1000).truncatedTo(ChronoUnit.MICROS);
|
||||
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
|
||||
|
||||
DocumentRecord initialRecord = new DocumentRecord(
|
||||
fingerprint,
|
||||
new SourceDocumentLocator("/source/test.pdf"),
|
||||
"test.pdf",
|
||||
ProcessingStatus.PROCESSING,
|
||||
FailureCounters.zero(),
|
||||
null,
|
||||
null,
|
||||
createdAt, // Much older createdAt
|
||||
createdAt
|
||||
);
|
||||
repository.create(initialRecord);
|
||||
|
||||
// Update with new timestamps
|
||||
DocumentRecord updated = new DocumentRecord(
|
||||
fingerprint,
|
||||
new SourceDocumentLocator("/source/test.pdf"),
|
||||
"test.pdf",
|
||||
ProcessingStatus.SUCCESS,
|
||||
FailureCounters.zero(),
|
||||
null,
|
||||
now,
|
||||
createdAt, // createdAt should remain unchanged
|
||||
now
|
||||
);
|
||||
|
||||
// When
|
||||
repository.update(updated);
|
||||
DocumentRecordLookupResult result = repository.findByFingerprint(fingerprint);
|
||||
|
||||
// Then: createdAt should be preserved
|
||||
assertThat(result).isInstanceOf(DocumentTerminalSuccess.class);
|
||||
DocumentTerminalSuccess success = (DocumentTerminalSuccess) result;
|
||||
assertThat(success.record().createdAt()).isEqualTo(createdAt);
|
||||
assertThat(success.record().updatedAt()).isEqualTo(now);
|
||||
}
|
||||
}
|
||||
@@ -179,4 +179,15 @@ class SqliteUnitOfWorkAdapterTest {
|
||||
assertTrue(lookupResult instanceof de.gecheckt.pdf.umbenenner.application.port.out.DocumentUnknown,
|
||||
"DocumentRecord should be rolled back on runtime exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that null operations Consumer throws NullPointerException.
|
||||
*/
|
||||
@Test
|
||||
void executeInTransaction_throwsNullPointerExceptionForNullOperations() {
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
unitOfWorkAdapter.executeInTransaction(null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user