Implementierung für M2 vorläufig abgeschlossen
This commit is contained in:
@@ -1,12 +1,28 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.inbound.cli;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.RunBatchProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.BatchRunContext;
|
||||
|
||||
/**
|
||||
* CLI command adapter for batch processing scheduling.
|
||||
* <p>
|
||||
* This class acts as the technical entry point that delegates to the application layer.
|
||||
* This class acts as the inbound technical entry point that exclusively delegates
|
||||
* to the application layer's batch processing use case via the {@link RunBatchProcessingUseCase}
|
||||
* interface. It receives the batch run outcome and makes it available to the Bootstrap layer
|
||||
* for exit code determination and logging.
|
||||
* <p>
|
||||
* AP-003 Implementation: Minimal no-op command to validate the call chain from CLI to Application.
|
||||
* <p>
|
||||
* M2-AP-002 Update: Returns {@link BatchRunOutcome} instead of boolean,
|
||||
* allowing Bootstrap to systematically derive exit codes (AP-007).
|
||||
* <p>
|
||||
* M2-AP-003 Update: Accepts {@link BatchRunContext} and passes it to the use case,
|
||||
* enabling run ID and timing tracking throughout the batch cycle.
|
||||
* <p>
|
||||
* M2-AP-005 Update: Dependency inversion achieved - this adapter depends only on the
|
||||
* RunBatchProcessingUseCase interface, not on any concrete implementation. Bootstrap
|
||||
* is responsible for injecting the appropriate use case implementation.
|
||||
*/
|
||||
public class SchedulerBatchCommand {
|
||||
|
||||
@@ -22,13 +38,14 @@ public class SchedulerBatchCommand {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the batch processing command.
|
||||
* Executes the batch processing command with the given run context.
|
||||
* <p>
|
||||
* AP-003: Delegates to the use case without any additional logic.
|
||||
* Delegates to the use case with the batch run context and returns its structured outcome.
|
||||
*
|
||||
* @return true if execution succeeded, false otherwise
|
||||
* @param context the technical context for this batch run
|
||||
* @return {@link BatchRunOutcome} describing the result of batch processing
|
||||
*/
|
||||
public boolean run() {
|
||||
return useCase.execute();
|
||||
public BatchRunOutcome run(BatchRunContext context) {
|
||||
return useCase.execute(context);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,22 @@
|
||||
/**
|
||||
* CLI adapter for inbound commands.
|
||||
* This package contains the technical entry points that delegate to application use cases.
|
||||
* Inbound adapter for CLI/batch command processing.
|
||||
* <p>
|
||||
* AP-003: Contains minimal command classes for validating the startup path.
|
||||
* This package contains the technical entry points that delegate exclusively to application
|
||||
* use cases via their inbound port interfaces. The adapter enforces strict dependency inversion:
|
||||
* it depends on the application's inbound port contracts, not on concrete implementations.
|
||||
* <p>
|
||||
* Components:
|
||||
* <ul>
|
||||
* <li>{@link de.gecheckt.pdf.umbenenner.adapter.inbound.cli.SchedulerBatchCommand}
|
||||
* — CLI entry point that delegates to RunBatchProcessingUseCase interface (AP-005)</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* M2-AP-005 Architecture:
|
||||
* <ul>
|
||||
* <li>Adapter depends on: {@link de.gecheckt.pdf.umbenenner.application.port.in.RunBatchProcessingUseCase} (interface)</li>
|
||||
* <li>Adapter does not depend on: any concrete use case implementation</li>
|
||||
* <li>Bootstrap wires concrete use case implementation via constructor injection</li>
|
||||
* <li>Dependency direction: Adapter IN → (through port) → Application</li>
|
||||
* </ul>
|
||||
*/
|
||||
package de.gecheckt.pdf.umbenenner.adapter.inbound.cli;
|
||||
@@ -0,0 +1,154 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.inbound.cli;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.BatchRunOutcome;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.in.RunBatchProcessingUseCase;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.BatchRunContext;
|
||||
import de.gecheckt.pdf.umbenenner.domain.model.RunId;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SchedulerBatchCommand}.
|
||||
* <p>
|
||||
* Verifies that the CLI adapter correctly delegates to the batch processing use case
|
||||
* and returns the outcome without transformation.
|
||||
*/
|
||||
class SchedulerBatchCommandTest {
|
||||
|
||||
@Test
|
||||
void constructor_acceptsRunBatchProcessingUseCase() {
|
||||
RunBatchProcessingUseCase mockUseCase = (context) -> BatchRunOutcome.SUCCESS;
|
||||
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(mockUseCase);
|
||||
|
||||
assertNotNull(command, "Constructor should create a valid SchedulerBatchCommand");
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_delegatesToUseCaseAndReturnsOutcome() {
|
||||
// Setup: mock use case that returns SUCCESS
|
||||
RunBatchProcessingUseCase mockUseCase = (context) -> BatchRunOutcome.SUCCESS;
|
||||
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(mockUseCase);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test-run"), Instant.now());
|
||||
|
||||
// Execute
|
||||
BatchRunOutcome outcome = command.run(context);
|
||||
|
||||
// Verify
|
||||
assertEquals(BatchRunOutcome.SUCCESS, outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_passesContextToUseCase() {
|
||||
RunId expectedRunId = new RunId("test-run-123");
|
||||
Instant expectedStartTime = Instant.now();
|
||||
|
||||
// Setup: mock use case that captures the context
|
||||
MockCapturingUseCase mockUseCase = new MockCapturingUseCase();
|
||||
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(mockUseCase);
|
||||
BatchRunContext context = new BatchRunContext(expectedRunId, expectedStartTime);
|
||||
|
||||
// Execute
|
||||
command.run(context);
|
||||
|
||||
// Verify context was passed correctly
|
||||
assertNotNull(mockUseCase.capturedContext);
|
||||
assertEquals(expectedRunId, mockUseCase.capturedContext.runId());
|
||||
assertEquals(expectedStartTime, mockUseCase.capturedContext.startInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_returnsSuccessOutcome() {
|
||||
RunBatchProcessingUseCase successUseCase = (context) -> BatchRunOutcome.SUCCESS;
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(successUseCase);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test"), Instant.now());
|
||||
|
||||
BatchRunOutcome outcome = command.run(context);
|
||||
|
||||
assertEquals(BatchRunOutcome.SUCCESS, outcome);
|
||||
assertTrue(outcome.isSuccess());
|
||||
assertFalse(outcome.isFailure());
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_returnsFailureOutcome() {
|
||||
RunBatchProcessingUseCase failureUseCase = (context) -> BatchRunOutcome.FAILURE;
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(failureUseCase);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test"), Instant.now());
|
||||
|
||||
BatchRunOutcome outcome = command.run(context);
|
||||
|
||||
assertEquals(BatchRunOutcome.FAILURE, outcome);
|
||||
assertTrue(outcome.isFailure());
|
||||
assertFalse(outcome.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_returnsLockUnavailableOutcome() {
|
||||
RunBatchProcessingUseCase lockUnavailableUseCase = (context) -> BatchRunOutcome.LOCK_UNAVAILABLE;
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(lockUnavailableUseCase);
|
||||
BatchRunContext context = new BatchRunContext(new RunId("test"), Instant.now());
|
||||
|
||||
BatchRunOutcome outcome = command.run(context);
|
||||
|
||||
assertEquals(BatchRunOutcome.LOCK_UNAVAILABLE, outcome);
|
||||
assertTrue(outcome.isLockUnavailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_multipleCallsWithDifferentContexts() {
|
||||
RunBatchProcessingUseCase mockUseCase = (context) -> BatchRunOutcome.SUCCESS;
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(mockUseCase);
|
||||
|
||||
BatchRunContext context1 = new BatchRunContext(new RunId("run-1"), Instant.now());
|
||||
BatchRunContext context2 = new BatchRunContext(new RunId("run-2"), Instant.now());
|
||||
|
||||
// Execute multiple times
|
||||
BatchRunOutcome outcome1 = command.run(context1);
|
||||
BatchRunOutcome outcome2 = command.run(context2);
|
||||
|
||||
// Verify both succeed independently
|
||||
assertEquals(BatchRunOutcome.SUCCESS, outcome1);
|
||||
assertEquals(BatchRunOutcome.SUCCESS, outcome2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void run_delegatesWithoutModifyingContext() {
|
||||
RunId runId = new RunId("immutable-test");
|
||||
Instant startTime = Instant.parse("2026-03-31T20:00:00Z");
|
||||
|
||||
RunBatchProcessingUseCase mockUseCase = (context) -> {
|
||||
// Verify context state is not modified
|
||||
assertEquals(runId, context.runId());
|
||||
assertEquals(startTime, context.startInstant());
|
||||
assertFalse(context.isCompleted());
|
||||
return BatchRunOutcome.SUCCESS;
|
||||
};
|
||||
|
||||
SchedulerBatchCommand command = new SchedulerBatchCommand(mockUseCase);
|
||||
BatchRunContext context = new BatchRunContext(runId, startTime);
|
||||
|
||||
command.run(context);
|
||||
// After run, context should still be incomplete (not modified by adapter)
|
||||
assertFalse(context.isCompleted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock use case that captures the passed context for verification.
|
||||
*/
|
||||
private static class MockCapturingUseCase implements RunBatchProcessingUseCase {
|
||||
BatchRunContext capturedContext;
|
||||
|
||||
@Override
|
||||
public BatchRunOutcome execute(BatchRunContext context) {
|
||||
this.capturedContext = context;
|
||||
return BatchRunOutcome.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user