Skip to content

Commit

Permalink
Adding testing to support work to do outbound quote support.
Browse files Browse the repository at this point in the history
Signed-off-by: Scott Nichols <nicholss@google.com>
  • Loading branch information
Scott Nichols authored and markpeek committed Apr 8, 2019
1 parent 58f0318 commit 9cc1a4c
Show file tree
Hide file tree
Showing 11 changed files with 1,090 additions and 7 deletions.
9 changes: 8 additions & 1 deletion alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type EventResponse = cloudevents.EventResponse
type EventContext = cloudevents.EventContext
type EventContextV01 = cloudevents.EventContextV01
type EventContextV02 = cloudevents.EventContextV02
type EventContextV3 = cloudevents.EventContextV03
type EventContextV03 = cloudevents.EventContextV03

// Custom Types

Expand All @@ -40,6 +40,13 @@ type HTTPTransportResponseContext = http.TransportResponseContext
type HTTPEncoding = http.Encoding

var (
// ContentType Helpers

StringOfApplicationJSON = cloudevents.StringOfApplicationJSON
StringOfApplicationXML = cloudevents.StringOfApplicationXML
StringOfApplicationCloudEventsJSON = cloudevents.StringOfApplicationCloudEventsJSON
StringOfApplicationCloudEventsBatchJSON = cloudevents.StringOfApplicationCloudEventsBatchJSON

// Client Creation

NewClient = client.New
Expand Down
11 changes: 10 additions & 1 deletion pkg/cloudevents/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,16 @@ func (e Event) String() string {
b.WriteString("Data,\n ")
if strings.HasPrefix(e.DataContentType(), "application/json") {
var prettyJSON bytes.Buffer
err := json.Indent(&prettyJSON, e.Data.([]byte), " ", " ")

data, ok := e.Data.([]byte)
if !ok {
var err error
data, err = json.Marshal(e.Data)
if err != nil {
data = []byte(err.Error())
}
}
err := json.Indent(&prettyJSON, data, " ", " ")
if err != nil {
b.Write(e.Data.([]byte))
} else {
Expand Down
11 changes: 11 additions & 0 deletions pkg/cloudevents/transport/http/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ const (
Unknown
)

type Quoting int32

const (
// Unquoted does not use a wrapping for string header values
Unquoted Quoting = iota
// SingleQuoted uses ' for wrapping string header values
SingleQuoted
// DoubleQuoted uses " for wrapping string header values
DoubleQuoted
)

// String pretty-prints the encoding as a string.
func (e Encoding) String() string {
switch e {
Expand Down
29 changes: 29 additions & 0 deletions pkg/cloudevents/transport/http/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,35 @@ func WithBinaryEncoding() Option {
}
}

// WithQuotingHeaderMode sets the HTTP binary mode outbound value quote mode.
func WithQuotingHeaderMode(mode Quoting) Option {
return func(t *Transport) error {
if t == nil {
return fmt.Errorf("http quoting header mode option can not set nil transport")
}

t.Quoting = mode
return nil
}
}

// WithUnquotedHeaderValues sets outbound header string quote mode to Unquoted.
func WithUnquotedHeaderValues() Option {
return WithQuotingHeaderMode(Unquoted)
}

// WithUnquotedHeaderValues sets outbound header string quote mode to
// SingleQuoted.
func WithSingleQuoteHeaderValues() Option {
return WithQuotingHeaderMode(SingleQuoted)
}

// WithUnquotedHeaderValues sets outbound header string quote mode to
// DoubleQuoted.
func WithDoubleQuoteHeaderValues() Option {
return WithQuotingHeaderMode(DoubleQuoted)
}

// WithStructuredEncoding sets the encoding selection strategy for
// default encoding selections based on Event, the encoded event will be the
// given version in Structured form.
Expand Down
16 changes: 11 additions & 5 deletions pkg/cloudevents/transport/http/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"log"
"net"
"net/http"
"strconv"
"strings"
"sync"
"time"
Expand All @@ -32,6 +33,9 @@ const (
type Transport struct {
// The encoding used to select the codec for outbound events.
Encoding Encoding

Quoting Quoting

// DefaultEncodingSelectionFn allows for other encoding selection strategies to be injected.
DefaultEncodingSelectionFn EncodingSelector

Expand Down Expand Up @@ -407,24 +411,26 @@ func (t *Transport) ServeHTTP(w http.ResponseWriter, req *http.Request) {
r.Error()
return
}

if resp != nil {
if t.Req != nil {
copyHeaders(t.Req.Header, w.Header())
}
if len(resp.Header) > 0 {
copyHeaders(resp.Header, w.Header())
}
status := http.StatusAccepted
if resp.StatusCode >= 200 && resp.StatusCode < 600 {
status = resp.StatusCode
}
w.WriteHeader(status)
w.Header().Add("Content-Length", strconv.Itoa(len(resp.Body)))
if len(resp.Body) > 0 {
if _, err := w.Write(resp.Body); err != nil {
r.Error()
return
}
}
status := http.StatusAccepted
if resp.StatusCode >= 200 && resp.StatusCode < 600 {
status = resp.StatusCode
}
w.WriteHeader(status)

r.OK()
return
Expand Down
150 changes: 150 additions & 0 deletions test/http/loopback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package http

import (
"context"
"github.com/cloudevents/sdk-go"
"github.com/cloudevents/sdk-go/pkg/cloudevents/client"
cehttp "github.com/cloudevents/sdk-go/pkg/cloudevents/transport/http"
"github.com/cloudevents/sdk-go/pkg/cloudevents/types"
"github.com/google/uuid"
"net/http/httptest"
"testing"
"time"
)

// Loopback Test:

// Obj -> Send -> Wire Format -> Receive -> Got
// Given: ^ ^ ^==Want
// Obj is an event of a version.
// Client is a set to binary or

func AlwaysThen(then time.Time) client.EventDefaulter {
return func(event cloudevents.Event) cloudevents.Event {
if event.Context != nil {
switch event.Context.GetSpecVersion() {
case "0.1":
ec := event.Context.AsV01()
ec.EventTime = &types.Timestamp{Time: then}
event.Context = ec
case "0.2":
ec := event.Context.AsV02()
ec.Time = &types.Timestamp{Time: then}
event.Context = ec
case "0.3":
ec := event.Context.AsV03()
ec.Time = &types.Timestamp{Time: then}
event.Context = ec
}
}
return event
}
}

type TapTest struct {
now time.Time
event *cloudevents.Event
resp *cloudevents.Event
want *cloudevents.Event
asSent *TapValidation
asRecv *TapValidation
}

type TapTestCases map[string]TapTest

func ClientLoopback(t *testing.T, tc TapTest, topts ...cehttp.Option) {
tap := NewTap()
server := httptest.NewServer(tap)
defer server.Close()

if len(topts) == 0 {
topts = append(topts, cloudevents.WithBinaryEncoding())
}
topts = append(topts, cloudevents.WithTarget(server.URL))
transport, err := cloudevents.NewHTTPTransport(
topts...,
)
if err != nil {
t.Fatal(err)
}

tap.handler = transport

ce, err := cloudevents.NewClient(
transport,
cloudevents.WithEventDefaulter(AlwaysThen(tc.now)),
)
if err != nil {
t.Fatal(err)
}

testID := uuid.New().String()
ctx := cloudevents.ContextWithHeader(context.Background(), unitTestIDKey, testID)

recvCtx, recvCancel := context.WithCancel(context.Background())

go func() {
_ = ce.StartReceiver(recvCtx, func(resp *cloudevents.EventResponse) {
if tc.resp != nil {
resp.RespondWith(200, tc.resp)
}
})
}()

got, err := ce.Send(ctx, *tc.event)
if err != nil {
t.Fatal(err)
}
recvCancel()

assertEventEquality(t, "response event", tc.want, got)

if req, ok := tap.req[testID]; ok {
assertTappedEquality(t, "http request", tc.asSent, &req)
}

if resp, ok := tap.resp[testID]; ok {
assertTappedEquality(t, "http response", tc.asRecv, &resp)
}
}

// To help with debug, if needed.
func printTap(t *testing.T, tap *tapHandler, testID string) {
if r, ok := tap.req[testID]; ok {
t.Log("tap request ", r.URI, r.Method)
if r.ContentLength > 0 {
t.Log(" .body: ", r.Body)
} else {
t.Log("tap request had no body.")
}

if len(r.Header) > 0 {
for h, vs := range r.Header {
for _, v := range vs {
t.Logf(" .header %s: %s", h, v)
}
}
} else {
t.Log("tap request had no headers.")
}
}

if r, ok := tap.resp[testID]; ok {
t.Log("tap response.status: ", r.Status)
if r.ContentLength > 0 {
t.Log(" .body: ", r.Body)
} else {
t.Log("tap response had no body.")
}

if len(r.Header) > 0 {
for h, vs := range r.Header {
for _, v := range vs {
t.Logf(" .header %s: %s", h, v)
}
}
} else {
t.Log("tap response had no headers.")
}
}
}
Loading

0 comments on commit 9cc1a4c

Please sign in to comment.