diff --git a/README.md b/README.md index 27e89dcf..61e675a5 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,26 @@ The run "C1234" can be cancelled using the following command: galasactl runs cancel --name C1234 ``` +## monitors set + +This command can be used to update a monitor in the Galasa service. The name of the monitor to be enabled must be provided using the `--name` flag. + +### Examples + +To enable a monitor named "myCustomMonitor": + +``` +galasactl monitors set --name myCustomMonitor --is-enabled true +``` + +To disable a monitor named "myCustomMonitor": + +``` +galasactl monitors set --name myCustomMonitor --is-enabled false +``` + +For a complete list of supported parameters see [here](./docs/generated/galasactl_monitors_set.md). + ## monitors get This command can be used to get the details of monitors, like resource cleanup monitors, that are available in the Galasa service. diff --git a/docs/generated/errors-list.md b/docs/generated/errors-list.md index 5993883b..e594e363 100644 --- a/docs/generated/errors-list.md +++ b/docs/generated/errors-list.md @@ -222,11 +222,13 @@ The `galasactl` tool can generate the following errors: - GAL1223E: Failed to get monitors. Unexpected http status code {} received from the server. Error details from the server are not in the json format. - GAL1224E: Galasa Monitor named {} is not known on the Galasa service. - GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore). -- GAL1226E: Internal failure. Contents of gzip could be read, but not decoded. New gzip reader failed: file: {} error: {} -- GAL1227E: Internal failure. Contents of gzip could not be decoded. {} error: {} -- GAL1228E: Internal failure. Contents of gzip could not be encoded and compressed. {} error: {} -- GAL1229E: Internal failure. Contents of gzip could not be flushed while encoding and compressing. {} error: {} -- GAL1230E: Internal failure. Gzip file could not be closed while encoding and compressing. {} error: {} +- GAL1226E: Invalid '--is-enabled' value provided. Supported values are 'true' and 'false'. Check your command parameters and try again. +- GAL1227E: Failed to update a monitor named '{}'. Sending the put request to the Galasa service failed. Cause is {} +- GAL1228E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. +- GAL1229E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server could not be read. Cause: {} +- GAL1230E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in a valid json format. Cause: '{}' +- GAL1231E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are: '{}' +- GAL1232E: Failed to update a monitor named '{}'. Unexpected http status code {} received from the server. Error details from the server are not in the json format. - GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '{}', and 'pre-release' repository is '{}' - GAL2501I: Downloaded {} artifacts to folder '{}' diff --git a/docs/generated/galasactl_monitors.md b/docs/generated/galasactl_monitors.md index 3b5bc303..c23ec6d5 100644 --- a/docs/generated/galasactl_monitors.md +++ b/docs/generated/galasactl_monitors.md @@ -26,4 +26,5 @@ The parent command for operations to manipulate monitors in the Galasa service * [galasactl](galasactl.md) - CLI for Galasa * [galasactl monitors get](galasactl_monitors_get.md) - Get monitors from the Galasa service +* [galasactl monitors set](galasactl_monitors_set.md) - Update a monitor in the Galasa service diff --git a/docs/generated/galasactl_monitors_set.md b/docs/generated/galasactl_monitors_set.md new file mode 100644 index 00000000..d6444d34 --- /dev/null +++ b/docs/generated/galasactl_monitors_set.md @@ -0,0 +1,34 @@ +## galasactl monitors set + +Update a monitor in the Galasa service + +### Synopsis + +Updates a monitor with the given name in the Galasa service + +``` +galasactl monitors set [flags] +``` + +### Options + +``` + -h, --help Displays the options for the 'monitors set' command. + --is-enabled string A boolean flag that determines whether the given monitor should be enabled or disabled. Supported values are 'true' and 'false'. + --name string A mandatory flag that identifies the monitor to be manipulated by name. +``` + +### Options inherited from parent commands + +``` + -b, --bootstrap string Bootstrap URL. Should start with 'http://' or 'file://'. If it starts with neither, it is assumed to be a fully-qualified path. If missing, it defaults to use the 'bootstrap.properties' file in your GALASA_HOME. Example: http://example.com/bootstrap, file:///user/myuserid/.galasa/bootstrap.properties , file://C:/Users/myuserid/.galasa/bootstrap.properties + --galasahome string Path to a folder where Galasa will read and write files and configuration settings. The default is '${HOME}/.galasa'. This overrides the GALASA_HOME environment variable which may be set instead. + -l, --log string File to which log information will be sent. Any folder referred to must exist. An existing file will be overwritten. Specify "-" to log to stderr. Defaults to not logging. + --rate-limit-retries int The maximum number of retries that should be made when requests to the Galasa Service fail due to rate limits being exceeded. Must be a whole number. Defaults to 3 retries (default 3) + --rate-limit-retry-backoff-secs float The amount of time in seconds to wait before retrying a command if it failed due to rate limits being exceeded. Defaults to 1 second. (default 1) +``` + +### SEE ALSO + +* [galasactl monitors](galasactl_monitors.md) - Manage monitors in the Galasa service + diff --git a/pkg/cmd/commandCollection.go b/pkg/cmd/commandCollection.go index dad02192..743bc071 100644 --- a/pkg/cmd/commandCollection.go +++ b/pkg/cmd/commandCollection.go @@ -41,6 +41,7 @@ const ( COMMAND_NAME_LOCAL_INIT = "local init" COMMAND_NAME_MONITORS = "monitors" COMMAND_NAME_MONITORS_GET = "monitors get" + COMMAND_NAME_MONITORS_SET = "monitors set" COMMAND_NAME_PROPERTIES = "properties" COMMAND_NAME_PROPERTIES_GET = "properties get" COMMAND_NAME_PROPERTIES_SET = "properties set" @@ -441,16 +442,19 @@ func (commands *commandCollectionImpl) addMonitorsCommands(factory spi.Factory, var err error var monitorsCommand spi.GalasaCommand var monitorsGetCommand spi.GalasaCommand + var monitorsSetCommand spi.GalasaCommand monitorsCommand, err = NewMonitorsCmd(rootCommand, commsFlagSet) if err == nil { monitorsGetCommand, err = NewMonitorsGetCommand(factory, monitorsCommand, commsFlagSet) + monitorsSetCommand, err = NewMonitorsSetCommand(factory, monitorsCommand, commsFlagSet) } if err == nil { commands.commandMap[monitorsCommand.Name()] = monitorsCommand commands.commandMap[monitorsGetCommand.Name()] = monitorsGetCommand + commands.commandMap[monitorsSetCommand.Name()] = monitorsSetCommand } return err diff --git a/pkg/cmd/monitorsSet.go b/pkg/cmd/monitorsSet.go new file mode 100644 index 00000000..21296dda --- /dev/null +++ b/pkg/cmd/monitorsSet.go @@ -0,0 +1,152 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "log" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/monitors" + "github.com/galasa-dev/cli/pkg/spi" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/spf13/cobra" +) + +type MonitorsSetCmdValues struct { + isEnabledStr string +} + +type MonitorsSetCommand struct { + values *MonitorsSetCmdValues + cobraCommand *cobra.Command +} + +// ------------------------------------------------------------------------------------------------ +// Constructors methods +// ------------------------------------------------------------------------------------------------ +func NewMonitorsSetCommand( + factory spi.Factory, + monitorsSetCommand spi.GalasaCommand, + commsFlagSet GalasaFlagSet, +) (spi.GalasaCommand, error) { + + cmd := new(MonitorsSetCommand) + + err := cmd.init(factory, monitorsSetCommand, commsFlagSet) + return cmd, err +} + +// ------------------------------------------------------------------------------------------------ +// Public methods +// ------------------------------------------------------------------------------------------------ +func (cmd *MonitorsSetCommand) Name() string { + return COMMAND_NAME_MONITORS_SET +} + +func (cmd *MonitorsSetCommand) CobraCommand() *cobra.Command { + return cmd.cobraCommand +} + +func (cmd *MonitorsSetCommand) Values() interface{} { + return cmd.values +} + +// ------------------------------------------------------------------------------------------------ +// Private methods +// ------------------------------------------------------------------------------------------------ +func (cmd *MonitorsSetCommand) init(factory spi.Factory, monitorsCommand spi.GalasaCommand, commsFlagSet GalasaFlagSet) error { + var err error + + cmd.values = &MonitorsSetCmdValues{} + cmd.cobraCommand, err = cmd.createCobraCmd(factory, monitorsCommand, commsFlagSet.Values().(*CommsFlagSetValues)) + + return err +} + +func (cmd *MonitorsSetCommand) createCobraCmd( + factory spi.Factory, + monitorsCommand spi.GalasaCommand, + commsFlagSetValues *CommsFlagSetValues, +) (*cobra.Command, error) { + + var err error + + monitorsCommandValues := monitorsCommand.Values().(*MonitorsCmdValues) + monitorsSetCobraCmd := &cobra.Command{ + Use: "set", + Short: "Update a monitor in the Galasa service", + Long: "Updates a monitor with the given name in the Galasa service", + Aliases: []string{COMMAND_NAME_MONITORS_SET}, + RunE: func(cobraCommand *cobra.Command, args []string) error { + return cmd.executeMonitorsSet(factory, monitorsCommand.Values().(*MonitorsCmdValues), commsFlagSetValues) + }, + } + + addMonitorNameFlag(monitorsSetCobraCmd, true, monitorsCommandValues) + isEnabledFlag := "is-enabled" + + monitorsSetCobraCmd.Flags().StringVar(&cmd.values.isEnabledStr, isEnabledFlag, "", "A boolean flag that determines whether the given monitor should be enabled or disabled. Supported values are 'true' and 'false'.") + + monitorsSetCobraCmd.MarkFlagsOneRequired( + isEnabledFlag, + ) + + monitorsCommand.CobraCommand().AddCommand(monitorsSetCobraCmd) + + return monitorsSetCobraCmd, err +} + +func (cmd *MonitorsSetCommand) executeMonitorsSet( + factory spi.Factory, + monitorsCmdValues *MonitorsCmdValues, + commsFlagSetValues *CommsFlagSetValues, +) error { + + var err error + // Operations on the file system will all be relative to the current folder. + fileSystem := factory.GetFileSystem() + + err = utils.CaptureLog(fileSystem, commsFlagSetValues.logFileName) + if err == nil { + commsFlagSetValues.isCapturingLogs = true + + log.Println("Galasa CLI - Update monitors in the Galasa service") + + env := factory.GetEnvironment() + + var galasaHome spi.GalasaHome + galasaHome, err = utils.NewGalasaHome(fileSystem, env, commsFlagSetValues.CmdParamGalasaHomePath) + if err == nil { + + var commsClient api.APICommsClient + commsClient, err = api.NewAPICommsClient( + commsFlagSetValues.bootstrap, + commsFlagSetValues.maxRetries, + commsFlagSetValues.retryBackoffSeconds, + factory, + galasaHome, + ) + + if err == nil { + + byteReader := factory.GetByteReader() + + setMonitorsFunc := func(apiClient *galasaapi.APIClient) error { + return monitors.SetMonitor( + monitorsCmdValues.name, + cmd.values.isEnabledStr, + apiClient, + byteReader, + ) + } + err = commsClient.RunAuthenticatedCommandWithRateLimitRetries(setMonitorsFunc) + } + } + } + + return err +} diff --git a/pkg/cmd/monitorsSet_test.go b/pkg/cmd/monitorsSet_test.go new file mode 100644 index 00000000..bbe398c4 --- /dev/null +++ b/pkg/cmd/monitorsSet_test.go @@ -0,0 +1,97 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package cmd + +import ( + "testing" + + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func TestCommandListContainsMonitorsSetCommand(t *testing.T) { + /// Given... + factory := utils.NewMockFactory() + commands, _ := NewCommandCollection(factory) + + // When... + monitorsCommand, err := commands.GetCommand(COMMAND_NAME_MONITORS_SET) + assert.Nil(t, err) + + // Then... + assert.NotNil(t, monitorsCommand) + assert.Equal(t, COMMAND_NAME_MONITORS_SET, monitorsCommand.Name()) + assert.NotNil(t, monitorsCommand.Values()) + assert.IsType(t, &MonitorsSetCmdValues{}, monitorsCommand.Values()) +} + +func TestMonitorsSetHelpFlagSetCorrectly(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) + + var args []string = []string{"monitors", "set", "--help"} + + // When... + err := commandCollection.Execute(args) + + // Then... + checkOutput("Updates a monitor with the given name in the Galasa service", "", factory, t) + + assert.Nil(t, err) +} + +func TestMonitorsSetWithNameAndIsEnabledFlagsReturnsOk(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) + + var args []string = []string{"monitors", "set", "--name", "myMonitor", "--is-enabled", "true"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.Nil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", "", factory, t) +} + +func TestMonitorsSetNoFlagsReturnsErrorMessage(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) + + var args []string = []string{"monitors", "set"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.NotNil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", `Error: required flag(s) "name" not set`, factory, t) +} + +func TestMonitorsSetWithNameFlagOnlyReturnsErrorMessage(t *testing.T) { + // Given... + factory := utils.NewMockFactory() + commandCollection, _ := setupTestCommandCollection(COMMAND_NAME_MONITORS_SET, factory, t) + + var args []string = []string{"monitors", "set", "--name", "myCustomMonitor"} + + // When... + err := commandCollection.Execute(args) + + // Then... + assert.NotNil(t, err) + + // Check what the user saw is reasonable. + checkOutput("", `Error: at least one of the flags in the group [is-enabled] is required`, factory, t) +} + diff --git a/pkg/errors/errorMessage.go b/pkg/errors/errorMessage.go index d8322492..8355c986 100644 --- a/pkg/errors/errorMessage.go +++ b/pkg/errors/errorMessage.go @@ -430,6 +430,15 @@ var ( // Getting a single monitor by name... GALASA_ERROR_MONITOR_NAME_NOT_FOUND = NewMessageType("GAL1224E: Galasa Monitor named %v is not known on the Galasa service.", 1224, STACK_TRACE_NOT_WANTED) GALASA_ERROR_INVALID_MONITOR_NAME = NewMessageType("GAL1225E: Invalid monitor name provided. The name provided with the --name flag cannot be empty and must only contain characters in the following ranges: 'a'-'z', 'A'-'Z', '0'-'9', '-' (dash), '_' (underscore).", 1225, STACK_TRACE_NOT_WANTED) + + // When updating a monitor... + GALASA_ERROR_INVALID_IS_ENABLED_FLAG = NewMessageType("GAL1226E: Invalid '--is-enabled' value provided. Supported values are 'true' and 'false'. Check your command parameters and try again.", 1226, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_REQUEST_FAILED = NewMessageType("GAL1227E: Failed to update a monitor named '%s'. Sending the put request to the Galasa service failed. Cause is %v", 1227, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_NO_RESPONSE_CONTENT = NewMessageType("GAL1228E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server.", 1228, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_RESPONSE_BODY_UNREADABLE = NewMessageType("GAL1229E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server could not be read. Cause: %s", 1229, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_UNPARSEABLE_CONTENT = NewMessageType("GAL1230E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server are not in a valid json format. Cause: '%s'", 1230, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_SERVER_REPORTED_ERROR = NewMessageType("GAL1231E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server are: '%s'", 1231, STACK_TRACE_NOT_WANTED) + GALASA_ERROR_UPDATE_MONITOR_EXPLANATION_NOT_JSON = NewMessageType("GAL1232E: Failed to update a monitor named '%s'. Unexpected http status code %v received from the server. Error details from the server are not in the json format.", 1232, STACK_TRACE_NOT_WANTED) // Warnings... GALASA_WARNING_MAVEN_NO_GALASA_OBR_REPO = NewMessageType("GAL2000W: Warning: Maven configuration file settings.xml should contain a reference to a Galasa repository so that the galasa OBR can be resolved. The official release repository is '%s', and 'pre-release' repository is '%s'", 2000, STACK_TRACE_WANTED) diff --git a/pkg/monitors/monitorsSet.go b/pkg/monitors/monitorsSet.go new file mode 100644 index 00000000..fb486523 --- /dev/null +++ b/pkg/monitors/monitorsSet.go @@ -0,0 +1,105 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ + +package monitors + +import ( + "context" + "log" + "net/http" + "strconv" + + "github.com/galasa-dev/cli/pkg/embedded" + galasaErrors "github.com/galasa-dev/cli/pkg/errors" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/spi" +) + +func SetMonitor( + monitorName string, + isEnabledStr string, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + + monitorName, err = validateMonitorName(monitorName) + if err == nil { + if isEnabledStr != "" { + err = setMonitorIsEnabledState(monitorName, isEnabledStr, apiClient, byteReader) + } + } + + log.Printf("SetMonitor exiting. err is %v\n", err) + return err +} + +func setMonitorIsEnabledState( + monitorName string, + isEnabledStr string, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + var desiredEnabledState bool + + desiredEnabledState, err = strconv.ParseBool(isEnabledStr) + if err == nil { + err = sendUpdateMonitorStateRequest(monitorName, desiredEnabledState, apiClient, byteReader) + } else { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_INVALID_IS_ENABLED_FLAG) + } + + return err +} + +func sendUpdateMonitorStateRequest( + monitorName string, + isEnabled bool, + apiClient *galasaapi.APIClient, + byteReader spi.ByteReader, +) error { + var err error + var httpResponse *http.Response + var context context.Context = context.Background() + var restApiVersion string + restApiVersion, err = embedded.GetGalasactlRestApiVersion() + + if err == nil { + requestBody := *galasaapi.NewUpdateGalasaMonitorRequest() + monitorData := *galasaapi.NewUpdateGalasaMonitorRequestData() + monitorData.SetIsEnabled(isEnabled) + + requestBody.SetData(monitorData) + + httpResponse, err = apiClient.MonitorsAPIApi.SetMonitorStatus(context, monitorName). + UpdateGalasaMonitorRequest(requestBody). + ClientApiVersion(restApiVersion). + Execute() + + if httpResponse != nil { + defer httpResponse.Body.Close() + } + + if err != nil { + if httpResponse == nil { + err = galasaErrors.NewGalasaError(galasaErrors.GALASA_ERROR_UPDATE_MONITOR_REQUEST_FAILED, err.Error()) + } else { + err = galasaErrors.HttpResponseToGalasaError( + httpResponse, + monitorName, + byteReader, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_NO_RESPONSE_CONTENT, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_RESPONSE_BODY_UNREADABLE, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_UNPARSEABLE_CONTENT, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_SERVER_REPORTED_ERROR, + galasaErrors.GALASA_ERROR_UPDATE_MONITOR_EXPLANATION_NOT_JSON, + ) + } + } + } + return err +} diff --git a/pkg/monitors/monitorsSet_test.go b/pkg/monitors/monitorsSet_test.go new file mode 100644 index 00000000..dcc4bfba --- /dev/null +++ b/pkg/monitors/monitorsSet_test.go @@ -0,0 +1,237 @@ +/* + * Copyright contributors to the Galasa project + * + * SPDX-License-Identifier: EPL-2.0 + */ +package monitors + +import ( + "encoding/json" + "io" + "net/http" + "strconv" + "testing" + + "github.com/galasa-dev/cli/pkg/api" + "github.com/galasa-dev/cli/pkg/galasaapi" + "github.com/galasa-dev/cli/pkg/utils" + "github.com/stretchr/testify/assert" +) + +func readMonitorRequestBody(req *http.Request) galasaapi.UpdateGalasaMonitorRequest { + var monitorUpdateRequest galasaapi.UpdateGalasaMonitorRequest + requestBodyBytes, _ := io.ReadAll(req.Body) + defer req.Body.Close() + + _ = json.Unmarshal(requestBodyBytes, &monitorUpdateRequest) + return monitorUpdateRequest +} + +func TestCanEnableAMonitor(t *testing.T) { + // Given... + monitorName := "customManagerCleanup" + isEnabled := "true" + + // Create the expected HTTP interactions with the API server + putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + requestBody := readMonitorRequestBody(req) + assert.Equal(t, requestBody.Data.GetIsEnabled(), true) + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNoContent) + } + + interactions := []utils.HttpInteraction{ + putMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + monitorName, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.Nil(t, err, "SetMonitor returned an unexpected error") +} + +func TestSetMonitorWithBlankNameDisplaysError(t *testing.T) { + // Given... + monitorName := " " + isEnabled := "true" + + // The client-side validation should fail, so no HTTP interactions will be performed + interactions := []utils.HttpInteraction{} + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + monitorName, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "SetMonitor did not return an error as expected") + consoleOutputText := err.Error() + assert.Contains(t, consoleOutputText, "GAL1225E") + assert.Contains(t, consoleOutputText, " Invalid monitor name provided") +} + +func TestEnableNonExistantMonitorDisplaysError(t *testing.T) { + // Given... + nonExistantMonitor := "monitorDoesNotExist123" + isEnabled := "true" + + // Create the expected HTTP interactions with the API server + setMonitorInteraction := utils.NewHttpInteraction("/monitors/" + nonExistantMonitor, http.MethodPut) + setMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNotFound) + writer.Write([]byte(`{ "error_message": "No such monitor exists" }`)) + } + + interactions := []utils.HttpInteraction{ setMonitorInteraction } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + nonExistantMonitor, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "SetMonitor did not return an error but it should have") + assert.ErrorContains(t, err, "GAL1231E") +} + +func TestSetMonitorFailsWithNoExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { + // Given... + monitorName := "myMonitor" + isEnabled := "true" + + // Create the expected HTTP interactions with the API server + setMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + setMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.WriteHeader(http.StatusInternalServerError) + } + + interactions := []utils.HttpInteraction{ + setMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + monitorName, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "SetMonitor did not return an error but it should have") + assert.ErrorContains(t, err, "GAL1228E") +} + +func TestSetMonitorFailsWithNonJsonContentTypeExplanationErrorPayloadGivesCorrectMessage(t *testing.T) { + // Given... + monitorName := "myMonitor" + isEnabled := "true" + + // Create the expected HTTP interactions with the API server + setMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + setMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + writer.WriteHeader(http.StatusInternalServerError) + writer.Header().Set("Content-Type", "application/notJsonOnPurpose") + writer.Write([]byte("something not json but non-zero-length.")) + } + + interactions := []utils.HttpInteraction{ + setMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + monitorName, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.NotNil(t, err, "SetMonitor did not return an error but it should have") + assert.ErrorContains(t, err, strconv.Itoa(http.StatusInternalServerError)) + assert.ErrorContains(t, err, "GAL1232E") + assert.ErrorContains(t, err, "Error details from the server are not in the json format") +} + +func TestCanDisableAMonitor(t *testing.T) { + // Given... + monitorName := "customManagerCleanup" + isEnabled := "false" + + // Create the expected HTTP interactions with the API server + putMonitorInteraction := utils.NewHttpInteraction("/monitors/" + monitorName, http.MethodPut) + putMonitorInteraction.WriteHttpResponseFunc = func(writer http.ResponseWriter, req *http.Request) { + requestBody := readMonitorRequestBody(req) + assert.Equal(t, requestBody.Data.GetIsEnabled(), false) + + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusNoContent) + } + + interactions := []utils.HttpInteraction{ + putMonitorInteraction, + } + + server := utils.NewMockHttpServer(t, interactions) + defer server.Server.Close() + + apiServerUrl := server.Server.URL + apiClient := api.InitialiseAPI(apiServerUrl) + mockByteReader := utils.NewMockByteReader() + + // When... + err := SetMonitor( + monitorName, + isEnabled, + apiClient, + mockByteReader) + + // Then... + assert.Nil(t, err, "SetMonitor returned an unexpected error") +}