// prepareTestRunCommand contains boiler plate code for testing run command, to avoid duplication.
func prepareTestRunCommand(t *testing.T) (commandInvoker CommandInvoker, cancelFlag task.CancelFlag) {
	cancelFlag = task.NewChanneledCancelFlag()
	commandInvoker = func(commands []string) (stdout io.Reader, stderr io.Reader, exitCode int, errs []error) {
		defer cancelFlag.Set(task.Completed)

		// run command and output to in-memory buffers
		var stdoutBuf bytes.Buffer
		var stderrBuf bytes.Buffer
		workDir := "."
		tempExitCode, err := RunCommand(logger, cancelFlag, workDir, &stdoutBuf, &stderrBuf, defaultExecutionTimeout, commands[0], commands[1:])
		exitCode = tempExitCode

		// record error if any
		if err != nil {
			errs = append(errs, err)
		} else {
			errs = []error{}
		}

		// return readers that read from in-memory buffers
		stdout = bytes.NewReader(stdoutBuf.Bytes())
		stderr = bytes.NewReader(stderrBuf.Bytes())
		return
	}
	return
}
func testProcessSendCommandMessage(t *testing.T, testCase TestCaseSendCommand) {

	cancelFlag := task.NewChanneledCancelFlag()

	// method should call replyBuilder to format the response
	replyBuilderMock := new(MockedReplyBuilder)
	replyBuilderMock.On("BuildReply", mock.Anything, testCase.PluginResults).Return(testCase.ReplyPayload)

	// method should call the proper APIs on the MDS service
	mdsMock := new(MockedMDS)
	var replyPayload string
	mdsMock.On("SendReply", mock.Anything, *testCase.Msg.MessageId, mock.AnythingOfType("string")).Return(nil).Run(func(args mock.Arguments) {
		replyPayload = args.Get(2).(string)
	})
	mdsMock.On("DeleteMessage", mock.Anything, *testCase.Msg.MessageId).Return(nil)

	// create a mock sendResponse function
	sendResponse := func(messageID string, pluginID string, results map[string]*contracts.PluginResult) {
		contextMock := context.NewMockDefault()
		log := contextMock.Log()
		payloadDoc := replyBuilderMock.BuildReply(pluginID, results)
		payloadB, err := json.Marshal(payloadDoc)
		if err != nil {
			return
		}
		payload := string(payloadB)
		// call the mock sendreply so that we can assert the reply sent
		err = mdsMock.SendReply(log, messageID, payload)
	}

	// method should call plugin runner with the given configuration
	pluginRunnerMock := new(MockedPluginRunner)
	// mock.AnythingOfType("func(string, string, map[string]*plugin.Result)")
	pluginRunnerMock.On("RunPlugins", mock.Anything, *testCase.Msg.MessageId, testCase.PluginConfigs, mock.Anything, cancelFlag).Return(testCase.PluginResults)

	// call method under test
	//orchestrationRootDir is set to empty such that it can meet the test expectation.
	orchestrationRootDir := ""
	p := Processor{}
	p.processSendCommandMessage(context.NewMockDefault(), mdsMock, orchestrationRootDir, pluginRunnerMock.RunPlugins, cancelFlag, replyBuilderMock.BuildReply, sendResponse, testCase.Msg)

	// assert that the expectations were met
	pluginRunnerMock.AssertExpectations(t)
	replyBuilderMock.AssertExpectations(t)
	mdsMock.AssertExpectations(t)

	// check that the method sent the right reply
	var parsedReply messageContracts.SendReplyPayload
	err := json.Unmarshal([]byte(replyPayload), &parsedReply)
	assert.Nil(t, err)
	assert.Equal(t, testCase.ReplyPayload, parsedReply)
}
// prepareTestShellCommandExecuter contains boiler plate code for testing shell executer, to avoid duplication.
func prepareTestShellCommandExecuter(t *testing.T) (orchestrationDir string, commandInvoker CommandInvoker, cancelFlag task.CancelFlag) {
	// create shell executer, cancel flag, working dir
	sh := ShellCommandExecuter{}
	cancelFlag = task.NewChanneledCancelFlag()
	orchestrationDir, err := ioutil.TempDir("", "TestShellExecute")
	if err != nil {
		t.Fatal(err)
	}
	workDir := "."

	// commandInvoker calls the shell then sets the state of the flag to completed
	commandInvoker = func(commands []string) (stdout io.Reader, stderr io.Reader, exitCode int, errs []error) {
		defer cancelFlag.Set(task.Completed)
		scriptPath := filepath.Join(orchestrationDir, pluginutil.RunCommandScriptName)
		stdoutFilePath := filepath.Join(orchestrationDir, stdOutFileName)
		stderrFilePath := filepath.Join(orchestrationDir, stdErrFileName)

		// Used to mimic the process
		CreateScriptFile(scriptPath, commands)
		return sh.Execute(logger, workDir, stdoutFilePath, stderrFilePath, cancelFlag, defaultExecutionTimeout, commands[0], commands[1:])
	}

	return
}