1
0

Umsetzung von Meilenstein M7

This commit is contained in:
2026-04-07 17:26:02 +02:00
parent ffd91c766d
commit e9e9b2d17a
30 changed files with 2328 additions and 206 deletions

View File

@@ -30,6 +30,7 @@ import de.gecheckt.pdf.umbenenner.application.config.RuntimeConfiguration;
import de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration;
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.AiContentSensitivity;
import de.gecheckt.pdf.umbenenner.application.port.out.AiInvocationPort;
import de.gecheckt.pdf.umbenenner.application.port.out.ClockPort;
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
@@ -204,7 +205,7 @@ public class BootstrapRunner {
this.schemaInitPortFactory = SqliteSchemaInitializationAdapter::new;
this.useCaseFactory = (startConfig, lock) -> {
// Extract runtime configuration from startup configuration
RuntimeConfiguration runtimeConfig = new RuntimeConfiguration(startConfig.maxPages());
RuntimeConfiguration runtimeConfig = new RuntimeConfiguration(startConfig.maxPages(), startConfig.maxRetriesTransient(), resolveAiContentSensitivity(startConfig.logAiSensitive()));
String jdbcUrl = buildJdbcUrl(startConfig);
FingerprintPort fingerprintPort = new Sha256FingerprintAdapter();
@@ -218,7 +219,9 @@ public class BootstrapRunner {
TargetFolderPort targetFolderPort = new FilesystemTargetFolderAdapter(startConfig.targetFolder());
TargetFileCopyPort targetFileCopyPort = new FilesystemTargetFileCopyAdapter(startConfig.targetFolder());
DocumentProcessingCoordinator documentProcessingCoordinator =
new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository, unitOfWorkPort, targetFolderPort, targetFileCopyPort, coordinatorLogger);
new DocumentProcessingCoordinator(documentRecordRepository, processingAttemptRepository,
unitOfWorkPort, targetFolderPort, targetFileCopyPort, coordinatorLogger,
startConfig.maxRetriesTransient());
// Wire AI naming pipeline
AiInvocationPort aiInvocationPort = new OpenAiHttpAdapter(startConfig);
@@ -408,6 +411,23 @@ public class BootstrapRunner {
}
}
/**
* Derives the {@link AiContentSensitivity} decision from the raw {@code log.ai.sensitive}
* configuration flag.
* <p>
* The safe default is {@link AiContentSensitivity#PROTECT_SENSITIVE_CONTENT}.
* {@link AiContentSensitivity#LOG_SENSITIVE_CONTENT} is only produced when
* {@code logAiSensitive} is explicitly {@code true}.
*
* @param logAiSensitive the parsed boolean value of the {@code log.ai.sensitive} property
* @return the appropriate sensitivity decision; never {@code null}
*/
static AiContentSensitivity resolveAiContentSensitivity(boolean logAiSensitive) {
return logAiSensitive
? AiContentSensitivity.LOG_SENSITIVE_CONTENT
: AiContentSensitivity.PROTECT_SENSITIVE_CONTENT;
}
/**
* Builds the JDBC URL for the SQLite database from the configured file path.
*

View File

@@ -7,6 +7,7 @@ import de.gecheckt.pdf.umbenenner.adapter.out.configuration.ConfigurationLoading
import de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration;
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.AiContentSensitivity;
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
import de.gecheckt.pdf.umbenenner.application.port.out.DocumentPersistenceException;
import de.gecheckt.pdf.umbenenner.application.port.out.PersistenceSchemaInitializationPort;
@@ -60,7 +61,8 @@ class BootstrapRunnerEdgeCasesTest {
null, // null runtimeLockFile
tempDir.resolve("logs"),
"INFO",
"test-key"
"test-key",
false
);
AtomicReference<Path> capturedLockPath = new AtomicReference<>();
@@ -106,7 +108,8 @@ class BootstrapRunnerEdgeCasesTest {
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
"test-key",
false
);
String jdbcUrl = BootstrapRunner.buildJdbcUrl(config);
@@ -132,7 +135,8 @@ class BootstrapRunnerEdgeCasesTest {
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
"test-key",
false
);
String jdbcUrl = BootstrapRunner.buildJdbcUrl(config);
@@ -159,7 +163,8 @@ class BootstrapRunnerEdgeCasesTest {
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-key"
"test-key",
false
);
// Verify BatchRunContext can be created (used internally by BootstrapRunner)
@@ -223,7 +228,7 @@ class BootstrapRunnerEdgeCasesTest {
return new StartConfiguration(sourceDir, targetDir, dbFile,
URI.create("https://api.example.com"), "gpt-4", 30, 3, 100, 50000,
promptFile, tempDir.resolve("lock.lock"), tempDir.resolve("logs"),
"INFO", "key");
"INFO", "key", false);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -319,6 +324,24 @@ class BootstrapRunnerEdgeCasesTest {
assertEquals(1, runner.run(), "LOCK_UNAVAILABLE outcome should map to exit code 1");
}
// =========================================================================
// AI Content Sensitivity Resolution
// =========================================================================
@Test
void resolveAiContentSensitivity_falseYieldsProtectSensitiveContent() {
assertEquals(AiContentSensitivity.PROTECT_SENSITIVE_CONTENT,
BootstrapRunner.resolveAiContentSensitivity(false),
"logAiSensitive=false must resolve to PROTECT_SENSITIVE_CONTENT (safe default)");
}
@Test
void resolveAiContentSensitivity_trueYieldsLogSensitiveContent() {
assertEquals(AiContentSensitivity.LOG_SENSITIVE_CONTENT,
BootstrapRunner.resolveAiContentSensitivity(true),
"logAiSensitive=true must resolve to LOG_SENSITIVE_CONTENT");
}
// =========================================================================
// Mocks
// =========================================================================
@@ -364,7 +387,8 @@ class BootstrapRunnerEdgeCasesTest {
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-api-key"
"test-api-key",
false
);
} catch (Exception e) {
throw new RuntimeException("Failed to create mock configuration", e);

View File

@@ -186,7 +186,8 @@ class BootstrapRunnerTest {
Paths.get(""), // empty simulates unconfigured runtime.lock.file
tempDir.resolve("logs"),
"INFO",
"test-key"
"test-key",
false
);
AtomicReference<Path> capturedLockPath = new AtomicReference<>();
@@ -319,7 +320,8 @@ class BootstrapRunnerTest {
tempDir.resolve("lock.lock"),
tempDir.resolve("logs"),
"INFO",
"test-api-key"
"test-api-key",
false
);
} catch (Exception e) {
throw new RuntimeException("Failed to create mock configuration", e);