1
0

M8 Abschlussdokumentation und Betriebsdoku final geschärft

This commit is contained in:
2026-04-08 17:09:53 +02:00
parent d61316c699
commit 03689802dd
26 changed files with 75 additions and 118 deletions

View File

@@ -14,7 +14,7 @@ import de.gecheckt.pdf.umbenenner.application.port.out.RunLockUnavailableExcepti
/**
* File-based implementation of {@link RunLockPort} that uses a lock file to prevent concurrent runs.
* <p>
* AP-006 Implementation: Creates an exclusive lock file on acquire and deletes it on release.
* Creates an exclusive lock file on acquire and deletes it on release.
* If the lock file already exists, {@link #acquire()} throws {@link RunLockUnavailableException}
* to signal that another instance is already running.
* <p>

View File

@@ -102,7 +102,7 @@ public class PdfTextExtractionPortAdapter implements PdfTextExtractionPort {
try {
int pageCount = document.getNumberOfPages();
// AP-003: Handle case of zero pages as technical error
// Handle case of zero pages as technical error
// (PdfPageCount requires >= 1, so this is a constraint violation)
if (pageCount < 1) {
return new PdfExtractionTechnicalError(
@@ -124,7 +124,7 @@ public class PdfTextExtractionPortAdapter implements PdfTextExtractionPort {
}
} catch (IOException e) {
// All I/O and PDFBox loading/parsing errors are technical errors in AP-003
// All I/O and PDFBox loading/parsing errors are technical errors
String errorMessage = e.getMessage() != null ? e.getMessage() : e.toString();
return new PdfExtractionTechnicalError(
"Failed to load or parse PDF: " + errorMessage,

View File

@@ -14,7 +14,7 @@ import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentLocator;
/**
* File-system based implementation of {@link SourceDocumentCandidatesPort}.
* <p>
* AP-002 Implementation: Scans a configured source folder and returns only PDF files
* Scans a configured source folder and returns only PDF files
* (by extension) as {@link SourceDocumentCandidate} objects.
* <p>
* Design:
@@ -29,13 +29,11 @@ import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentLocator;
* <p>
* Non-goals:
* <ul>
* <li>No PDF validation (that is AP-003)</li>
* <li>No PDF structure validation</li>
* <li>No recursion into subdirectories</li>
* <li>No content evaluation (that happens in AP-004: brauchbarer Text assessment)</li>
* <li>No content evaluation (text usability is assessed during document processing)</li>
* <li>No fachlich evaluation of candidates</li>
* </ul>
*
* @since M3-AP-002
*/
public class SourceDocumentCandidatesPortAdapter implements SourceDocumentCandidatesPort {

View File

@@ -43,7 +43,7 @@ import de.gecheckt.pdf.umbenenner.application.port.out.PersistenceSchemaInitiali
* <li>Target-copy column ({@code final_target_file_name}) to {@code processing_attempt}</li>
* </ul>
*
* <h2>M4→current-schema status migration</h2>
* <h2>Legacy-state migration</h2>
* <p>
* Documents in an earlier positive intermediate state ({@code SUCCESS} recorded without
* a validated naming proposal) are idempotently migrated to {@code READY_FOR_AI} so that
@@ -178,7 +178,7 @@ public class SqliteSchemaInitializationAdapter implements PersistenceSchemaIniti
};
// -------------------------------------------------------------------------
// M4→current-schema status migration
// Legacy-state status migration
// -------------------------------------------------------------------------
/**

View File

@@ -836,7 +836,7 @@ class StartConfigurationValidatorTest {
}
/**
* M3/AP-007: Focused tests for source folder validation using mocked filesystem checks.
* Focused tests for source folder validation using mocked filesystem checks.
* <p>
* These tests verify the four critical paths for source folder validation without
* relying on platform-dependent filesystem permissions or the actual FS state.
@@ -941,7 +941,7 @@ class StartConfigurationValidatorTest {
);
// Mock: simulate path exists, is directory, but is not readable
// This is the critical M3/AP-007 case that is hard to test on actual FS
// This is the critical case that is hard to test on actual FS
StartConfigurationValidator.SourceFolderChecker mockChecker = path ->
"- source.folder: directory is not readable: " + path;

View File

@@ -19,8 +19,6 @@ import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentLocator;
/**
* Unit tests for {@link Sha256FingerprintAdapter}.
*
* @since M4-AP-002
*/
class Sha256FingerprintAdapterTest {

View File

@@ -28,12 +28,10 @@ import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentLocator;
/**
* Tests for {@link PdfTextExtractionPortAdapter}.
* <p>
* M3-AP-003: Minimal tests validating basic extraction functionality and technical error handling.
* In AP-003 scope: all extraction problems are treated as TechnicalError, not ContentError.
* No fachliche validation of text content (that is AP-004).
* Validates basic extraction functionality and technical error handling.
* All extraction problems are treated as {@link de.gecheckt.pdf.umbenenner.domain.model.PdfExtractionTechnicalError},
* not content errors. Content usability (text quality assessment) is handled in the application layer.
* PDFs are created programmatically using PDFBox to avoid external dependencies on test files.
*
* @since M3-AP-003
*/
class PdfTextExtractionPortAdapterTest {
@@ -170,8 +168,8 @@ class PdfTextExtractionPortAdapterTest {
PdfExtractionResult result = adapter.extractTextAndPageCount(candidate);
// AP-003: Empty text is SUCCESS, not an error
// Fachliche Bewertung of text content happens in AP-004
// Empty text is SUCCESS at extraction level, not an error
// Fachliche Bewertung of text content happens in the application layer
assertInstanceOf(PdfExtractionSuccess.class, result);
PdfExtractionSuccess success = (PdfExtractionSuccess) result;
assertEquals(1, success.pageCount().value());

View File

@@ -20,8 +20,6 @@ import de.gecheckt.pdf.umbenenner.domain.model.SourceDocumentCandidate;
/**
* Tests for {@link SourceDocumentCandidatesPortAdapter}.
*
* @since M3-AP-002
*/
class SourceDocumentCandidatesPortAdapterTest {
@@ -198,7 +196,7 @@ class SourceDocumentCandidatesPortAdapterTest {
@Test
void testLoadCandidates_EmptyPdfFilesAreIncluded() throws IOException {
// Create empty PDF files (M3-AP-002 requirement: PDF-Dateien im Quellordner)
// Create empty PDF files
Files.createFile(tempDir.resolve("empty1.pdf"));
Files.createFile(tempDir.resolve("empty2.pdf"));
// Also add a non-empty PDF for contrast
@@ -207,7 +205,7 @@ class SourceDocumentCandidatesPortAdapterTest {
List<SourceDocumentCandidate> candidates = adapter.loadCandidates();
assertEquals(3, candidates.size(),
"Empty PDF files should be included as candidates; content evaluation happens in AP-004");
"Empty PDF files should be included as candidates; content evaluation happens during document processing");
assertTrue(candidates.stream().allMatch(c -> c.uniqueIdentifier().endsWith(".pdf")),
"All candidates should be PDF files");
}

View File

@@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.*;
* Tests verify transactional semantics: successful commits, rollback on first-write failure,
* rollback on second-write failure, and proper handling of DocumentPersistenceException.
*
* @since M4-AP-006
*/
class SqliteUnitOfWorkAdapterTest {