diff --git a/examples/formats/json/go/func.go b/examples/formats/json/go/func.go index b17661c17..f61222d2f 100644 --- a/examples/formats/json/go/func.go +++ b/examples/formats/json/go/func.go @@ -7,6 +7,7 @@ import ( "log" "net/http" "os" + "strconv" ) type Person struct { @@ -34,7 +35,6 @@ func main() { } person := Person{} stderr.Encode(in.Body) - stderr.Encode(fmt.Sprintf(in.Body)) if len(in.Body) != 0 { if err := json.NewDecoder(bytes.NewReader([]byte(in.Body))).Decode(&person); err != nil { log.Fatalf("Unable to decode Person object data: %s", err.Error()) @@ -51,9 +51,13 @@ func main() { log.Fatalf("Unable to marshal JSON response body: %s", err.Error()) fmt.Fprintf(os.Stderr, err.Error()) } + h := http.Header{} + h.Set("Content-Type", "application/json") + h.Set("Content-Length", strconv.Itoa(len(b))) out := &JSON{ StatusCode: http.StatusOK, Body: string(b), + Headers: h, } stderr.Encode(out) if err := stdout.Encode(out); err != nil { diff --git a/test/fn-api-tests/fn/formats/json/go/Dockerfile b/test/fn-api-tests/fn/formats/json/go/Dockerfile new file mode 100644 index 000000000..1c1b324fd --- /dev/null +++ b/test/fn-api-tests/fn/formats/json/go/Dockerfile @@ -0,0 +1,8 @@ +FROM fnproject/go:dev as build-stage +WORKDIR /function +ADD . /src +RUN cd /src && go build -o func +FROM fnproject/go +WORKDIR /function +COPY --from=build-stage /src/func /function/ +ENTRYPOINT ["./func"] diff --git a/test/fn-api-tests/fn/formats/json/go/func.go b/test/fn-api-tests/fn/formats/json/go/func.go new file mode 100644 index 000000000..f61222d2f --- /dev/null +++ b/test/fn-api-tests/fn/formats/json/go/func.go @@ -0,0 +1,68 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "strconv" +) + +type Person struct { + Name string `json:"name"` +} + +type JSON struct { + Headers http.Header `json:"headers"` + Body string `json:"body,omitempty"` + StatusCode int `json:"status,omitempty"` +} + +func main() { + + stdin := json.NewDecoder(os.Stdin) + stdout := json.NewEncoder(os.Stdout) + stderr := json.NewEncoder(os.Stderr) + for { + in := &JSON{} + + err := stdin.Decode(in) + if err != nil { + log.Fatalf("Unable to decode incoming data: %s", err.Error()) + fmt.Fprintf(os.Stderr, err.Error()) + } + person := Person{} + stderr.Encode(in.Body) + if len(in.Body) != 0 { + if err := json.NewDecoder(bytes.NewReader([]byte(in.Body))).Decode(&person); err != nil { + log.Fatalf("Unable to decode Person object data: %s", err.Error()) + fmt.Fprintf(os.Stderr, err.Error()) + } + } + if person.Name == "" { + person.Name = "World" + } + + mapResult := map[string]string{"message": fmt.Sprintf("Hello %s", person.Name)} + b, err := json.Marshal(mapResult) + if err != nil { + log.Fatalf("Unable to marshal JSON response body: %s", err.Error()) + fmt.Fprintf(os.Stderr, err.Error()) + } + h := http.Header{} + h.Set("Content-Type", "application/json") + h.Set("Content-Length", strconv.Itoa(len(b))) + out := &JSON{ + StatusCode: http.StatusOK, + Body: string(b), + Headers: h, + } + stderr.Encode(out) + if err := stdout.Encode(out); err != nil { + log.Fatalf("Unable to encode JSON response: %s", err.Error()) + fmt.Fprintf(os.Stderr, err.Error()) + } + } +} diff --git a/test/fn-api-tests/formats_test.go b/test/fn-api-tests/formats_test.go new file mode 100644 index 000000000..59cc51290 --- /dev/null +++ b/test/fn-api-tests/formats_test.go @@ -0,0 +1,72 @@ +package tests + +import ( + "bytes" + "encoding/json" + "net/url" + "path" + "strconv" + "strings" + "testing" +) + +type JSONResponse struct { + Message string `json:"message"` +} + +func TestFnFormats(t *testing.T) { + + t.Run("test-json-format", func(t *testing.T) { + t.Parallel() + s := SetupDefaultSuite() + + // TODO(treeder): put image in fnproject @ dockerhub + image := "denismakogon/test-hot-json-go:0.0.1" + format := "json" + route := "/test-hot-json-go" + + CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) + CreateRoute(t, s.Context, s.Client, s.AppName, route, image, "sync", + format, s.RouteConfig, s.RouteHeaders) + + u := url.URL{ + Scheme: "http", + Host: Host(), + } + u.Path = path.Join(u.Path, "r", s.AppName, s.RoutePath) + + b, _ := json.Marshal(&struct { + Name string `json:"name"` + }{ + Name: "Jimmy", + }) + content := bytes.NewBuffer(b) + output := &bytes.Buffer{} + headers, err := CallFN(u.String(), content, output, "POST", []string{}) + if err != nil { + t.Errorf("Got unexpected error: %v", err) + } + + msg := &JSONResponse{} + json.Unmarshal(output.Bytes(), msg) + expectedOutput := "Hello Jimmy" + if !strings.Contains(expectedOutput, msg.Message) { + t.Errorf("Assertion error.\n\tExpected: %v\n\tActual: %v", expectedOutput, output.String()) + } + + expectedHeaderNames := []string{"Content-Type", "Content-Length"} + expectedHeaderValues := []string{"application/json; charset=utf-8", strconv.Itoa(output.Len())} + for i, name := range expectedHeaderNames { + actual := headers.Get(name) + expected := expectedHeaderValues[i] + if !strings.Contains(expected, actual) { + t.Errorf("HTTP header assertion error for %v."+ + "\n\tExpected: %v\n\tActual: %v", name, expected, actual) + } + } + + DeleteApp(t, s.Context, s.Client, s.AppName) + + }) + +} diff --git a/test/fn-api-tests/routes_api.go b/test/fn-api-tests/routes_api.go index 89e392242..e5b2a6a7b 100644 --- a/test/fn-api-tests/routes_api.go +++ b/test/fn-api-tests/routes_api.go @@ -89,7 +89,7 @@ func assertRouteFields(t *testing.T, routeObject *models.Route, path, image, rou } -func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, routePath, routeType string, routeConfig map[string]string, headers map[string][]string) (*routes.PostAppsAppRoutesOK, error) { +func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, routePath, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) (*routes.PostAppsAppRoutesOK, error) { cfg := &routes.PostAppsAppRoutesParams{ App: appName, Body: &models.RouteWrapper{ @@ -99,6 +99,7 @@ func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, route Image: image, Path: routePath, Type: routeType, + Format: routeFormat, }, }, Context: ctx, @@ -119,7 +120,7 @@ func createRoute(ctx context.Context, fnclient *client.Fn, appName, image, route } func CreateRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath, image, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) { - routeResponse, err := createRoute(ctx, fnclient, appName, image, routePath, routeType, routeConfig, headers) + routeResponse, err := createRoute(ctx, fnclient, appName, image, routePath, routeType, routeFormat, routeConfig, headers) CheckRouteResponseError(t, err) assertRouteFields(t, routeResponse.Payload.Route, routePath, image, routeType, routeFormat) diff --git a/test/fn-api-tests/routes_test.go b/test/fn-api-tests/routes_test.go index 70c77c319..07815e844 100644 --- a/test/fn-api-tests/routes_test.go +++ b/test/fn-api-tests/routes_test.go @@ -16,7 +16,7 @@ func TestRoutes(t *testing.T) { t.Parallel() s := SetupDefaultSuite() CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) - _, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "", + _, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "", s.Format, s.RouteConfig, s.RouteHeaders) if err == nil { t.Errorf("Should fail with Invalid route Type.") @@ -128,7 +128,8 @@ func TestRoutes(t *testing.T) { CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders) - _, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath, newRouteType, s.RouteConfig, s.RouteHeaders) + _, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath, + newRouteType, s.Format, s.RouteConfig, s.RouteHeaders) if err == nil { t.Errorf("Route duplicate error should appear, but it didn't") }