Optimierung: Anwendungskonfiguration auf Minimalbedarf zugeschnitten
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.config;
|
||||
|
||||
/**
|
||||
* Minimal runtime configuration for the application layer.
|
||||
* <p>
|
||||
* Contains only the application-level runtime parameters that are needed during
|
||||
* batch document processing. Technical infrastructure configuration (paths, API keys,
|
||||
* persistence parameters, etc.) is kept in the bootstrap layer.
|
||||
* <p>
|
||||
* This intentionally small contract ensures the application layer depends only on
|
||||
* the configuration values it actually uses, following hexagonal architecture principles.
|
||||
*/
|
||||
public record RuntimeConfiguration(
|
||||
/**
|
||||
* Maximum number of pages a document can have to be processed.
|
||||
* Documents exceeding this limit are rejected during pre-checks.
|
||||
*/
|
||||
int maxPages
|
||||
)
|
||||
{ }
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.service;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
|
||||
@@ -48,23 +48,23 @@ public class DocumentProcessingService {
|
||||
*
|
||||
* @param candidate the document candidate to process
|
||||
* @param extractionResult the result from PDF extraction (from {@link de.gecheckt.pdf.umbenenner.application.port.out.PdfTextExtractionPort})
|
||||
* @param configuration the startup configuration (used for pre-check validation)
|
||||
* @param runtimeConfig the runtime configuration (used for pre-check validation)
|
||||
* @return the complete processing outcome (implementing {@link DocumentProcessingOutcome})
|
||||
* @throws NullPointerException if any parameter is null
|
||||
*/
|
||||
public static DocumentProcessingOutcome processDocument(
|
||||
SourceDocumentCandidate candidate,
|
||||
PdfExtractionResult extractionResult,
|
||||
StartConfiguration configuration) {
|
||||
RuntimeConfiguration runtimeConfig) {
|
||||
|
||||
Objects.requireNonNull(candidate, "candidate must not be null");
|
||||
Objects.requireNonNull(extractionResult, "extractionResult must not be null");
|
||||
Objects.requireNonNull(configuration, "configuration must not be null");
|
||||
Objects.requireNonNull(runtimeConfig, "runtimeConfig must not be null");
|
||||
|
||||
return switch (extractionResult) {
|
||||
case PdfExtractionSuccess success ->
|
||||
// Extraction succeeded: evaluate pre-checks
|
||||
PreCheckEvaluator.evaluate(candidate, success, configuration);
|
||||
PreCheckEvaluator.evaluate(candidate, success, runtimeConfig);
|
||||
|
||||
case PdfExtractionContentError contentError ->
|
||||
// PDF content not extractable: classify as pre-check failed (deterministic content error)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.service;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
|
||||
@@ -44,18 +44,18 @@ public class PreCheckEvaluator {
|
||||
*
|
||||
* @param candidate the source document metadata
|
||||
* @param extraction the successfully extracted PDF content
|
||||
* @param configuration the startup configuration (used for maxPages limit)
|
||||
* @param runtimeConfig the runtime configuration (used for maxPages limit)
|
||||
* @return the pre-check outcome: passed or failed with reason (both implement {@link DocumentProcessingOutcome})
|
||||
* @throws NullPointerException if any parameter is null
|
||||
*/
|
||||
public static DocumentProcessingOutcome evaluate(
|
||||
SourceDocumentCandidate candidate,
|
||||
PdfExtractionSuccess extraction,
|
||||
StartConfiguration configuration) {
|
||||
RuntimeConfiguration runtimeConfig) {
|
||||
|
||||
Objects.requireNonNull(candidate, "candidate must not be null");
|
||||
Objects.requireNonNull(extraction, "extraction must not be null");
|
||||
Objects.requireNonNull(configuration, "configuration must not be null");
|
||||
Objects.requireNonNull(runtimeConfig, "runtimeConfig must not be null");
|
||||
|
||||
// Pre-check 1: Verify document has usable text
|
||||
if (!hasUsableText(extraction.extractedText())) {
|
||||
@@ -66,7 +66,7 @@ public class PreCheckEvaluator {
|
||||
}
|
||||
|
||||
// Pre-check 2: Verify document page count does not exceed configured limit
|
||||
if (extraction.pageCount().exceedsLimit(configuration.maxPages())) {
|
||||
if (extraction.pageCount().exceedsLimit(runtimeConfig.maxPages())) {
|
||||
return new PreCheckFailed(
|
||||
candidate,
|
||||
PreCheckFailureReason.PAGE_LIMIT_EXCEEDED
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.usecase;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.FingerprintPort;
|
||||
@@ -77,7 +77,7 @@ import java.util.Objects;
|
||||
*/
|
||||
public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCase {
|
||||
|
||||
private final StartConfiguration configuration;
|
||||
private final RuntimeConfiguration runtimeConfiguration;
|
||||
private final RunLockPort runLockPort;
|
||||
private final SourceDocumentCandidatesPort sourceDocumentCandidatesPort;
|
||||
private final PdfTextExtractionPort pdfTextExtractionPort;
|
||||
@@ -86,13 +86,13 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
|
||||
private final ProcessingLogger logger;
|
||||
|
||||
/**
|
||||
* Creates the batch use case with the already-loaded startup configuration and all
|
||||
* required ports for the flow.
|
||||
* Creates the batch use case with the runtime configuration and all required ports for the flow.
|
||||
* <p>
|
||||
* The configuration is loaded and validated by Bootstrap before use case creation;
|
||||
* the use case receives the result directly and does not re-read the properties file.
|
||||
* The runtime configuration contains only the application-level settings needed
|
||||
* during batch processing. Technical infrastructure configuration (paths, persistence,
|
||||
* API details, etc.) is managed by Bootstrap.
|
||||
*
|
||||
* @param configuration the validated startup configuration; must not be null
|
||||
* @param runtimeConfiguration the runtime configuration containing application-level settings; must not be null
|
||||
* @param runLockPort for exclusive run locking; must not be null
|
||||
* @param sourceDocumentCandidatesPort for loading PDF candidates from the source folder;
|
||||
* must not be null
|
||||
@@ -106,14 +106,14 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
|
||||
* @throws NullPointerException if any parameter is null
|
||||
*/
|
||||
public DefaultBatchRunProcessingUseCase(
|
||||
StartConfiguration configuration,
|
||||
RuntimeConfiguration runtimeConfiguration,
|
||||
RunLockPort runLockPort,
|
||||
SourceDocumentCandidatesPort sourceDocumentCandidatesPort,
|
||||
PdfTextExtractionPort pdfTextExtractionPort,
|
||||
FingerprintPort fingerprintPort,
|
||||
DocumentProcessingCoordinator documentProcessingCoordinator,
|
||||
ProcessingLogger logger) {
|
||||
this.configuration = Objects.requireNonNull(configuration, "configuration must not be null");
|
||||
this.runtimeConfiguration = Objects.requireNonNull(runtimeConfiguration, "runtimeConfiguration must not be null");
|
||||
this.runLockPort = Objects.requireNonNull(runLockPort, "runLockPort must not be null");
|
||||
this.sourceDocumentCandidatesPort = Objects.requireNonNull(
|
||||
sourceDocumentCandidatesPort, "sourceDocumentCandidatesPort must not be null");
|
||||
@@ -142,8 +142,6 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
|
||||
return BatchRunOutcome.LOCK_UNAVAILABLE;
|
||||
}
|
||||
|
||||
logger.debug("Configuration in use: source={}, target={}",
|
||||
configuration.sourceFolder(), configuration.targetFolder());
|
||||
logger.info("Batch run started. RunId: {}, Start: {}",
|
||||
context.runId(), context.startInstant());
|
||||
|
||||
@@ -301,7 +299,7 @@ public class DefaultBatchRunProcessingUseCase implements BatchRunProcessingUseCa
|
||||
logExtractionResult(candidate, extractionResult);
|
||||
|
||||
DocumentProcessingOutcome outcome =
|
||||
DocumentProcessingService.processDocument(candidate, extractionResult, configuration);
|
||||
DocumentProcessingService.processDocument(candidate, extractionResult, runtimeConfiguration);
|
||||
|
||||
logProcessingOutcome(candidate, outcome);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.service;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
|
||||
@@ -34,7 +34,7 @@ class DocumentProcessingServiceTest {
|
||||
Path tempDir;
|
||||
|
||||
private SourceDocumentCandidate candidate;
|
||||
private StartConfiguration configuration;
|
||||
private RuntimeConfiguration runtimeConfig;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
@@ -44,30 +44,8 @@ class DocumentProcessingServiceTest {
|
||||
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"
|
||||
);
|
||||
// Create runtime configuration with maxPages limit
|
||||
runtimeConfig = new RuntimeConfiguration(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,7 +55,7 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act
|
||||
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
|
||||
candidate, extraction, configuration);
|
||||
candidate, extraction, runtimeConfig);
|
||||
|
||||
// Assert: Should produce PreCheckPassed
|
||||
assertInstanceOf(PreCheckPassed.class, outcome);
|
||||
@@ -93,7 +71,7 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act
|
||||
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
|
||||
candidate, extraction, configuration);
|
||||
candidate, extraction, runtimeConfig);
|
||||
|
||||
// Assert: Should produce PreCheckFailed with appropriate reason
|
||||
assertInstanceOf(PreCheckFailed.class, outcome);
|
||||
@@ -109,7 +87,7 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act
|
||||
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
|
||||
candidate, extraction, configuration);
|
||||
candidate, extraction, runtimeConfig);
|
||||
|
||||
// Assert: Should produce PreCheckFailed with page limit reason
|
||||
assertInstanceOf(PreCheckFailed.class, outcome);
|
||||
@@ -125,7 +103,7 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act
|
||||
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
|
||||
candidate, contentError, configuration);
|
||||
candidate, contentError, runtimeConfig);
|
||||
|
||||
// Assert: Should produce PreCheckFailed
|
||||
assertInstanceOf(PreCheckFailed.class, outcome);
|
||||
@@ -142,7 +120,7 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act
|
||||
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
|
||||
candidate, technicalError, configuration);
|
||||
candidate, technicalError, runtimeConfig);
|
||||
|
||||
// Assert: Should produce TechnicalDocumentError
|
||||
assertInstanceOf(TechnicalDocumentError.class, outcome);
|
||||
@@ -159,7 +137,7 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act
|
||||
DocumentProcessingOutcome outcome = DocumentProcessingService.processDocument(
|
||||
candidate, technicalError, configuration);
|
||||
candidate, technicalError, runtimeConfig);
|
||||
|
||||
// Assert
|
||||
assertInstanceOf(TechnicalDocumentError.class, outcome);
|
||||
@@ -174,14 +152,14 @@ class DocumentProcessingServiceTest {
|
||||
|
||||
// Act & Assert
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> DocumentProcessingService.processDocument(null, extraction, configuration));
|
||||
() -> DocumentProcessingService.processDocument(null, extraction, runtimeConfig));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testProcessDocument_WithNullExtractionResult_ThrowsException() {
|
||||
// Act & Assert
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> DocumentProcessingService.processDocument(candidate, null, configuration));
|
||||
() -> DocumentProcessingService.processDocument(candidate, null, runtimeConfig));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.service;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.DocumentProcessingOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailed;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.PreCheckFailureReason;
|
||||
@@ -31,7 +31,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWhenDocumentHasUsableTextAndValidPageCount() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Some meaningful text", new PdfPageCount(5));
|
||||
|
||||
@@ -45,7 +45,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_failsWithNoUsableTextWhenExtractedTextIsEmpty() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(1));
|
||||
|
||||
@@ -58,7 +58,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_failsWithNoUsableTextWhenTextIsOnlyWhitespace() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess(" \n\t \r\n ", new PdfPageCount(1));
|
||||
|
||||
@@ -71,7 +71,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_failsWithNoUsableTextWhenTextContainsOnlySpecialCharacters() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#$%^&*()_+-=[]{}|;:',.<>?/", new PdfPageCount(1));
|
||||
|
||||
@@ -84,7 +84,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWithTextContainingSingleLetter() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("a", new PdfPageCount(1));
|
||||
|
||||
@@ -95,7 +95,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWithTextContainingSingleDigit() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("5", new PdfPageCount(1));
|
||||
|
||||
@@ -106,7 +106,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWithTextMixedWithSpecialCharactersIfLettersOrDigitsPresent() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("!@#a$%^&*", new PdfPageCount(1));
|
||||
|
||||
@@ -117,7 +117,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWithWhitespaceAroundUsableText() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess(" meaningful text ", new PdfPageCount(1));
|
||||
|
||||
@@ -128,7 +128,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_failsWithPageLimitExceededWhenPageCountEqualsLimit() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(5));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(5));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(5));
|
||||
|
||||
@@ -139,7 +139,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_failsWithPageLimitExceededWhenPageCountExceedsLimit() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(5));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(5));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(6));
|
||||
|
||||
@@ -152,7 +152,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_failsWithPageLimitExceededEvenIfTextIsValid() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(2));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(2));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Excellent meaningful text with lots of content", new PdfPageCount(100));
|
||||
|
||||
@@ -167,7 +167,7 @@ class PreCheckEvaluatorTest {
|
||||
void evaluate_prefersPageLimitCheckOverTextCheck() throws Exception {
|
||||
// If both checks fail, page limit check should take precedence (not tested for priority,
|
||||
// but we verify that one failure is reported consistently)
|
||||
StartConfiguration config = buildConfig(maxPages(1));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(1));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("", new PdfPageCount(10));
|
||||
|
||||
@@ -181,7 +181,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_throwsNullPointerExceptionWhenCandidateIsNull() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Valid text", new PdfPageCount(1));
|
||||
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
@@ -191,7 +191,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_throwsNullPointerExceptionWhenExtractionIsNull() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
@@ -211,7 +211,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWithUnicodeGermanUmlauts() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Äußerst äöüß Großes", new PdfPageCount(1));
|
||||
|
||||
@@ -222,7 +222,7 @@ class PreCheckEvaluatorTest {
|
||||
|
||||
@Test
|
||||
void evaluate_passesWithOtherUnicodeCharacters() throws Exception {
|
||||
StartConfiguration config = buildConfig(maxPages(10));
|
||||
RuntimeConfiguration config = buildConfig(maxPages(10));
|
||||
SourceDocumentCandidate candidate = buildCandidate();
|
||||
PdfExtractionSuccess extraction = new PdfExtractionSuccess("Αβγδ 中文 καλημέρα", new PdfPageCount(1));
|
||||
|
||||
@@ -235,30 +235,8 @@ class PreCheckEvaluatorTest {
|
||||
// Helpers
|
||||
// =========================================================================
|
||||
|
||||
private StartConfiguration buildConfig(int maxPages) throws Exception {
|
||||
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);
|
||||
|
||||
return new StartConfiguration(
|
||||
sourceDir,
|
||||
targetDir,
|
||||
dbFile,
|
||||
URI.create("https://api.example.com"),
|
||||
"gpt-4",
|
||||
30,
|
||||
3,
|
||||
maxPages,
|
||||
50000,
|
||||
promptFile,
|
||||
tempDir.resolve("lock.lock"),
|
||||
tempDir.resolve("logs"),
|
||||
"INFO",
|
||||
"test-key"
|
||||
);
|
||||
private RuntimeConfiguration buildConfig(int maxPages) throws Exception {
|
||||
return new RuntimeConfiguration(maxPages);
|
||||
}
|
||||
|
||||
private int maxPages(int limit) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.usecase;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecord;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentRecordLookupResult;
|
||||
@@ -71,7 +71,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_successfullyAcquiresAndReleasesLock() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
DefaultBatchRunProcessingUseCase useCase = buildUseCase(
|
||||
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
|
||||
@@ -88,7 +88,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_returnsLockUnavailableWhenLockCannotBeAcquired() throws Exception {
|
||||
CountingRunLockPort lockPort = new CountingRunLockPort(true);
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
DefaultBatchRunProcessingUseCase useCase = buildUseCase(
|
||||
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
|
||||
@@ -108,7 +108,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_doesNotReleaseLockWhenAcquireFails() throws Exception {
|
||||
CountingRunLockPort lockPort = new CountingRunLockPort(true);
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
DefaultBatchRunProcessingUseCase useCase = buildUseCase(
|
||||
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
|
||||
@@ -125,7 +125,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_releasesLockEvenOnUnexpectedError() throws Exception {
|
||||
ErrorAfterAcquireLockPort lockPort = new ErrorAfterAcquireLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
DefaultBatchRunProcessingUseCase useCase = buildUseCase(
|
||||
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
|
||||
@@ -146,7 +146,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_withNoCandidates_returnsSuccess() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
DefaultBatchRunProcessingUseCase useCase = buildUseCase(
|
||||
config, lockPort, new EmptyCandidatesPort(), new NoOpExtractionPort(),
|
||||
@@ -161,7 +161,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_happyPath_candidatePassesPreChecks_persistenceInvoked() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("document.pdf");
|
||||
PdfExtractionSuccess success = new PdfExtractionSuccess("Invoice text", new PdfPageCount(1));
|
||||
@@ -184,7 +184,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_noUsableText_candidateEndsControlled_batchContinues() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("image-only.pdf");
|
||||
PdfExtractionSuccess emptySuccess = new PdfExtractionSuccess(" ", new PdfPageCount(1));
|
||||
@@ -206,7 +206,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_pageLimitExceeded_candidateEndsControlled_batchContinues() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("big.pdf");
|
||||
PdfExtractionSuccess manyPages = new PdfExtractionSuccess("Some text", new PdfPageCount(10));
|
||||
@@ -228,7 +228,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_extractionContentError_candidateEndsControlled_batchContinues() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("encrypted.pdf");
|
||||
PdfExtractionContentError contentError = new PdfExtractionContentError("PDF is encrypted");
|
||||
@@ -250,7 +250,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_extractionTechnicalError_candidateEndsControlled_batchContinues() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("corrupt.pdf");
|
||||
PdfExtractionTechnicalError technicalError = new PdfExtractionTechnicalError("I/O error reading file", null);
|
||||
@@ -272,7 +272,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_sourceAccessException_returnsFailure() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidatesPort failingPort = () -> {
|
||||
throw new SourceDocumentAccessException("Source folder not readable");
|
||||
@@ -297,7 +297,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_fingerprintFailure_candidateNotHistorised_batchContinues() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("unreadable.pdf");
|
||||
FixedCandidatesPort candidatesPort = new FixedCandidatesPort(List.of(candidate));
|
||||
@@ -322,7 +322,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_fingerprintFailure_extractionNotCalled() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate candidate = makeCandidate("unreadable.pdf");
|
||||
FixedCandidatesPort candidatesPort = new FixedCandidatesPort(List.of(candidate));
|
||||
@@ -350,7 +350,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_mixedBatch_allOutcomeTypes_batchOverallSucceeds() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
SourceDocumentCandidate goodCandidate = makeCandidate("good.pdf");
|
||||
SourceDocumentCandidate noTextCandidate = makeCandidate("notext.pdf");
|
||||
@@ -400,7 +400,7 @@ class BatchRunProcessingUseCaseTest {
|
||||
@Test
|
||||
void execute_multipleCandidates_allProcessed_batchSucceeds() throws Exception {
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
StartConfiguration config = buildConfig(tempDir);
|
||||
RuntimeConfiguration config = buildConfig(tempDir);
|
||||
|
||||
List<SourceDocumentCandidate> candidates = List.of(
|
||||
makeCandidate("a.pdf"),
|
||||
@@ -429,41 +429,20 @@ class BatchRunProcessingUseCaseTest {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static DefaultBatchRunProcessingUseCase buildUseCase(
|
||||
StartConfiguration config,
|
||||
RuntimeConfiguration runtimeConfig,
|
||||
RunLockPort lockPort,
|
||||
SourceDocumentCandidatesPort candidatesPort,
|
||||
PdfTextExtractionPort extractionPort,
|
||||
FingerprintPort fingerprintPort,
|
||||
DocumentProcessingCoordinator processor) {
|
||||
return new DefaultBatchRunProcessingUseCase(
|
||||
config, lockPort, candidatesPort, extractionPort, fingerprintPort, processor,
|
||||
runtimeConfig, lockPort, candidatesPort, extractionPort, fingerprintPort, processor,
|
||||
new NoOpProcessingLogger());
|
||||
}
|
||||
|
||||
private static StartConfiguration buildConfig(Path tempDir) throws Exception {
|
||||
Path sourceDir = Files.createDirectories(tempDir.resolve("source"));
|
||||
Path targetDir = Files.createDirectories(tempDir.resolve("target"));
|
||||
Path dbFile = tempDir.resolve("db.sqlite");
|
||||
if (!Files.exists(dbFile)) Files.createFile(dbFile);
|
||||
Path promptFile = tempDir.resolve("prompt.txt");
|
||||
if (!Files.exists(promptFile)) Files.createFile(promptFile);
|
||||
|
||||
return new StartConfiguration(
|
||||
sourceDir,
|
||||
targetDir,
|
||||
dbFile,
|
||||
URI.create("https://api.example.com"),
|
||||
"gpt-4",
|
||||
30,
|
||||
3, // maxRetries
|
||||
3, // maxPages (low limit – useful for page-limit tests)
|
||||
50000,
|
||||
promptFile,
|
||||
tempDir.resolve("lock.lock"),
|
||||
tempDir.resolve("logs"),
|
||||
"INFO",
|
||||
"test-key"
|
||||
);
|
||||
private static RuntimeConfiguration buildConfig(Path tempDir) throws Exception {
|
||||
// maxPages set to 3 – useful for page-limit tests
|
||||
return new RuntimeConfiguration(3);
|
||||
}
|
||||
|
||||
private static SourceDocumentCandidate makeCandidate(String filename) {
|
||||
|
||||
Reference in New Issue
Block a user