1
0

M3-AP-006: Fehlerklassifikation vereinfacht und Logging auf korrekte

Ergebnisfälle ausgerichtet
This commit is contained in:
2026-04-01 21:45:06 +02:00
parent d60d050948
commit 4d769643d4
13 changed files with 557 additions and 42 deletions

View File

@@ -0,0 +1,195 @@
package de.gecheckt.pdf.umbenenner.application.service;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.M3DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.M3PreCheckFailed;
import de.gecheckt.pdf.umbenenner.domain.model.M3PreCheckPassed;
import de.gecheckt.pdf.umbenenner.domain.model.M3TechnicalDocumentError;
import de.gecheckt.pdf.umbenenner.domain.model.PdfExtractionContentError;
import de.gecheckt.pdf.umbenenner.domain.model.PdfExtractionSuccess;
import de.gecheckt.pdf.umbenenner.domain.model.PdfExtractionTechnicalError;
import de.gecheckt.pdf.umbenenner.domain.model.PdfPageCount;
import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentCandidate;
import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentLocator;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests for {@link M3DocumentProcessingService}.
* <p>
* Verifies that all four M3 document processing outcomes are correctly classified.
*/
class M3DocumentProcessingServiceTest {
@TempDir
Path tempDir;
private SourceDocumentCandidate candidate;
private StartConfiguration configuration;
@BeforeEach
void setUp() throws Exception {
// Create test PDF file
Path pdfFile = tempDir.resolve("document.pdf");
Files.createFile(pdfFile);
SourceDocumentLocator locator = new SourceDocumentLocator(pdfFile.toString());
candidate = new SourceDocumentCandidate("document.pdf", 2048L, locator);
// Create directories and files for configuration
Path sourceDir = Files.createDirectories(tempDir.resolve("source"));
Path targetDir = Files.createDirectories(tempDir.resolve("target"));
Path dbFile = tempDir.resolve("db.sqlite");
Files.createFile(dbFile);
Path promptFile = tempDir.resolve("prompt.txt");
Files.createFile(promptFile);
configuration = new StartConfiguration(
sourceDir,
targetDir,
dbFile,
URI.create("http://localhost:8000"),
"gpt-4",
30,
3,
10,
5000,
promptFile,
tempDir.resolve("lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
);
}
@Test
void testProcessDocument_WithSuccessfulExtraction_AndPassedPreChecks() {
// Arrange: Successful extraction with valid text
var extraction = new PdfExtractionSuccess("This is valid PDF text", new PdfPageCount(5));
// Act
M3DocumentProcessingOutcome outcome = M3DocumentProcessingService.processDocument(
candidate, extraction, configuration);
// Assert: Should produce M3PreCheckPassed
assertInstanceOf(M3PreCheckPassed.class, outcome);
M3PreCheckPassed passed = (M3PreCheckPassed) outcome;
assertEquals(candidate, passed.candidate());
assertEquals(extraction, passed.extraction());
}
@Test
void testProcessDocument_WithSuccessfulExtraction_AndFailedPreCheck_NoUsableText() {
// Arrange: Successful extraction but with only whitespace
var extraction = new PdfExtractionSuccess(" \n \t ", new PdfPageCount(1));
// Act
M3DocumentProcessingOutcome outcome = M3DocumentProcessingService.processDocument(
candidate, extraction, configuration);
// Assert: Should produce M3PreCheckFailed with appropriate reason
assertInstanceOf(M3PreCheckFailed.class, outcome);
M3PreCheckFailed failed = (M3PreCheckFailed) outcome;
assertEquals(candidate, failed.candidate());
assertTrue(failed.failureReason().toLowerCase().contains("usable"));
}
@Test
void testProcessDocument_WithSuccessfulExtraction_AndFailedPreCheck_PageLimitExceeded() {
// Arrange: Successful extraction but exceeds page limit
var extraction = new PdfExtractionSuccess("Valid text content", new PdfPageCount(50));
// Act
M3DocumentProcessingOutcome outcome = M3DocumentProcessingService.processDocument(
candidate, extraction, configuration);
// Assert: Should produce M3PreCheckFailed with page limit reason
assertInstanceOf(M3PreCheckFailed.class, outcome);
M3PreCheckFailed failed = (M3PreCheckFailed) outcome;
assertEquals(candidate, failed.candidate());
assertTrue(failed.failureReason().toLowerCase().contains("page"));
}
@Test
void testProcessDocument_WithContentError() {
// Arrange: PDF content not extractable (classified as technical document error in M3)
var contentError = new PdfExtractionContentError("PDF is corrupted");
// Act
M3DocumentProcessingOutcome outcome = M3DocumentProcessingService.processDocument(
candidate, contentError, configuration);
// Assert: Should produce M3TechnicalDocumentError
assertInstanceOf(M3TechnicalDocumentError.class, outcome);
M3TechnicalDocumentError result = (M3TechnicalDocumentError) outcome;
assertEquals(candidate, result.candidate());
assertTrue(result.errorMessage().contains("PDF is corrupted"));
}
@Test
void testProcessDocument_WithTechnicalError() {
// Arrange: Technical error during extraction
var technicalError = new PdfExtractionTechnicalError("I/O error reading file",
new RuntimeException("File not found"));
// Act
M3DocumentProcessingOutcome outcome = M3DocumentProcessingService.processDocument(
candidate, technicalError, configuration);
// Assert: Should produce M3TechnicalDocumentError
assertInstanceOf(M3TechnicalDocumentError.class, outcome);
M3TechnicalDocumentError result = (M3TechnicalDocumentError) outcome;
assertEquals(candidate, result.candidate());
assertEquals("I/O error reading file", result.errorMessage());
assertNotNull(result.cause());
}
@Test
void testProcessDocument_WithTechnicalError_WithoutCause() {
// Arrange: Technical error without underlying exception
var technicalError = new PdfExtractionTechnicalError("Unknown error", null);
// Act
M3DocumentProcessingOutcome outcome = M3DocumentProcessingService.processDocument(
candidate, technicalError, configuration);
// Assert
assertInstanceOf(M3TechnicalDocumentError.class, outcome);
M3TechnicalDocumentError result = (M3TechnicalDocumentError) outcome;
assertNull(result.cause());
}
@Test
void testProcessDocument_WithNullCandidate_ThrowsException() {
// Arrange
var extraction = new PdfExtractionSuccess("Text", new PdfPageCount(1));
// Act & Assert
assertThrows(NullPointerException.class,
() -> M3DocumentProcessingService.processDocument(null, extraction, configuration));
}
@Test
void testProcessDocument_WithNullExtractionResult_ThrowsException() {
// Act & Assert
assertThrows(NullPointerException.class,
() -> M3DocumentProcessingService.processDocument(candidate, null, configuration));
}
@Test
void testProcessDocument_WithNullConfiguration_ThrowsException() {
// Arrange
var extraction = new PdfExtractionSuccess("Text", new PdfPageCount(1));
// Act & Assert
assertThrows(NullPointerException.class,
() -> M3DocumentProcessingService.processDocument(candidate, extraction, null));
}
}

View File

@@ -1,10 +1,10 @@
package de.gecheckt.pdf.umbenenner.application.service;
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
import de.gecheckt.pdf.umbenenner.domain.model.M3DocumentProcessingOutcome;
import de.gecheckt.pdf.umbenenner.domain.model.M3PreCheckFailed;
import de.gecheckt.pdf.umbenenner.domain.model.M3PreCheckFailureReason;
import de.gecheckt.pdf.umbenenner.domain.model.M3PreCheckPassed;
import de.gecheckt.pdf.umbenenner.domain.model.M3ProcessingDecision;
import de.gecheckt.pdf.umbenenner.domain.model.PdfExtractionSuccess;
import de.gecheckt.pdf.umbenenner.domain.model.PdfPageCount;
import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentCandidate;
@@ -35,7 +35,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Some meaningful text", new PdfPageCount(5));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass when text is usable and page count is valid");
M3PreCheckPassed passed = (M3PreCheckPassed) result;
@@ -49,7 +49,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckFailed, "Should fail with empty text");
M3PreCheckFailed failed = (M3PreCheckFailed) result;
@@ -62,7 +62,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess(" \n\t \r\n ", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckFailed, "Should fail with whitespace-only text");
M3PreCheckFailed failed = (M3PreCheckFailed) result;
@@ -75,7 +75,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#$%^&*()_+-=[]{}|;:',.<>?/", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckFailed, "Should fail with special characters only");
M3PreCheckFailed failed = (M3PreCheckFailed) result;
@@ -88,7 +88,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("a", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass with single letter");
}
@@ -99,7 +99,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("5", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass with single digit");
}
@@ -110,7 +110,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#a$%^&*", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass when letters/digits are present among special chars");
}
@@ -121,7 +121,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess(" meaningful text ", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass when text has meaningful content despite whitespace");
}
@@ -132,7 +132,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(5));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass when page count equals limit (not exceeded)");
}
@@ -143,7 +143,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(6));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckFailed, "Should fail when page count exceeds limit");
M3PreCheckFailed failed = (M3PreCheckFailed) result;
@@ -156,7 +156,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Excellent meaningful text with lots of content", new PdfPageCount(100));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckFailed, "Should fail with page limit exceeded even if text is good");
M3PreCheckFailed failed = (M3PreCheckFailed) result;
@@ -171,7 +171,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(10));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckFailed, "Should fail when both checks fail");
// The specific order of checks doesn't matter for M3; just verify one reason is returned
@@ -216,7 +216,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Äußerst äöüß Großes", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass with German umlauts (ÄÖÜß)");
}
@@ -227,7 +227,7 @@ class M3PreCheckEvaluatorTest {
SourceDocumentCandidate candidate = buildCandidate();
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Αβγδ 中文 καλημέρα", new PdfPageCount(1));
M3ProcessingDecision result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
M3DocumentProcessingOutcome result = M3PreCheckEvaluator.evaluate(candidate, extraction, config);
assertTrue(result instanceof M3PreCheckPassed, "Should pass with Greek, Chinese, and other Unicode letters");
}