From e0af7d441fb52035381cee1b96e0f9f200aeae99 Mon Sep 17 00:00:00 2001 From: Eli Bishop Date: Fri, 12 Nov 2021 18:34:22 -0800 Subject: [PATCH] add a capability that's needed for the JS SSE implementation --- README.md | 6 ++++-- framework/test_service.go | 13 +++++++++++-- ssetests/api.go | 14 ++++++++++++++ ssetests/tests_basic_parsing.go | 6 ++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2837a92..f0739df 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ This resource should return a 200 status to indicate that the service has starte * `capabilities`: An array of strings describing optional features that this SSE implementation supports: * `"comments"`: The SSE client allows the caller to read comment lines. All SSE implementations must be able to handle comment lines, but many of them will simply discard the comments and not allow them to be seen. + * `"event-type-listeners"`: This means that the SSE client's API requires the caller to explicitly listen for any event type that is not the default `"message"`. * `"headers"`: The SSE client can be configured to send custom headers. * `"last-event-id"`: The SSE client can be configured to send a specific `Last-Event-Id` value in its initial HTTP request. * `"post"`: The SSE client can be configured to send a `POST` request with a body instead of a `GET`. @@ -87,9 +88,10 @@ If any parameters are invalid, return HTTP `400`. ### Send command: `POST ` -A `POST` request to the resource that was returned by "Create stream" means the test harness wants to do something to an existing SSE client instance. The request body is a JSON object with the following property: +A `POST` request to the resource that was returned by "Create stream" means the test harness wants to do something to an existing SSE client instance. The request body is a JSON object which can be one of the following: -* `command`: Currently the only supported value is `"restart"`, meaning the stream should be disconnected and reconnected with the same stream URL. This will only be sent if the test service has the `"restart"` capability. +* `{ "command": "listen", "type": "" }` - The SSE client should be ready to receive events with the type `EVENT TYPE`. This will only be sent if the test service has the `"event-type-listeners"` capability. +* `{ "command": "restart" }` - The SSE client should disconnect and reconnect with the same stream URL. This will only be sent if the test service has the `"restart"` capability. Return any HTTP `2xx` status, `400` for an unrecognized command, or `404` if there is no such stream. diff --git a/framework/test_service.go b/framework/test_service.go index d2fc71e..6ca94ef 100644 --- a/framework/test_service.go +++ b/framework/test_service.go @@ -22,6 +22,7 @@ type TestServiceInfo struct { // which the test harness will interact with. type TestServiceEntity struct { resourceURL string + logger Logger } type commandRequestParams struct { @@ -125,6 +126,7 @@ func (h *TestHarness) NewTestServiceEntity( e := &TestServiceEntity{ resourceURL: resourceURL, + logger: logger, } return e, nil @@ -150,8 +152,15 @@ func (e *TestServiceEntity) Close() error { } // SendCommand sends a command to the test service entity. -func (e *TestServiceEntity) SendCommand(command string) error { - data, _ := json.Marshal(commandRequestParams{Command: command}) +func (e *TestServiceEntity) SendCommand(command string, additionalParams ...map[string]interface{}) error { + allParams := map[string]interface{}{"command": command} + for _, p := range additionalParams { + for k, v := range p { + allParams[k] = v + } + } + data, _ := json.Marshal(allParams) + e.logger.Printf("Sending command: %s", string(data)) resp, err := http.DefaultClient.Post(e.resourceURL, "application/json", bytes.NewBuffer(data)) if err != nil { return err diff --git a/ssetests/api.go b/ssetests/api.go index 47f1311..b1a803c 100644 --- a/ssetests/api.go +++ b/ssetests/api.go @@ -251,3 +251,17 @@ func (t *T) RestartClient() { t.requireSSEClientStarted() require.NoError(t, t.sseClientEntity.SendCommand("restart")) } + +// TellClientToExpectEventType tells the SSE client in the test service that it should be ready to +// receive an event with the specified type. This is only necessary for SSE implementations that +// require you to explicitly listen for each event type. +func (t *T) TellClientToExpectEventType(eventType string) { + if !t.harness.TestServiceHasCapability("event-type-listeners") { + // If the test service doesn't advertise this capability, then it is able to receive + // events of any type without specifically listening for them. + return + } + t.requireSSEClientStarted() + require.NoError(t, t.sseClientEntity.SendCommand("listen", + map[string]interface{}{"type": eventType})) +} diff --git a/ssetests/tests_basic_parsing.go b/ssetests/tests_basic_parsing.go index 70bf4d9..c77bbcb 100644 --- a/ssetests/tests_basic_parsing.go +++ b/ssetests/tests_basic_parsing.go @@ -38,6 +38,7 @@ func DoBasicParsingTests(t *T) { t.Run("event with specific type", func(t *T) { t.StartSSEClient() + t.TellClientToExpectEventType("greeting") t.SendOnStream("event: greeting\ndata: Hello\n\n") t.RequireSpecificEvents(EventMessage{Type: "greeting", Data: "Hello"}) }) @@ -56,30 +57,35 @@ func DoBasicParsingTests(t *T) { t.Run("event with type and ID", func(t *T) { t.StartSSEClient() + t.TellClientToExpectEventType("greeting") t.SendOnStream("event: greeting\nid: abc\ndata: Hello\n\n") t.RequireSpecificEvents(EventMessage{Type: "greeting", ID: "abc", Data: "Hello"}) }) t.Run("fields in reverse order", func(t *T) { t.StartSSEClient() + t.TellClientToExpectEventType("greeting") t.SendOnStream("data: Hello\nid: abc\nevent: greeting\n\n") t.RequireSpecificEvents(EventMessage{Type: "greeting", ID: "abc", Data: "Hello"}) }) t.Run("unknown field is ignored", func(t *T) { t.StartSSEClient() + t.TellClientToExpectEventType("greeting") t.SendOnStream("event: greeting\ncolor: blue\ndata: Hello\n\n") t.RequireSpecificEvents(EventMessage{Type: "greeting", Data: "Hello"}) }) t.Run("fields without leading space", func(t *T) { t.StartSSEClient() + t.TellClientToExpectEventType("greeting") t.SendOnStream("event:greeting\ndata:Hello\n\n") t.RequireSpecificEvents(EventMessage{Type: "greeting", Data: "Hello"}) }) t.Run("fields with extra leading space", func(t *T) { t.StartSSEClient() + t.TellClientToExpectEventType(" greeting") t.SendOnStream("event: greeting\ndata: Hello\n\n") t.RequireSpecificEvents(EventMessage{Type: " greeting", Data: " Hello"}) })