mirror of https://github.com/dapr/go-sdk.git
				
				
				
			Decode CloudEvent Data consistently between HTTP and gRPC (#230)
* Decode CloudEvent Data consistently between HTTP and gRPC Signed-off-by: Phil Kedy <phil.kedy@gmail.com> * linter issue Signed-off-by: Phil Kedy <phil.kedy@gmail.com> * Added parsing media type Signed-off-by: Phil Kedy <phil.kedy@gmail.com> * Adding extension json media type handling Signed-off-by: Phil Kedy <phil.kedy@gmail.com> * add TTL to actor timer/reminder requests (#225) Signed-off-by: Phil Kedy <phil.kedy@gmail.com> * Adding more tests Signed-off-by: Phil Kedy <phil.kedy@gmail.com> * Tweak Signed-off-by: Phil Kedy <phil.kedy@gmail.com> Co-authored-by: Dmitry Shmulevich <dmitry.shmulevich@gmail.com>
This commit is contained in:
		
							parent
							
								
									15fc672e08
								
							
						
					
					
						commit
						8a7a61b443
					
				|  | @ -1,5 +1,9 @@ | ||||||
| package common | package common | ||||||
| 
 | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // TopicEvent is the content of the inbound topic message.
 | // TopicEvent is the content of the inbound topic message.
 | ||||||
| type TopicEvent struct { | type TopicEvent struct { | ||||||
| 	// ID identifies the event.
 | 	// ID identifies the event.
 | ||||||
|  | @ -15,8 +19,12 @@ type TopicEvent struct { | ||||||
| 	// The content of the event.
 | 	// The content of the event.
 | ||||||
| 	// Note, this is why the gRPC and HTTP implementations need separate structs for cloud events.
 | 	// Note, this is why the gRPC and HTTP implementations need separate structs for cloud events.
 | ||||||
| 	Data interface{} `json:"data"` | 	Data interface{} `json:"data"` | ||||||
|  | 	// The content of the event represented as raw bytes.
 | ||||||
|  | 	// This can be deserialized into a Go struct using `Struct`.
 | ||||||
|  | 	RawData []byte `json:"-"` | ||||||
| 	// The base64 encoding content of the event.
 | 	// The base64 encoding content of the event.
 | ||||||
| 	// Note, this is processing rawPayload and binary content types .
 | 	// Note, this is processing rawPayload and binary content types.
 | ||||||
|  | 	// This field is deprecated and will be removed in the future.
 | ||||||
| 	DataBase64 string `json:"data_base64,omitempty"` | 	DataBase64 string `json:"data_base64,omitempty"` | ||||||
| 	// Cloud event subject
 | 	// Cloud event subject
 | ||||||
| 	Subject string `json:"subject"` | 	Subject string `json:"subject"` | ||||||
|  | @ -26,6 +34,12 @@ type TopicEvent struct { | ||||||
| 	PubsubName string `json:"pubsubname"` | 	PubsubName string `json:"pubsubname"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (e *TopicEvent) Struct(target interface{}) error { | ||||||
|  | 	// TODO: Enhance to inspect DataContentType for the best
 | ||||||
|  | 	// deserialization method.
 | ||||||
|  | 	return json.Unmarshal(e.RawData, target) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // InvocationEvent represents the input and output of binding invocation.
 | // InvocationEvent represents the input and output of binding invocation.
 | ||||||
| type InvocationEvent struct { | type InvocationEvent struct { | ||||||
| 	// Data is the payload that the input bindings sent.
 | 	// Data is the payload that the input bindings sent.
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,10 @@ package grpc | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"mime" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/golang/protobuf/ptypes/empty" | 	"github.com/golang/protobuf/ptypes/empty" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
|  | @ -62,13 +65,38 @@ func (s *Server) OnTopicEvent(ctx context.Context, in *pb.TopicEventRequest) (*p | ||||||
| 	} | 	} | ||||||
| 	key := fmt.Sprintf("%s-%s", in.PubsubName, in.Topic) | 	key := fmt.Sprintf("%s-%s", in.PubsubName, in.Topic) | ||||||
| 	if h, ok := s.topicSubscriptions[key]; ok { | 	if h, ok := s.topicSubscriptions[key]; ok { | ||||||
|  | 		data := interface{}(in.Data) | ||||||
|  | 		if len(in.Data) > 0 { | ||||||
|  | 			mediaType, _, err := mime.ParseMediaType(in.DataContentType) | ||||||
|  | 			if err == nil { | ||||||
|  | 				var v interface{} | ||||||
|  | 				switch mediaType { | ||||||
|  | 				case "application/json": | ||||||
|  | 					if err := json.Unmarshal(in.Data, &v); err == nil { | ||||||
|  | 						data = v | ||||||
|  | 					} | ||||||
|  | 				case "text/plain": | ||||||
|  | 					// Assume UTF-8 encoded string.
 | ||||||
|  | 					data = string(in.Data) | ||||||
|  | 				default: | ||||||
|  | 					if strings.HasPrefix(mediaType, "application/") && | ||||||
|  | 						strings.HasSuffix(mediaType, "+json") { | ||||||
|  | 						if err := json.Unmarshal(in.Data, &v); err == nil { | ||||||
|  | 							data = v | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		e := &common.TopicEvent{ | 		e := &common.TopicEvent{ | ||||||
| 			ID:              in.Id, | 			ID:              in.Id, | ||||||
| 			Source:          in.Source, | 			Source:          in.Source, | ||||||
| 			Type:            in.Type, | 			Type:            in.Type, | ||||||
| 			SpecVersion:     in.SpecVersion, | 			SpecVersion:     in.SpecVersion, | ||||||
| 			DataContentType: in.DataContentType, | 			DataContentType: in.DataContentType, | ||||||
| 			Data:            in.Data, | 			Data:            data, | ||||||
|  | 			RawData:         in.Data, | ||||||
| 			Topic:           in.Topic, | 			Topic:           in.Topic, | ||||||
| 			PubsubName:      in.PubsubName, | 			PubsubName:      in.PubsubName, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -161,3 +161,79 @@ func eventHandlerWithRetryError(ctx context.Context, event *common.TopicEvent) ( | ||||||
| func eventHandlerWithError(ctx context.Context, event *common.TopicEvent) (retry bool, err error) { | func eventHandlerWithError(ctx context.Context, event *common.TopicEvent) (retry bool, err error) { | ||||||
| 	return false, errors.New("nil event") | 	return false, errors.New("nil event") | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestEventDataHandling(t *testing.T) { | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 
 | ||||||
|  | 	tests := map[string]struct { | ||||||
|  | 		contentType string | ||||||
|  | 		data        string | ||||||
|  | 		value       interface{} | ||||||
|  | 	}{ | ||||||
|  | 		"JSON bytes": { | ||||||
|  | 			contentType: "application/json", | ||||||
|  | 			data:        `{"message":"hello"}`, | ||||||
|  | 			value: map[string]interface{}{ | ||||||
|  | 				"message": "hello", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"JSON entension media type bytes": { | ||||||
|  | 			contentType: "application/extension+json", | ||||||
|  | 			data:        `{"message":"hello"}`, | ||||||
|  | 			value: map[string]interface{}{ | ||||||
|  | 				"message": "hello", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"Test": { | ||||||
|  | 			contentType: "text/plain", | ||||||
|  | 			data:        `message = hello`, | ||||||
|  | 			value:       `message = hello`, | ||||||
|  | 		}, | ||||||
|  | 		"Other": { | ||||||
|  | 			contentType: "application/octet-stream", | ||||||
|  | 			data:        `message = hello`, | ||||||
|  | 			value:       []byte(`message = hello`), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	s := getTestServer() | ||||||
|  | 
 | ||||||
|  | 	sub := &common.Subscription{ | ||||||
|  | 		PubsubName: "messages", | ||||||
|  | 		Topic:      "test", | ||||||
|  | 		Route:      "/test", | ||||||
|  | 		Metadata:   map[string]string{}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	recv := make(chan struct{}, 1) | ||||||
|  | 	var topicEvent *common.TopicEvent | ||||||
|  | 	handler := func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { | ||||||
|  | 		topicEvent = e | ||||||
|  | 		recv <- struct{}{} | ||||||
|  | 
 | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  | 	err := s.AddTopicEventHandler(sub, handler) | ||||||
|  | 	assert.NoErrorf(t, err, "error adding event handler") | ||||||
|  | 
 | ||||||
|  | 	startTestServer(s) | ||||||
|  | 
 | ||||||
|  | 	for name, tt := range tests { | ||||||
|  | 		t.Run(name, func(t *testing.T) { | ||||||
|  | 			in := runtime.TopicEventRequest{ | ||||||
|  | 				Id:              "a123", | ||||||
|  | 				Source:          "test", | ||||||
|  | 				Type:            "test", | ||||||
|  | 				SpecVersion:     "v1.0", | ||||||
|  | 				DataContentType: tt.contentType, | ||||||
|  | 				Data:            []byte(tt.data), | ||||||
|  | 				Topic:           sub.Topic, | ||||||
|  | 				PubsubName:      sub.PubsubName, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			s.OnTopicEvent(ctx, &in) | ||||||
|  | 			<-recv | ||||||
|  | 			assert.Equal(t, tt.value, topicEvent.Data) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package http | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | @ -29,6 +30,34 @@ const ( | ||||||
| 	PubSubHandlerDropStatusCode int = http.StatusSeeOther | 	PubSubHandlerDropStatusCode int = http.StatusSeeOther | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // topicEventJSON is identical to `common.TopicEvent`
 | ||||||
|  | // except for it treats `data` as a json.RawMessage so it can
 | ||||||
|  | // be used as bytes or interface{}.
 | ||||||
|  | type topicEventJSON struct { | ||||||
|  | 	// ID identifies the event.
 | ||||||
|  | 	ID string `json:"id"` | ||||||
|  | 	// The version of the CloudEvents specification.
 | ||||||
|  | 	SpecVersion string `json:"specversion"` | ||||||
|  | 	// The type of event related to the originating occurrence.
 | ||||||
|  | 	Type string `json:"type"` | ||||||
|  | 	// Source identifies the context in which an event happened.
 | ||||||
|  | 	Source string `json:"source"` | ||||||
|  | 	// The content type of data value.
 | ||||||
|  | 	DataContentType string `json:"datacontenttype"` | ||||||
|  | 	// The content of the event.
 | ||||||
|  | 	// Note, this is why the gRPC and HTTP implementations need separate structs for cloud events.
 | ||||||
|  | 	Data json.RawMessage `json:"data"` | ||||||
|  | 	// The base64 encoding content of the event.
 | ||||||
|  | 	// Note, this is processing rawPayload and binary content types.
 | ||||||
|  | 	DataBase64 string `json:"data_base64,omitempty"` | ||||||
|  | 	// Cloud event subject
 | ||||||
|  | 	Subject string `json:"subject"` | ||||||
|  | 	// The pubsub topic which publisher sent to.
 | ||||||
|  | 	Topic string `json:"topic"` | ||||||
|  | 	// PubsubName is name of the pub/sub this message came from
 | ||||||
|  | 	PubsubName string `json:"pubsubname"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (s *Server) registerBaseHandler() { | func (s *Server) registerBaseHandler() { | ||||||
| 	// register subscribe handler
 | 	// register subscribe handler
 | ||||||
| 	f := func(w http.ResponseWriter, r *http.Request) { | 	f := func(w http.ResponseWriter, r *http.Request) { | ||||||
|  | @ -168,21 +197,78 @@ func (s *Server) AddTopicEventHandler(sub *common.Subscription, fn func(ctx cont | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// deserialize the event
 | 			// deserialize the event
 | ||||||
| 			var in common.TopicEvent | 			var in topicEventJSON | ||||||
| 			if err := json.NewDecoder(r.Body).Decode(&in); err != nil { | 			if err := json.NewDecoder(r.Body).Decode(&in); err != nil { | ||||||
| 				http.Error(w, err.Error(), PubSubHandlerDropStatusCode) | 				http.Error(w, err.Error(), PubSubHandlerDropStatusCode) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			if in.PubsubName == "" { | ||||||
|  | 				in.Topic = sub.PubsubName | ||||||
|  | 			} | ||||||
| 			if in.Topic == "" { | 			if in.Topic == "" { | ||||||
| 				in.Topic = sub.Topic | 				in.Topic = sub.Topic | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			var data interface{} | ||||||
|  | 			var rawData []byte | ||||||
|  | 			if len(in.Data) > 0 { | ||||||
|  | 				rawData = []byte(in.Data) | ||||||
|  | 				data = rawData | ||||||
|  | 				var v interface{} | ||||||
|  | 				// We can assume that rawData is valid JSON
 | ||||||
|  | 				// without checking in.DataContentType == "application/json".
 | ||||||
|  | 				if err := json.Unmarshal(rawData, &v); err == nil { | ||||||
|  | 					data = v | ||||||
|  | 					// Handling of JSON base64 encoded or escaped in a string.
 | ||||||
|  | 					if str, ok := v.(string); ok { | ||||||
|  | 						// This is the path that will most likely succeed.
 | ||||||
|  | 						var vString interface{} | ||||||
|  | 						if err := json.Unmarshal([]byte(str), &vString); err == nil { | ||||||
|  | 							data = vString | ||||||
|  | 						} else if decoded, err := base64.StdEncoding.DecodeString(str); err == nil { | ||||||
|  | 							// Decoded Base64 encoded JSON does not seem to be in the spec
 | ||||||
|  | 							// but it is in existing unit tests so this handles that case.
 | ||||||
|  | 							var vBase64 interface{} | ||||||
|  | 							if err := json.Unmarshal(decoded, &vBase64); err == nil { | ||||||
|  | 								data = vBase64 | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} else if in.DataBase64 != "" { | ||||||
|  | 				var err error | ||||||
|  | 				rawData, err = base64.StdEncoding.DecodeString(in.DataBase64) | ||||||
|  | 				if err == nil { | ||||||
|  | 					data = rawData | ||||||
|  | 					if in.DataContentType == "application/json" { | ||||||
|  | 						var v interface{} | ||||||
|  | 						if err := json.Unmarshal(rawData, &v); err == nil { | ||||||
|  | 							data = v | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			te := common.TopicEvent{ | ||||||
|  | 				ID:              in.ID, | ||||||
|  | 				SpecVersion:     in.SpecVersion, | ||||||
|  | 				Type:            in.Type, | ||||||
|  | 				Source:          in.Source, | ||||||
|  | 				DataContentType: in.DataContentType, | ||||||
|  | 				Data:            data, | ||||||
|  | 				RawData:         rawData, | ||||||
|  | 				DataBase64:      in.DataBase64, | ||||||
|  | 				Subject:         in.Subject, | ||||||
|  | 				PubsubName:      in.PubsubName, | ||||||
|  | 				Topic:           in.Topic, | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			w.Header().Add("Content-Type", "application/json") | 			w.Header().Add("Content-Type", "application/json") | ||||||
| 			w.WriteHeader(http.StatusOK) | 			w.WriteHeader(http.StatusOK) | ||||||
| 
 | 
 | ||||||
| 			// execute user handler
 | 			// execute user handler
 | ||||||
| 			retry, err := fn(r.Context(), &in) | 			retry, err := fn(r.Context(), &te) | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
| 				writeStatus(w, common.SubscriptionResponseStatusSuccess) | 				writeStatus(w, common.SubscriptionResponseStatusSuccess) | ||||||
| 				return | 				return | ||||||
|  |  | ||||||
|  | @ -85,6 +85,129 @@ func TestEventHandler(t *testing.T) { | ||||||
| 	makeEventRequest(t, s, "/errors", data, http.StatusOK) | 	makeEventRequest(t, s, "/errors", data, http.StatusOK) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestEventDataHandling(t *testing.T) { | ||||||
|  | 	tests := map[string]struct { | ||||||
|  | 		data   string | ||||||
|  | 		result interface{} | ||||||
|  | 	}{ | ||||||
|  | 		"JSON nested": { | ||||||
|  | 			data: `{ | ||||||
|  | 				"specversion" : "1.0", | ||||||
|  | 				"type" : "com.github.pull.create", | ||||||
|  | 				"source" : "https://github.com/cloudevents/spec/pull", | ||||||
|  | 				"subject" : "123", | ||||||
|  | 				"id" : "A234-1234-1234", | ||||||
|  | 				"time" : "2018-04-05T17:31:00Z", | ||||||
|  | 				"comexampleextension1" : "value", | ||||||
|  | 				"comexampleothervalue" : 5, | ||||||
|  | 				"datacontenttype" : "application/json", | ||||||
|  | 				"data" : { | ||||||
|  | 					"message":"hello" | ||||||
|  | 				} | ||||||
|  | 			}`, | ||||||
|  | 			result: map[string]interface{}{ | ||||||
|  | 				"message": "hello", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"JSON base64 encoded in data": { | ||||||
|  | 			data: `{ | ||||||
|  | 				"specversion" : "1.0", | ||||||
|  | 				"type" : "com.github.pull.create", | ||||||
|  | 				"source" : "https://github.com/cloudevents/spec/pull", | ||||||
|  | 				"subject" : "123", | ||||||
|  | 				"id" : "A234-1234-1234", | ||||||
|  | 				"time" : "2018-04-05T17:31:00Z", | ||||||
|  | 				"comexampleextension1" : "value", | ||||||
|  | 				"comexampleothervalue" : 5, | ||||||
|  | 				"datacontenttype" : "application/json", | ||||||
|  | 				"data" : "eyJtZXNzYWdlIjoiaGVsbG8ifQ==" | ||||||
|  | 			}`, | ||||||
|  | 			result: map[string]interface{}{ | ||||||
|  | 				"message": "hello", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"JSON base64 encoded in data_base64": { | ||||||
|  | 			data: `{ | ||||||
|  | 				"specversion" : "1.0", | ||||||
|  | 				"type" : "com.github.pull.create", | ||||||
|  | 				"source" : "https://github.com/cloudevents/spec/pull", | ||||||
|  | 				"subject" : "123", | ||||||
|  | 				"id" : "A234-1234-1234", | ||||||
|  | 				"time" : "2018-04-05T17:31:00Z", | ||||||
|  | 				"comexampleextension1" : "value", | ||||||
|  | 				"comexampleothervalue" : 5, | ||||||
|  | 				"datacontenttype" : "application/json", | ||||||
|  | 				"data_base64" : "eyJtZXNzYWdlIjoiaGVsbG8ifQ==" | ||||||
|  | 			}`, | ||||||
|  | 			result: map[string]interface{}{ | ||||||
|  | 				"message": "hello", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		"Binary base64 encoded in data_base64": { | ||||||
|  | 			data: `{ | ||||||
|  | 				"specversion" : "1.0", | ||||||
|  | 				"type" : "com.github.pull.create", | ||||||
|  | 				"source" : "https://github.com/cloudevents/spec/pull", | ||||||
|  | 				"subject" : "123", | ||||||
|  | 				"id" : "A234-1234-1234", | ||||||
|  | 				"time" : "2018-04-05T17:31:00Z", | ||||||
|  | 				"comexampleextension1" : "value", | ||||||
|  | 				"comexampleothervalue" : 5, | ||||||
|  | 				"datacontenttype" : "application/octet-stream", | ||||||
|  | 				"data_base64" : "eyJtZXNzYWdlIjoiaGVsbG8ifQ==" | ||||||
|  | 			}`, | ||||||
|  | 			result: []byte(`{"message":"hello"}`), | ||||||
|  | 		}, | ||||||
|  | 		"JSON string escaped": { | ||||||
|  | 			data: `{ | ||||||
|  | 				"specversion" : "1.0", | ||||||
|  | 				"type" : "com.github.pull.create", | ||||||
|  | 				"source" : "https://github.com/cloudevents/spec/pull", | ||||||
|  | 				"subject" : "123", | ||||||
|  | 				"id" : "A234-1234-1234", | ||||||
|  | 				"time" : "2018-04-05T17:31:00Z", | ||||||
|  | 				"comexampleextension1" : "value", | ||||||
|  | 				"comexampleothervalue" : 5, | ||||||
|  | 				"datacontenttype" : "application/json", | ||||||
|  | 				"data" : "{\"message\":\"hello\"}" | ||||||
|  | 			}`, | ||||||
|  | 			result: map[string]interface{}{ | ||||||
|  | 				"message": "hello", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	s := newServer("", nil) | ||||||
|  | 
 | ||||||
|  | 	sub := &common.Subscription{ | ||||||
|  | 		PubsubName: "messages", | ||||||
|  | 		Topic:      "test", | ||||||
|  | 		Route:      "/test", | ||||||
|  | 		Metadata:   map[string]string{}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	recv := make(chan struct{}, 1) | ||||||
|  | 	var topicEvent *common.TopicEvent | ||||||
|  | 	handler := func(ctx context.Context, e *common.TopicEvent) (retry bool, err error) { | ||||||
|  | 		topicEvent = e | ||||||
|  | 		recv <- struct{}{} | ||||||
|  | 
 | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  | 	err := s.AddTopicEventHandler(sub, handler) | ||||||
|  | 	assert.NoErrorf(t, err, "error adding event handler") | ||||||
|  | 
 | ||||||
|  | 	s.registerBaseHandler() | ||||||
|  | 
 | ||||||
|  | 	for name, tt := range tests { | ||||||
|  | 		t.Run(name, func(t *testing.T) { | ||||||
|  | 			makeEventRequest(t, s, "/test", tt.data, http.StatusOK) | ||||||
|  | 			<-recv | ||||||
|  | 			assert.Equal(t, tt.result, topicEvent.Data) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestHealthCheck(t *testing.T) { | func TestHealthCheck(t *testing.T) { | ||||||
| 	s := newServer("", nil) | 	s := newServer("", nil) | ||||||
| 	s.registerBaseHandler() | 	s.registerBaseHandler() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue