fix(ocr): replace IllegalStateException with DomainException.internal in triggerManualSenderTraining

Ensures the unexpected-state path produces a structured JSON error response
instead of an unmapped 500 RuntimeException. Adds OCR_TRAINING_CONFLICT
ErrorCode and mirrors it in the frontend errors.ts. Adds coverage tests for
getAllSenderModels() and runSenderTraining().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-18 09:46:00 +02:00
parent 794000cbd1
commit 2466553216
4 changed files with 61 additions and 2 deletions

View File

@@ -77,6 +77,8 @@ public enum ErrorCode {
OCR_PROCESSING_FAILED,
/** A training run is already in progress. 409 */
TRAINING_ALREADY_RUNNING,
/** Internal inconsistency: expected training run row was not found after creation. 500 */
OCR_TRAINING_CONFLICT,
// --- Tags ---
/** A tag with the given ID does not exist. 404 */

View File

@@ -2,6 +2,8 @@ package org.raddatz.familienarchiv.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.model.OcrTrainingRun;
import org.raddatz.familienarchiv.model.SenderModel;
import org.raddatz.familienarchiv.model.TrainingStatus;
@@ -64,8 +66,9 @@ public class SenderModelService {
boolean runNow = runOrQueueSenderTraining(personId, (int) correctedLines);
TrainingStatus targetStatus = runNow ? TrainingStatus.RUNNING : TrainingStatus.QUEUED;
OcrTrainingRun run = trainingRunRepository.findFirstByPersonIdAndStatus(personId, targetStatus)
.orElseThrow(() -> new IllegalStateException(
"Expected " + targetStatus + " row for person " + personId));
.orElseThrow(() -> DomainException.internal(
ErrorCode.OCR_TRAINING_CONFLICT,
"Expected " + targetStatus + " run for person " + personId));
if (runNow) {
self.runSenderTraining(personId);
}