Skip to content

Commit

Permalink
add a capability that's needed for the JS SSE implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
eli-darkly committed Nov 13, 2021
1 parent b03f726 commit e0af7d4
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 4 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -87,9 +88,10 @@ If any parameters are invalid, return HTTP `400`.

### Send command: `POST <URL of stream instance>`

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": "<EVENT 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.

Expand Down
13 changes: 11 additions & 2 deletions framework/test_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type TestServiceInfo struct {
// which the test harness will interact with.
type TestServiceEntity struct {
resourceURL string
logger Logger
}

type commandRequestParams struct {
Expand Down Expand Up @@ -125,6 +126,7 @@ func (h *TestHarness) NewTestServiceEntity(

e := &TestServiceEntity{
resourceURL: resourceURL,
logger: logger,
}

return e, nil
Expand All @@ -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
Expand Down
14 changes: 14 additions & 0 deletions ssetests/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}))
}
6 changes: 6 additions & 0 deletions ssetests/tests_basic_parsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"})
})
Expand All @@ -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"})
})
Expand Down

0 comments on commit e0af7d4

Please sign in to comment.