Implementierung für M2 vorläufig abgeschlossen
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.usecase;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.StartConfiguration;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.RunLockPort;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.RunLockUnavailableException;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.BatchRunContext;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.RunId;
|
||||
|
||||
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 java.time.Instant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link M2BatchRunProcessingUseCase}.
|
||||
* <p>
|
||||
* Verifies correct orchestration of the M2 batch cycle including lock management,
|
||||
* configuration loading, and controlled execution flow.
|
||||
*/
|
||||
class M2BatchRunProcessingUseCaseTest {
|
||||
|
||||
@TempDir
|
||||
Path tempDir;
|
||||
|
||||
@Test
|
||||
void execute_successfullyAcquiresAndReleasesLock() {
|
||||
// Setup mock ports that track invocations
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
MockConfigurationPort configPort = new MockConfigurationPort(tempDir);
|
||||
|
||||
M2BatchRunProcessingUseCase useCase = new M2BatchRunProcessingUseCase(configPort, lockPort);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test-run-1"), Instant.now());
|
||||
|
||||
// Execute
|
||||
BatchRunOutcome outcome = useCase.execute(context);
|
||||
|
||||
// Verify lock lifecycle
|
||||
assertTrue(lockPort.wasAcquireCalled(), "Lock acquire should be called");
|
||||
assertTrue(lockPort.wasReleaseCalled(), "Lock release should be called");
|
||||
assertTrue(outcome.isSuccess(), "Batch should complete successfully");
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_returnsLockUnavailableWhenLockCannotBeAcquired() {
|
||||
// Setup: lock port that fails to acquire (another instance is running)
|
||||
RunLockPort lockPort = new RunLockPort() {
|
||||
@Override
|
||||
public void acquire() {
|
||||
throw new RunLockUnavailableException("Another instance already running");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
// Nothing to release
|
||||
}
|
||||
};
|
||||
|
||||
ConfigurationPort configPort = new MockConfigurationPort(tempDir);
|
||||
M2BatchRunProcessingUseCase useCase = new M2BatchRunProcessingUseCase(configPort, lockPort);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test-run-2"), Instant.now());
|
||||
|
||||
// Execute
|
||||
BatchRunOutcome outcome = useCase.execute(context);
|
||||
|
||||
// AP-007: lock unavailable is a distinct, controlled early-termination outcome
|
||||
assertTrue(outcome.isLockUnavailable(), "Outcome should be LOCK_UNAVAILABLE when lock cannot be acquired");
|
||||
assertTrue(outcome.isFailure(), "LOCK_UNAVAILABLE also reports as failure for exit code derivation");
|
||||
assertFalse(outcome.isSuccess(), "Batch should not succeed when lock unavailable");
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_releasesLockEvenOnError() {
|
||||
// Setup: mock lock port that tracks release calls
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
|
||||
// Config port that throws exception
|
||||
ConfigurationPort configPort = () -> {
|
||||
throw new RuntimeException("Configuration loading error");
|
||||
};
|
||||
|
||||
M2BatchRunProcessingUseCase useCase = new M2BatchRunProcessingUseCase(configPort, lockPort);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test-run-3"), Instant.now());
|
||||
|
||||
// Execute (expect failure due to config error)
|
||||
BatchRunOutcome outcome = useCase.execute(context);
|
||||
|
||||
// Verify lock is still released despite error
|
||||
assertTrue(lockPort.wasReleaseCalled(), "Lock should be released even on configuration error");
|
||||
assertTrue(outcome.isFailure(), "Batch should fail");
|
||||
}
|
||||
|
||||
@Test
|
||||
void execute_loadsConfigurationDuringExecution() {
|
||||
// Setup
|
||||
MockRunLockPort lockPort = new MockRunLockPort();
|
||||
MockConfigurationPort configPort = new MockConfigurationPort(tempDir);
|
||||
|
||||
M2BatchRunProcessingUseCase useCase = new M2BatchRunProcessingUseCase(configPort, lockPort);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test-run-4"), Instant.now());
|
||||
|
||||
// Execute
|
||||
BatchRunOutcome outcome = useCase.execute(context);
|
||||
|
||||
// Verify configuration was loaded
|
||||
assertTrue(configPort.wasLoadConfigurationCalled(), "Configuration should be loaded");
|
||||
assertTrue(outcome.isSuccess(), "Batch should succeed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock ConfigurationPort for testing.
|
||||
*/
|
||||
private static class MockConfigurationPort implements ConfigurationPort {
|
||||
private final Path tempDir;
|
||||
private boolean loadConfigurationCalled = false;
|
||||
|
||||
MockConfigurationPort(Path tempDir) {
|
||||
this.tempDir = tempDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StartConfiguration loadConfiguration() {
|
||||
loadConfigurationCalled = true;
|
||||
|
||||
try {
|
||||
Path sourceDir = Files.createDirectories(tempDir.resolve("source"));
|
||||
Path targetDir = Files.createDirectories(tempDir.resolve("target"));
|
||||
Path dbFile = Files.createFile(tempDir.resolve("db.sqlite"));
|
||||
Path promptFile = Files.createFile(tempDir.resolve("prompt.txt"));
|
||||
|
||||
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",
|
||||
"test-key"
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create mock configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
boolean wasLoadConfigurationCalled() {
|
||||
return loadConfigurationCalled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock RunLockPort for testing.
|
||||
*/
|
||||
private static class MockRunLockPort implements RunLockPort {
|
||||
private boolean acquireCalled = false;
|
||||
private boolean releaseCalled = false;
|
||||
|
||||
@Override
|
||||
public void acquire() {
|
||||
acquireCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
releaseCalled = true;
|
||||
}
|
||||
|
||||
boolean wasAcquireCalled() {
|
||||
return acquireCalled;
|
||||
}
|
||||
|
||||
boolean wasReleaseCalled() {
|
||||
return releaseCalled;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user