From 4c03e5553c79e39965e9639ef6abc7d9b9e60415 Mon Sep 17 00:00:00 2001 From: Lesia Date: Sat, 18 Mar 2023 17:45:36 +0300 Subject: [PATCH] Update event subscription (#241) --- redfish/eventdestination.go | 150 ++++++++++--- redfish/eventservice.go | 54 ++++- redfish/eventservice_test.go | 425 ++++++++++++++++++++++------------- 3 files changed, 441 insertions(+), 188 deletions(-) diff --git a/redfish/eventdestination.go b/redfish/eventdestination.go index c4e1951d..fbf25a9b 100644 --- a/redfish/eventdestination.go +++ b/redfish/eventdestination.go @@ -272,20 +272,50 @@ func GetEventDestination(c common.Client, uri string) (*EventDestination, error) // subscriptionPayload is the payload to create the event subscription type subscriptionPayload struct { - Destination string `json:"Destination"` - EventTypes []EventType `json:"EventTypes"` - HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` - Oem interface{} `json:"Oem,omitempty"` - Protocol EventDestinationProtocol `json:"Protocol,omitempty"` - Context string `json:"Context,omitempty"` + Destination string `json:"Destination,omitempty"` + EventTypes []EventType `json:"EventTypes,omitempty"` + RegistryPrefixes []string `json:"RegistryPrefixes,omitempty"` + ResourceTypes []string `json:"ResourceTypes,omitempty"` + DeliveryRetryPolicy DeliveryRetryPolicy `json:"DeliveryRetryPolicy,omitempty"` + HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` + Oem interface{} `json:"Oem,omitempty"` + Protocol EventDestinationProtocol `json:"Protocol,omitempty"` + Context string `json:"Context,omitempty"` } // validateCreateEventDestinationParams will validate // CreateEventDestination parameters + +// Deprecated: (v1.5) EventType-based eventing is DEPRECATED in the Redfish schema +// in favor of using RegistryPrefix and ResourceTypes func validateCreateEventDestinationParams( uri string, destination string, + protocol EventDestinationProtocol, + context string, eventTypes []EventType, +) error { + // validate event types + if len(eventTypes) == 0 { + return fmt.Errorf("at least one event type for subscription should be defined") + } + + for _, et := range eventTypes { + if !et.IsValidEventType() { + return fmt.Errorf("invalid event type") + } + } + + return validateCreateEventDestinationMandatoryParams(uri, destination, protocol, context) +} + +// validateCreateEventDestinationMandatoryParams will validate +// mandatory parameters for CreateEventDestination HTTP POST request +func validateCreateEventDestinationMandatoryParams( + uri string, + destination string, + protocol EventDestinationProtocol, + context string, ) error { // validate uri if strings.TrimSpace(uri) == "" { @@ -297,19 +327,23 @@ func validateCreateEventDestinationParams( return fmt.Errorf("empty destination is not valid") } - if !strings.HasPrefix(destination, "http") { + u, err := url.ParseRequestURI(destination) + if err != nil { + return err + } + + if !strings.HasPrefix(u.Scheme, "http") { return fmt.Errorf("destination should start with http") } - // validate event types - if len(eventTypes) == 0 { - return fmt.Errorf("at least one event type for subscription should be defined") + // validate protocol + if strings.TrimSpace(string(protocol)) == "" { + return fmt.Errorf("the required property protocol should be defined") } - for _, et := range eventTypes { - if !et.IsValidEventType() { - return fmt.Errorf("invalid event type") - } + // validate context + if strings.TrimSpace(context) == "" { + return fmt.Errorf("the required property context should be defined") } return nil @@ -327,6 +361,9 @@ func validateCreateEventDestinationParams( // it should contain the vendor specific struct that goes inside the Oem session. // It returns the new subscription URI if the event subscription is created // with success or any error encountered. + +// Deprecated: (v1.5) EventType-based eventing is DEPRECATED in the Redfish schema +// in favor of using RegistryPrefix and ResourceTypes func CreateEventDestination( c common.Client, uri string, @@ -337,25 +374,79 @@ func CreateEventDestination( context string, oem interface{}, ) (string, error) { - // validate input parameters - err := validateCreateEventDestinationParams( - uri, - destination, - eventTypes, - ) + // validate mandatory input parameters + if err := validateCreateEventDestinationParams(uri, destination, protocol, context, eventTypes); err != nil { + return "", err + } - if err != nil { + // create subscription payload + s := &subscriptionPayload{ + EventTypes: eventTypes, + } + + return sendCreateEventDestinationRequest(c, s, uri, destination, httpHeaders, protocol, context, oem) +} + +// For Redfish v1.5+ +// CreateEventDestination will create a EventDestination instance. +// URI should contain the address of the collection for Event Subscriptions. +// Destination should contain the URL of the destination for events to be sent. +// RegistryPrefixes is the list of the prefixes for the Message Registries +// that contain the MessageIds that are sent to this event destination. +// If RegistryPrefixes is empty on subscription, the client is subscribing to all Message Registries. +// ResourceTypes is the list of Resource Type values (Schema names) that correspond to the OriginOfCondition, +// the version and full namespace should not be specified. +// If ResourceTypes is empty on subscription, the client is subscribing to receive events regardless of ResourceType. +// HttpHeaders is optional and gives the opportunity to specify any arbitrary +// HTTP headers required for the event POST operation. +// Protocol should be the communication protocol of the event endpoint, usually RedfishEventDestinationProtocol. +// Context is a required client-supplied string that is sent with the event notifications. +// DeliveryRetryPolicy is optional, it should contain the subscription delivery retry policy for events, +// where the subscription type is RedfishEvent. +// Oem is optional and gives the opportunity to specify any OEM specific properties, +// it should contain the vendor specific struct that goes inside the Oem session. +// Returns the new subscription URI if the event subscription is created with success or any error encountered. +func CreateEventDestinationInstance( + c common.Client, + uri string, + destination string, + registryPrefixes []string, + resourceTypes []string, + httpHeaders map[string]string, + protocol EventDestinationProtocol, + context string, + deliveryRetryPolicy DeliveryRetryPolicy, + oem interface{}, +) (string, error) { + // validate mandatory input parameters + if err := validateCreateEventDestinationMandatoryParams(uri, destination, protocol, context); err != nil { return "", err } // create subscription payload s := &subscriptionPayload{ - Destination: destination, - EventTypes: eventTypes, - Protocol: protocol, - Context: context, + RegistryPrefixes: registryPrefixes, + ResourceTypes: resourceTypes, + DeliveryRetryPolicy: deliveryRetryPolicy, } + return sendCreateEventDestinationRequest(c, s, uri, destination, httpHeaders, protocol, context, oem) +} + +func sendCreateEventDestinationRequest( + c common.Client, + s *subscriptionPayload, + uri string, + destination string, + httpHeaders map[string]string, + protocol EventDestinationProtocol, + context string, + oem interface{}, +) (subscriptionLink string, err error) { + s.Destination = destination + s.Protocol = protocol + s.Context = context + // HTTP headers if len(httpHeaders) > 0 { s.HTTPHeaders = httpHeaders @@ -368,17 +459,18 @@ func CreateEventDestination( resp, err := c.Post(uri, s) if err != nil { - return "", err + return } defer resp.Body.Close() // return subscription link from returned location - subscriptionLink := resp.Header.Get("Location") - if urlParser, err := url.ParseRequestURI(subscriptionLink); err == nil { + subscriptionLink = resp.Header.Get("Location") + urlParser, err := url.ParseRequestURI(subscriptionLink) + if err == nil { subscriptionLink = urlParser.RequestURI() } - return subscriptionLink, nil + return subscriptionLink, err } // DeleteEventDestination will delete a EventDestination. diff --git a/redfish/eventservice.go b/redfish/eventservice.go index a88a39d3..0fc564cb 100644 --- a/redfish/eventservice.go +++ b/redfish/eventservice.go @@ -302,6 +302,9 @@ func (eventservice *EventService) GetEventSubscription(uri string) (*EventDestin // it should contain the vendor specific struct that goes inside the Oem session. // It returns the new subscription URI if the event subscription is created // with success or any error encountered. + +// Deprecated: (v1.5) EventType-based eventing is DEPRECATED in the Redfish schema +// in favor of using RegistryPrefix and ResourceTypes func (eventservice *EventService) CreateEventSubscription( destination string, eventTypes []EventType, @@ -326,6 +329,53 @@ func (eventservice *EventService) CreateEventSubscription( ) } +// For Redfish v1.5+ +// CreateEventSubscription creates the subscription using the event service. +// Destination should contain the URL of the destination for events to be sent. +// RegistryPrefixes is the list of the prefixes for the Message Registries +// that contain the MessageIds that are sent to this event destination. +// If RegistryPrefixes is empty on subscription, the client is subscribing to all Message Registries. +// ResourceTypes is the list of Resource Type values (Schema names) that correspond to the OriginOfCondition, +// the version and full namespace should not be specified. +// If ResourceTypes is empty on subscription, the client is subscribing to receive events regardless of ResourceType. +// HttpHeaders is optional and gives the opportunity to specify any arbitrary +// HTTP headers required for the event POST operation. +// Protocol should be the communication protocol of the event endpoint, usually RedfishEventDestinationProtocol. +// Context is a required client-supplied string that is sent with the event notifications. +// DeliveryRetryPolicy is optional, it should contain the subscription delivery retry policy for events, +// where the subscription type is RedfishEvent. +// Oem is optional and gives the opportunity to specify any OEM specific properties, +// it should contain the vendor specific struct that goes inside the Oem session. +// It returns the new subscription URI if the event subscription is created +// with success or any error encountered. +func (eventservice *EventService) CreateEventSubscriptionInstance( + destination string, + registryPrefixes []string, + resourceTypes []string, + httpHeaders map[string]string, + protocol EventDestinationProtocol, + context string, + deliveryRetryPolicy DeliveryRetryPolicy, + oem interface{}, +) (string, error) { + if strings.TrimSpace(eventservice.Subscriptions) == "" { + return "", fmt.Errorf("empty subscription link in the event service") + } + + return CreateEventDestinationInstance( + eventservice.Client, + eventservice.Subscriptions, + destination, + registryPrefixes, + resourceTypes, + httpHeaders, + protocol, + context, + deliveryRetryPolicy, + oem, + ) +} + // DeleteEventSubscription deletes a specific subscription using the event service. func (eventservice *EventService) DeleteEventSubscription(uri string) error { return DeleteEventDestination(eventservice.Client, uri) @@ -339,8 +389,8 @@ func (eventservice *EventService) SubmitTestEvent(message string) error { EventGroupID string `json:"EventGroupId"` EventID string `json:"EventId"` EventTimestamp string - EventType string - Message string + EventType string `json:"EventType,omitempty"` + Message string `json:"Message,omitempty"` MessageArgs []string MessageID string `json:"MessageId"` OriginOfCondition string diff --git a/redfish/eventservice_test.go b/redfish/eventservice_test.go index 5fb19c60..e760b7f9 100644 --- a/redfish/eventservice_test.go +++ b/redfish/eventservice_test.go @@ -16,6 +16,12 @@ import ( "github.com/stmcginnis/gofish/common" ) +const ( + subscriptionMask = "/redfish/v1/EventService/Subscriptions/%s/" + subsctiptionID = "SubscriptionId" + eventDestinationID = "EventDestination-1" +) + var eventServiceBody = `{ "@Redfish.Copyright": "Copyright 2014-2019 DMTF. All rights reserved.", "@odata.context": "/redfish/v1/$metadata#EventService.EventService", @@ -38,7 +44,7 @@ var eventServiceBody = `{ "Alert" ], "RegistryPrefixes": ["EVENT_"], - "ResourceTypes": [], + "ResourceTypes": ["Chassis"], "SSEFilterPropertiesSupported": { "EventFormatType": true, "MessageId": true, @@ -70,42 +76,52 @@ var eventServiceBody = `{ } }` -// TestEventService tests the parsing of EventService objects. -func TestEventService(t *testing.T) { - var result EventService - err := json.NewDecoder(strings.NewReader(eventServiceBody)).Decode(&result) - - if err != nil { - t.Errorf("Error decoding JSON: %s", err) - } - - if result.ID != "EventService" { - t.Errorf("Received invalid ID: %s", result.ID) +func assertContains(t testing.TB, expected, actual string) { + t.Helper() + if !strings.Contains(actual, expected) { + t.Errorf("\nExpected payload item: %s \nActual CreateEventSubscription payload: %s", expected, actual) } +} - if result.Name != "Event Service" { - t.Errorf("Received invalid name: %s", result.Name) +func assertNotContain(t testing.TB, expected, actual string) { + t.Helper() + if strings.Contains(actual, expected) { + t.Errorf("\nExpected payload item: %s \nActual CreateEventSubscription payload: %s", expected, actual) } +} - if result.DeliveryRetryAttempts != 4 { - t.Errorf("Expected 4 retry attempts, got: %d", result.DeliveryRetryAttempts) +func assertError(t testing.TB, expected, actual string) { + t.Helper() + if expected != actual { + t.Errorf("\nExpected error: %s \nActual error: %s", + actual, + expected) } +} - if result.DeliveryRetryIntervalSeconds != 30 { - t.Errorf("Expected 30 second retry interval, got: %d", result.DeliveryRetryIntervalSeconds) - } +// TestEventService tests the parsing of EventService objects. +func TestEventService(t *testing.T) { + var result EventService + err := json.NewDecoder(strings.NewReader(eventServiceBody)).Decode(&result) - if result.SSEFilterPropertiesSupported.MetricReportDefinition { - t.Error("MetricReportDefinition filter should be false") + if err != nil { + t.Errorf("Error decoding JSON: %s", err) } - if !result.SSEFilterPropertiesSupported.MessageID { - t.Error("Message ID filter should be true") + assertEquals := func(t testing.TB, expected string, actual string) { + t.Helper() + if expected != actual { + t.Errorf("\nExpected value: %s \nActual value: %s", expected, actual) + } } - if result.SubmitTestEventTarget != "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent" { - t.Errorf("Invalid SubmitTestEvent target: %s", result.SubmitTestEventTarget) - } + assertEquals(t, "EventService", result.ID) + assertEquals(t, "Event Service", result.Name) + assertEquals(t, "4", fmt.Sprint(result.DeliveryRetryAttempts)) + assertEquals(t, "30", fmt.Sprint(result.DeliveryRetryIntervalSeconds)) + assertEquals(t, "false", fmt.Sprint(result.SSEFilterPropertiesSupported.MetricReportDefinition)) + assertEquals(t, "true", fmt.Sprint(result.SSEFilterPropertiesSupported.MessageID)) + assertEquals(t, "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent", result.SubmitTestEventTarget) for _, et := range result.EventTypesForSubscription { if !et.IsValidEventType() { @@ -171,7 +187,7 @@ func TestEventServiceCreateEventSubscription(t *testing.T) { // define the expected subscription URI that should be // returned during create event subscription - expectedSubscriptionURI := "/redfish/v1/EventService/Subscriptions/SubscriptionId/" + expectedSubscriptionURI := fmt.Sprintf(subscriptionMask, subsctiptionID) // create the custom test client testClient := &common.TestClient{ @@ -198,21 +214,27 @@ func TestEventServiceCreateEventSubscription(t *testing.T) { } result.SetClient(testClient) + validDestinationURI := "https://myeventreceiver/eventreceiver" + validEventTypes := []EventType{AlertEventType} + validCustomHeader := map[string]string{ + "Header": "HeaderValue"} + validDestinationProtocol := RedfishEventDestinationProtocol + validContext := "Public" + validOem := OemVendor{ + Vendor: Vendor{ + FirstVendorSpecificConfiguration: 1, + SecondVendorSpecificConfiguration: 2, + }, + } + // create event subscription subscriptionURI, err := result.CreateEventSubscription( - "https://myeventreciever/eventreceiver", - []EventType{SupportedEventTypes["Alert"]}, - map[string]string{ - "Header": "HeaderValue", - }, - RedfishEventDestinationProtocol, - "Public", - OemVendor{ - Vendor: Vendor{ - FirstVendorSpecificConfiguration: 1, - SecondVendorSpecificConfiguration: 2, - }, - }, + validDestinationURI, + validEventTypes, + validCustomHeader, + validDestinationProtocol, + validContext, + validOem, ) // validate the return values @@ -220,30 +242,131 @@ func TestEventServiceCreateEventSubscription(t *testing.T) { t.Errorf("Error making CreateEventSubscription call: %s", err) } - if subscriptionURI != expectedSubscriptionURI { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - subscriptionURI, - expectedSubscriptionURI) - } + // validate CreateEventSubscription call + assertError(t, expectedSubscriptionURI, subscriptionURI) // validate the payload calls := testClient.CapturedCalls() - if !strings.Contains(calls[0].Payload, "Destination:https://myeventreciever/eventreceiver") { - t.Errorf("Unexpected Destination CreateEventSubscription payload: %s", calls[0].Payload) - } + actual := calls[0].Payload + + propertyName := "Destination" + expected := fmt.Sprintf("%s:%s", propertyName, validDestinationURI) + assertContains(t, expected, actual) + + propertyName = "EventTypes" + expected = fmt.Sprintf("%s:%v", propertyName, validEventTypes) + assertContains(t, expected, actual) + + propertyName = "HttpHeaders" + expected = fmt.Sprintf("%s:%v", propertyName, validCustomHeader) + assertContains(t, expected, actual) + + propertyName = "Protocol" + expected = fmt.Sprintf("%s:%v", propertyName, validDestinationProtocol) + assertContains(t, expected, actual) + + propertyName = "Context" + expected = fmt.Sprintf("%s:%v", propertyName, validContext) + assertContains(t, expected, actual) + + propertyName = "Oem" + expected = fmt.Sprintf("%s:map[Vendor:map[FirstVendorSpecificConfiguration:%d SecondVendorSpecificConfiguration:%d]", + propertyName, validOem.Vendor.FirstVendorSpecificConfiguration, validOem.Vendor.SecondVendorSpecificConfiguration) + assertContains(t, expected, actual) +} - if !strings.Contains(calls[0].Payload, "EventTypes:[Alert]") { - t.Errorf("Unexpected EventTypes CreateEventSubscription payload: %s", calls[0].Payload) +func TestEventServiceCreateEventSubscriptionInstance(t *testing.T) { + var result EventService + err := json.NewDecoder(strings.NewReader(eventServiceBody)).Decode(&result) + if err != nil { + t.Errorf("Error decoding JSON: %s", err) } - if !strings.Contains(calls[0].Payload, "HttpHeaders:map[Header:HeaderValue]") { - t.Errorf("Unexpected HttpHeaders CreateEventSubscription payload: %s", calls[0].Payload) + // define the expected subscription URI that should be + // returned during create event subscription + expectedSubscriptionURI := fmt.Sprintf(subscriptionMask, subsctiptionID) + + // create the custom test client + testClient := &common.TestClient{ + CustomReturnForActions: map[string][]interface{}{ + http.MethodPost: { + // defining the custom return for the first POST operation + &http.Response{ + Status: "201 Created", + StatusCode: 201, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Body: io.NopCloser(bytes.NewBufferString("")), + ContentLength: int64(len("")), + Header: http.Header{ + "Location": []string{ + fmt.Sprintf("https://redfish-server%s", + expectedSubscriptionURI), + }, + }, + }, + }, + }, } + result.SetClient(testClient) - if !strings.Contains(calls[0].Payload, "Oem:map[Vendor:map[FirstVendorSpecificConfiguration:1 SecondVendorSpecificConfiguration:2]") { - t.Errorf("Unexpected Oem CreateEventSubscription payload: %s", calls[0].Payload) + validDestinationURI := "https://myeventreceiver/eventreceiver" + validDestinationProtocol := RedfishEventDestinationProtocol + validContext := "Public" + validRegistryPrefixes := []string{"EVENT_"} + validResourceTypes := []string{"Chassis"} + validDeliveryRetryPolicy := SuspendRetriesDeliveryRetryPolicy + + // create event subscription + subscriptionURI, err := result.CreateEventSubscriptionInstance( + validDestinationURI, + validRegistryPrefixes, + validResourceTypes, + nil, + validDestinationProtocol, + validContext, + validDeliveryRetryPolicy, + nil, + ) + + // validate the return values + if err != nil { + t.Errorf("Error making CreateEventSubscription call: %s", err) } + + // validate CreateEventSubscription call + assertError(t, expectedSubscriptionURI, subscriptionURI) + + // validate the payload + calls := testClient.CapturedCalls() + + actual := calls[0].Payload + + propertyName := "Destination" + expected := fmt.Sprintf("%s:%s", propertyName, validDestinationURI) + assertContains(t, expected, actual) + + propertyName = "RegistryPrefixes" + expected = fmt.Sprintf("%s:%v", propertyName, validRegistryPrefixes) + assertContains(t, expected, actual) + + propertyName = "ResourceTypes" + expected = fmt.Sprintf("%s:%v", propertyName, validResourceTypes) + assertContains(t, expected, actual) + + propertyName = "Protocol" + expected = fmt.Sprintf("%s:%v", propertyName, validDestinationProtocol) + assertContains(t, expected, actual) + + propertyName = "Context" + expected = fmt.Sprintf("%s:%v", propertyName, validContext) + assertContains(t, expected, actual) + + propertyName = "DeliveryRetryPolicy" + expected = fmt.Sprintf("%s:%s", propertyName, validDeliveryRetryPolicy) + assertContains(t, expected, actual) } // TestEventServiceDeleteEventSubscription tests the DeleteEventSubscription call. @@ -267,8 +390,8 @@ func TestEventServiceDeleteEventSubscription(t *testing.T) { result.SetClient(testClient) // create event subscription - err = result.DeleteEventSubscription( - "/redfish/v1/EventService/Subscriptions/SubscriptionId/") + subscriptionToDelete := fmt.Sprintf(subscriptionMask, subsctiptionID) + err = result.DeleteEventSubscription(subscriptionToDelete) // validate the return values if err != nil { @@ -305,11 +428,11 @@ func TestEventServiceGetEventSubscription(t *testing.T) { result.SetClient(testClient) // create event subscription - eventDestination, err := result.GetEventSubscription( - "/redfish/v1/EventService/Subscriptions/EventDestination-1/") + eventSubscription := fmt.Sprintf(subscriptionMask, eventDestinationID) + eventDestination, err := result.GetEventSubscription(eventSubscription) // validate the return values - if eventDestination.ID != "EventDestination-1" { + if eventDestination.ID != eventDestinationID { t.Errorf("Error making GetEventSubscription call: %s", err) } } @@ -357,7 +480,7 @@ func TestEventServiceGetEventSubscriptions(t *testing.T) { eventDestinations, err := result.GetEventSubscriptions() // validate the return values - if eventDestinations[0].ID != "EventDestination-1" { + if eventDestinations[0].ID != eventDestinationID { t.Errorf("Error making GetEventSubscriptions call: %s", err) } } @@ -373,7 +496,7 @@ func TestEventServiceCreateEventSubscriptionWithoutOptionalParameters(t *testing // define the expected subscription URI that should be // returned during create event subscription - expectedSubscriptionURI := "/redfish/v1/EventService/Subscriptions/SubscriptionId/" + expectedSubscriptionURI := fmt.Sprintf(subscriptionMask, subsctiptionID) // create the custom test client testClient := &common.TestClient{ @@ -400,13 +523,18 @@ func TestEventServiceCreateEventSubscriptionWithoutOptionalParameters(t *testing } result.SetClient(testClient) + validDestinationURI := "https://myeventreceiver/eventreceiver" + validEventTypes := []EventType{AlertEventType} + validDestinationProtocol := RedfishEventDestinationProtocol + validContext := "Public" + // create event subscription subscriptionURI, err := result.CreateEventSubscription( - "https://myeventreciever/eventreceiver", - []EventType{SupportedEventTypes["Alert"]}, + validDestinationURI, + validEventTypes, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) @@ -415,30 +543,35 @@ func TestEventServiceCreateEventSubscriptionWithoutOptionalParameters(t *testing t.Errorf("Error making CreateEventSubscription call: %s", err) } - if subscriptionURI != expectedSubscriptionURI { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - subscriptionURI, - expectedSubscriptionURI) - } + // validate CreateEventSubscription call + assertError(t, expectedSubscriptionURI, subscriptionURI) // validate the payload calls := testClient.CapturedCalls() - if !strings.Contains(calls[0].Payload, "Destination:https://myeventreciever/eventreceiver") { - t.Errorf("Unexpected Destination CreateEventSubscription payload: %s", calls[0].Payload) - } + actual := calls[0].Payload - if !strings.Contains(calls[0].Payload, "EventTypes:[Alert]") { - t.Errorf("Unexpected EventTypes CreateEventSubscription payload: %s", calls[0].Payload) - } + propertyName := "Destination" + expected := fmt.Sprintf("%s:%s", propertyName, validDestinationURI) + assertContains(t, expected, actual) - if strings.Contains(calls[0].Payload, "Oem") { - t.Errorf("Unexpected Oem CreateEventSubscription payload: %s", calls[0].Payload) - } + propertyName = "EventTypes" + expected = fmt.Sprintf("%s:%v", propertyName, validEventTypes) + assertContains(t, expected, actual) - if strings.Contains(calls[0].Payload, "HttpHeaders") { - t.Errorf("Unexpected HttpHeaders CreateEventSubscription payload: %s", calls[0].Payload) - } + propertyName = "Protocol" + expected = fmt.Sprintf("%s:%v", propertyName, validDestinationProtocol) + assertContains(t, expected, actual) + + propertyName = "Context" + expected = fmt.Sprintf("%s:%v", propertyName, validContext) + assertContains(t, expected, actual) + + expected = "HttpHeaders" + assertNotContain(t, expected, actual) + + expected = "Oem" + assertNotContain(t, expected, actual) } // TestEventServiceCreateEventSubscriptionInputParametersValidation @@ -450,118 +583,114 @@ func TestEventServiceCreateEventSubscriptionInputParametersValidation(t *testing t.Errorf("Error decoding JSON: %s", err) } + validDestinationURI := "https://myeventreceiver/eventreceiver" + validEventTypes := []EventType{AlertEventType} + validDestinationProtocol := RedfishEventDestinationProtocol + validContext := "Public" + // create event subscription invalid destination invalidDestination := "myeventreciever/eventreceiver" _, err = result.CreateEventSubscription( invalidDestination, - []EventType{SupportedEventTypes["Alert"]}, + validEventTypes, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) // validate the returned error - expectedError := "destination should start with http" - if err.Error() != expectedError { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - err, - expectedError) - } + expectedError := fmt.Sprintf("parse %q: invalid URI for request", invalidDestination) + assertError(t, expectedError, err.Error()) + + // create event subscription invalid destination + invalidDestination = "ftp://myeventreciever/eventreceiver" + _, err = result.CreateEventSubscription( + invalidDestination, + validEventTypes, + nil, + validDestinationProtocol, + validContext, + nil, + ) + + // validate the returned error + expectedError = "destination should start with http" + assertError(t, expectedError, err.Error()) // create event subscription invalid destination invalidDestination = "" _, err = result.CreateEventSubscription( invalidDestination, - []EventType{SupportedEventTypes["Alert"]}, + validEventTypes, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) // validate the returned error expectedError = "empty destination is not valid" - if err.Error() != expectedError { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) // create event subscription invalid destination invalidDestination = " " _, err = result.CreateEventSubscription( invalidDestination, - []EventType{SupportedEventTypes["Alert"]}, + validEventTypes, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) // validate the returned error expectedError = "empty destination is not valid" - if err.Error() != expectedError { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) // create event subscription empty event type _, err = result.CreateEventSubscription( - "https://myeventreciever/eventreceiver", + validDestinationURI, []EventType{}, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) // validate the returned error expectedError = "at least one event type for subscription should be defined" - if err.Error() != expectedError { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) // create event subscription nil event type _, err = result.CreateEventSubscription( - "https://myeventreciever/eventreceiver", + validDestinationURI, nil, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) // validate the returned error expectedError = "at least one event type for subscription should be defined" - if err.Error() != expectedError { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) // create event subscription empty // subscription link in the event service result.Subscriptions = "" _, err = result.CreateEventSubscription( - "https://myeventreciever/eventreceiver", - []EventType{SupportedEventTypes["Alert"]}, + validDestinationURI, + validEventTypes, nil, - RedfishEventDestinationProtocol, - "Public", + validDestinationProtocol, + validContext, nil, ) // validate the returned error expectedError = "empty subscription link in the event service" - if err.Error() != expectedError { - t.Errorf("Error CreateEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) } // TestEventServiceDeleteEventSubscriptionInputParametersValidation @@ -572,27 +701,20 @@ func TestEventServiceDeleteEventSubscriptionInputParametersValidation(t *testing if err != nil { t.Errorf("Error decoding JSON: %s", err) } + // delete event subscription err = result.DeleteEventSubscription("") // validate the returned error expectedError := "uri should not be empty" - if err.Error() != expectedError { - t.Errorf("Error DeleteEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) // delete event subscription err = result.DeleteEventSubscription(" ") // validate the returned error expectedError = "uri should not be empty" - if err.Error() != expectedError { - t.Errorf("Error DeleteEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) } // TestEventServiceGetEventSubscriptionInputParametersValidation @@ -603,27 +725,20 @@ func TestEventServiceGetEventSubscriptionInputParametersValidation(t *testing.T) if err != nil { t.Errorf("Error decoding JSON: %s", err) } + // get event subscription _, err = result.GetEventSubscription("") // validate the returned error expectedError := "uri should not be empty" - if err.Error() != expectedError { - t.Errorf("Error GetEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) // get event subscription _, err = result.GetEventSubscription(" ") // validate the returned error expectedError = "uri should not be empty" - if err.Error() != expectedError { - t.Errorf("Error GetEventSubscription returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) } // TestEventServiceGetEventSubscriptionsEmptySubscriptionsLink @@ -642,9 +757,5 @@ func TestEventServiceGetEventSubscriptionsEmptySubscriptionsLink(t *testing.T) { // validate the returned error expectedError := "empty subscription link in the event service" - if err.Error() != expectedError { - t.Errorf("Error GetEventSubscriptions returned: %s expected: %s", - err, - expectedError) - } + assertError(t, expectedError, err.Error()) }