Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,7 @@ public void validate() throws FlamingockException{
}

// Validate pipeline has stages
if (loadedStages == null || loadedStages.isEmpty()) {
errors.add(new ValidationError("Pipeline must contain at least one stage", "pipeline", "pipeline"));

} else {
if (loadedStages != null) {
loadedStages.stream()
.map(stage -> stage.getValidationErrors(DEFAULT_CONTEXT))
.forEach(errors::addAll);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,20 @@ public void rollback() {
}
}

/**
* Test that a pipeline with no stages should be allowed.
* This test is expected to FAIL initially (TDD approach) because the current implementation
* requires at least one stage to be defined in the pipeline.
*/
@Test
@DisplayName("Should throw an exception when Pipeline.validateAndGetLoadedStages() if no stages")
void shouldThrowExceptionWhenPipelineDoesNotContainStages() {


@DisplayName("Should allow pipeline with no stages")
void shouldAllowPipelineWithNoStages() {
LoadedPipeline emptyPipeline = LoadedPipeline.builder()
.addPreviewPipeline(new PreviewPipeline())
.build();

FlamingockException exception = Assertions.assertThrows(FlamingockException.class, emptyPipeline::validate);

Assertions.assertTrue(exception.getMessage().contains("Pipeline must contain at least one stage"),
"Error message should mention that pipeline must contain at least one stage");

// Expect validation to pass (no exception should be thrown)
Assertions.assertDoesNotThrow(emptyPipeline::validate);
}

@Test
Expand Down Expand Up @@ -107,6 +107,51 @@ void shouldThrowExceptionWhenAllStagesEmpty() {

}

@Test
@DisplayName("Should throw an exception only for the empty stage when mixed with non-empty stages")
void shouldThrowExceptionOnlyForEmptyStageWhenMixedWithNonEmptyStages() {
PreviewMethod executionMethod = new PreviewMethod("apply", Collections.emptyList());

CodePreviewChange validTask = new CodePreviewChange(
"valid-task",
"001",
"test-author",
PipelineTestChange.class.getName(),
PreviewConstructor.getDefault(),
executionMethod,
null,
false,
true,
false,
null,
RecoveryDescriptor.getDefault(),
false);

// Stage with a valid task
PreviewStage stageWithChanges = Mockito.mock(PreviewStage.class);
Mockito.when(stageWithChanges.getType()).thenReturn(StageType.DEFAULT);
Mockito.when(stageWithChanges.getName()).thenReturn("stage-with-changes");
Mockito.when(stageWithChanges.getTasks()).thenReturn((Collection) Collections.singletonList(validTask));

// Stage without any tasks (empty)
PreviewStage emptyStage = getPreviewStage("empty-stage");

PreviewPipeline previewPipeline = new PreviewPipeline();
previewPipeline.setStages(Arrays.asList(stageWithChanges, emptyStage));

LoadedPipeline pipeline = LoadedPipeline.builder()
.addPreviewPipeline(previewPipeline)
.build();

FlamingockException exception = Assertions.assertThrows(FlamingockException.class, pipeline::validate);

// Should only fail for the empty stage
Assertions.assertTrue(exception.getMessage().contains("Stage[empty-stage] must contain at least one task"),
"Error message should mention the empty stage");
Assertions.assertFalse(exception.getMessage().contains("stage-with-changes"),
"Error message should NOT mention the stage with changes");
}


private static PreviewStage getPreviewStage(String name) {
PreviewStage stage = Mockito.mock(PreviewStage.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,10 +672,6 @@ private void validateConfiguration(EnableFlamingock pipelineAnnotation, boolean
throw new RuntimeException("@EnableFlamingock annotation cannot have both configFile and stages configured. Choose one: either specify configFile OR stages.");
}

if (!hasFileInAnnotation && !hasStagesInAnnotation) {
throw new RuntimeException("@EnableFlamingock annotation must specify either configFile OR stages configuration.");
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,29 +127,23 @@ void shouldCreateCorrectPipelineStructureForFileConfiguration() throws Exception
}

/**
* Test error handling for invalid annotation configuration.
* Test that @EnableFlamingock with an explicit empty stages array should be allowed.
* This test is expected to FAIL initially (TDD approach) because the current implementation
* requires at least one stage to be defined.
*/
@Test
@DisplayName("Should throw error for invalid annotation configuration")
void shouldThrowErrorForInvalidAnnotationConfiguration() throws Exception {
// Given - create invalid @EnableFlamingock annotation (neither file nor stages)
EnableFlamingock invalidAnnotation = createMockAnnotationWithNeitherFileNorStages();
@DisplayName("Should allow empty stages array in @EnableFlamingock annotation")
void shouldAllowEmptyStagesArrayInAnnotation() throws Exception {
// Given - create @EnableFlamingock annotation with explicit empty stages array
EnableFlamingock annotation = new MockFlamingockBuilder()
.withStages() // Empty stages array
.build();
Map<String, List<AbstractPreviewTask>> changes = new HashMap<>();
FlamingockAnnotationProcessor processor = new FlamingockAnnotationProcessor();

// When & Then - should throw RuntimeException from the main validation method
Exception exception = assertThrows(Exception.class, () ->
callGetPipelineFromProcessChanges(processor, changes, invalidAnnotation));

// Check if it's wrapped in InvocationTargetException
Throwable cause = exception.getCause();
if (cause instanceof RuntimeException) {
assertTrue(cause.getMessage().contains("must specify either configFile OR stages"),
"Should have error about missing configuration");
} else {
assertTrue(exception.getMessage().contains("must specify either configFile OR stages"),
"Should have error about missing configuration");
}

// When & Then - should NOT throw exception (empty stages should be allowed)

assertDoesNotThrow(() -> callGetPipelineFromProcessChanges(processor, changes, annotation));
}

/**
Expand Down
Loading