@SuppressWarnings({"PMD.AvoidInstantiatingObjectsInLoops"})
  public void validate(Workflow workflow, User caller) {
    try {
      RunProperties runProperties = new RunProperties();
      runProperties.setOwner(caller);
      Map<String, ParamDefinition> workflowParams = workflow.getParams();
      Map<String, ParamDefinition> defaultDryRunParams =
          defaultParamManager.getDefaultDryRunParams();
      // add run params to override params with known invalid defaults
      Map<String, ParamDefinition> filteredParams =
          defaultDryRunParams.entrySet().stream()
              .filter(
                  entry ->
                      workflowParams != null
                          && workflowParams.containsKey(entry.getKey())
                          && workflowParams.get(entry.getKey()).getType()
                              == entry.getValue().getType())
              .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

      Initiator initiator = new ValidationInitiator();
      initiator.setCaller(caller);
      RunRequest runRequest =
          RunRequest.builder()
              .initiator(initiator)
              .currentPolicy(RunPolicy.START_FRESH_NEW_RUN)
              .runParams(filteredParams)
              .build();

      WorkflowInstance workflowInstance =
          workflowHelper.createWorkflowInstance(workflow, 1L, 1L, runProperties, runRequest);
      WorkflowSummary workflowSummary =
          workflowHelper.createWorkflowSummaryFromInstance(workflowInstance);

      // todo: improve to traverse in DAG order to validate steps and their params
      for (Step step : workflow.getSteps()) {
        StepRuntimeSummary runtimeSummary =
            StepRuntimeSummary.builder()
                .stepId(step.getId())
                .stepAttemptId(1L)
                .stepInstanceId(1L)
                .stepInstanceUuid(UUID.randomUUID().toString())
                .stepName(StepHelper.getStepNameOrDefault(step))
                .tags(step.getTags())
                .type(step.getType())
                .subType(step.getSubType())
                .params(new LinkedHashMap<>())
                .transition(StepInstanceTransition.from(step))
                .synced(true)
                .dependencies(Collections.emptyMap())
                .build();
        paramsManager.generateMergedStepParams(
            workflowSummary, step, stepRuntimeMap.get(step.getType()), runtimeSummary);
      }
    } catch (Exception e) {
      throw new MaestroDryRunException(
          e,
          "Exception during dry run validation for workflow %s Error=[%s] Type=[%s] StackTrace=[%s]",
          workflow.getId(),
          e.getMessage(),
          e.getClass(),
          ExceptionHelper.getStackTrace(e, MAX_STACKTRACE_LINES));
    }
  }
@Test
  public void testValidatePass() {
    when(paramsManager.generateMergedWorkflowParams(any(), any()))
        .thenReturn(new LinkedHashMap<>());
    when(paramsManager.generateMergedStepParams(any(), any(), any(), any()))
        .thenReturn(new LinkedHashMap<>());
    dryRunValidator.validate(definition.getWorkflow(), user);
  }