From 80aad6e9d3af0435ea338266fdde5d0aa8d99d69 Mon Sep 17 00:00:00 2001 From: Awbrey Hughlett Date: Sun, 24 Jan 2021 20:21:20 -0500 Subject: [PATCH] Handle condition for slice of Renderer In the case that a Renderer contains a slice of Renderer, the renderer mothod would not be called. A condition for a reflect.Slice was added. Tests were added to the package to test both cases of a Renderer containing another Renderer and a slice of Renderer. --- render.go | 29 +++++++++--- render_test.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 render_test.go diff --git a/render.go b/render.go index 75a90e2..0899ff9 100644 --- a/render.go +++ b/render.go @@ -70,20 +70,39 @@ func renderer(w http.ResponseWriter, r *http.Request, v Renderer) error { return nil } - // For structs, we call Render on each field that implements Renderer - for i := 0; i < rv.NumField(); i++ { - f := rv.Field(i) + cr := func(f reflect.Value) error { if f.Type().Implements(rendererType) { - if isNil(f) { - continue + return nil } fv := f.Interface().(Renderer) if err := renderer(w, r, fv); err != nil { return err } + } else { + } + return nil + } + // For structs, we call Render on each field that implements Renderer + for i := 0; i < rv.NumField(); i++ { + f := rv.Field(i) + + if f.Kind() == reflect.Slice { + for j := 0; j < f.Len(); j++ { + g := f.Index(j) + + err := cr(g) + if err != nil { + return err + } + } + } else { + err := cr(f) + if err != nil { + return err + } } } diff --git a/render_test.go b/render_test.go new file mode 100644 index 0000000..bedf667 --- /dev/null +++ b/render_test.go @@ -0,0 +1,119 @@ +package render + +import ( + "bufio" + "fmt" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" +) + +type basicRenderInterface struct { + RenderStruct Renderer +} + +func (h *basicRenderInterface) Render(w http.ResponseWriter, r *http.Request) error { + return nil +} + +type baseRenderInterface struct { + PtrVal *string + NoRenderStruct interface{} + RenderList []Renderer + RenderStruct Renderer +} + +func (h *baseRenderInterface) Render(w http.ResponseWriter, r *http.Request) error { + return nil +} + +type otherRenderInterface struct { + Value int +} + +func (o *otherRenderInterface) Render(w http.ResponseWriter, r *http.Request) error { + fmt.Fprintf(w, "%d", o.Value) + return nil +} + +func TestRendererInterface(t *testing.T) { + rv := reflect.ValueOf(newTestStruct()) + if !rv.Type().Implements(rendererType) { + t.Fatal("test element should implement Renderer interface") + } +} + +func TestBasicRenderer(t *testing.T) { + h := &basicRenderInterface{ + RenderStruct: &otherRenderInterface{Value: 2}, + } + + // declare the request + r := req(t, postReq("test")) + + // create the response recorder + w := httptest.NewRecorder() + + err := renderer(w, r, h) + if err != nil { + t.Errorf("error encountered: %s", err) + } + + response := fmt.Sprintf("%s", w.Body) + if response != "2" { + t.Errorf("unexpected response %s; expected 2", response) + } +} + +func TestRenderer(t *testing.T) { + h := newTestStruct() + + // declare the request + r := req(t, postReq("test")) + + // create the response recorder + w := httptest.NewRecorder() + + err := renderer(w, r, h) + if err != nil { + t.Errorf("error encountered: %s", err) + } + + response := fmt.Sprintf("%s", w.Body) + if response != "123" { + t.Errorf("unexpected response %s; expected 123", response) + } +} + +func newTestStruct() *baseRenderInterface { + return &baseRenderInterface{ + PtrVal: nil, + NoRenderStruct: otherRenderInterface{}, + RenderList: []Renderer{ + &otherRenderInterface{Value: 1}, + &otherRenderInterface{Value: 2}, + }, + RenderStruct: &otherRenderInterface{Value: 3}, + } +} + +func req(t testing.TB, v string) *http.Request { + req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(v))) + if err != nil { + t.Fatal(err) + } + return req +} + +func postReq(cont string) string { + post := + `POST / HTTP/1.1 +Content-Type: application/vnd.api+json +User-Agent: mockagent +Content-Length: %d + +%s` + return fmt.Sprintf(post, len(cont), cont) +}