From 8b87d9c8264475bc3c2193f0c7f9ed22141875fa Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 20 Feb 2015 05:08:11 -0800 Subject: [PATCH] Add many tests. - Finish up render package, rename some things. - Add authboss view data/error tests. --- errors.go | 6 +-- errors_test.go | 43 +++++++++++++++++++ internal/render/render.go | 31 +++++++------- internal/render/render_test.go | 75 ++++++++++++++++++++++++++++++++++ views.go | 13 +++++- views_test.go | 49 ++++++++++++++++++++++ 6 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 errors_test.go create mode 100644 internal/render/render_test.go create mode 100644 views_test.go diff --git a/errors.go b/errors.go index d797e10..30855da 100644 --- a/errors.go +++ b/errors.go @@ -33,7 +33,7 @@ type ClientDataErr struct { } func (c ClientDataErr) Error() string { - return fmt.Sprintf("Failed to retrieve client attribute: %s (%v)", c.Name) + return fmt.Sprintf("Failed to retrieve client attribute: %s", c.Name) } // ErrAndRedirect represents a general error whose response should @@ -46,7 +46,7 @@ type ErrAndRedirect struct { } func (e ErrAndRedirect) Error() string { - return fmt.Sprintf("Error: %v, Redirecting to: %s", e.Error, e.Endpoint) + return fmt.Sprintf("Error: %v, Redirecting to: %s", e.Err, e.Endpoint) } // RenderErr represents an error that occured during rendering @@ -58,5 +58,5 @@ type RenderErr struct { } func (r RenderErr) Error() string { - return fmt.Sprintf("Error rendering template %q (%#v): %v", r.TemplateName, r.Data, r.Err) + return fmt.Sprintf("Error rendering template %q: %v, data: %#v", r.TemplateName, r.Err, r.Data) } diff --git a/errors_test.go b/errors_test.go new file mode 100644 index 0000000..994c83d --- /dev/null +++ b/errors_test.go @@ -0,0 +1,43 @@ +package authboss + +import ( + "errors" + "testing" +) + +func TestAttributeErr(t *testing.T) { + estr := "Failed to retrieve database attribute, type was wrong: lol (want: String, got: int)" + if str := MakeAttributeErr("lol", String, 5).Error(); str != estr { + t.Error("Error was wrong:", str) + } + + estr = "Failed to retrieve database attribute: lol" + err := AttributeErr{Name: "lol"} + if str := err.Error(); str != estr { + t.Error("Error was wrong:", str) + } +} + +func TestClientDataErr(t *testing.T) { + estr := "Failed to retrieve client attribute: lol" + err := ClientDataErr{"lol"} + if str := err.Error(); str != estr { + t.Error("Error was wrong:", str) + } +} + +func TestErrAndRedirect(t *testing.T) { + estr := "Error: cause, Redirecting to: /" + err := ErrAndRedirect{errors.New("cause"), "/", "success", "failure"} + if str := err.Error(); str != estr { + t.Error("Error was wrong:", str) + } +} + +func TestRenderErr(t *testing.T) { + estr := `Error rendering template "lol": cause, data: authboss.HTMLData{"a":5}` + err := RenderErr{"lol", NewHTMLData("a", 5), errors.New("cause")} + if str := err.Error(); str != estr { + t.Error("Error was wrong:", str) + } +} diff --git a/internal/render/render.go b/internal/render/render.go index d7dc784..7e23f9f 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -2,6 +2,7 @@ package render import ( "bytes" + "html/template" "io" "net/http" @@ -13,34 +14,36 @@ import ( func View(ctx *authboss.Context, w http.ResponseWriter, r *http.Request, t views.Templates, name string, data authboss.HTMLData) error { tpl, ok := t[name] if !ok { - return authboss.RenderErr{tpl.Name(), data, ErrTemplateNotFound} + return authboss.RenderErr{tpl.Name(), data, views.ErrTemplateNotFound} } - data.Merge("xsrfName", authboss.Cfg.XSRFName, "xsrfToken", authboss.Cfg.XSRFMaker(w, r)) - - if flash, ok := ctx.CookieStorer.Get(authboss.FlashSuccessKey); ok { - ctx.CookieStorer.Del(authboss.FlashSuccessKey) - data.Merge(authboss.FlashSuccessKey, flash) - } - if flash, ok := ctx.CookieStorer.Get(authboss.FlashErrorKey); ok { - ctx.CookieStorer.Del(authboss.FlashErrorKey) - data.Merge(authboss.FlashErrorKey, flash) - } + data.MergeKV("xsrfName", template.HTML(authboss.Cfg.XSRFName), "xsrfToken", template.HTML(authboss.Cfg.XSRFMaker(w, r))) if authboss.Cfg.LayoutDataMaker != nil { data.Merge(authboss.Cfg.LayoutDataMaker(w, r)) } - buffer = &bytes.Buffer{} - err = tpl.ExecuteTemplate(buffer, tpl.Name(), data) + if flash, ok := ctx.CookieStorer.Get(authboss.FlashSuccessKey); ok { + ctx.CookieStorer.Del(authboss.FlashSuccessKey) + data.MergeKV(authboss.FlashSuccessKey, flash) + } + if flash, ok := ctx.CookieStorer.Get(authboss.FlashErrorKey); ok { + ctx.CookieStorer.Del(authboss.FlashErrorKey) + data.MergeKV(authboss.FlashErrorKey, flash) + } + + buffer := &bytes.Buffer{} + err := tpl.ExecuteTemplate(buffer, tpl.Name(), data) if err != nil { return authboss.RenderErr{tpl.Name(), data, err} } - err = io.Copy(w, buffer) + _, err = io.Copy(w, buffer) if err != nil { return authboss.RenderErr{tpl.Name(), data, err} } + + return nil } // Redirect sets any flash messages given and redirects the user. diff --git a/internal/render/render_test.go b/internal/render/render_test.go new file mode 100644 index 0000000..ccab806 --- /dev/null +++ b/internal/render/render_test.go @@ -0,0 +1,75 @@ +package render + +import ( + "html/template" + "net/http" + "net/http/httptest" + "testing" + + "gopkg.in/authboss.v0" + "gopkg.in/authboss.v0/internal/mocks" + "gopkg.in/authboss.v0/internal/views" +) + +var testViewTemplate = template.Must(template.New("").Parse(`{{.external}} {{.fun}} {{.flash_success}} {{.flash_error}} {{.xsrfName}} {{.xsrfToken}}`)) + +func TestView(t *testing.T) { + cookies := mocks.NewMockClientStorer() + authboss.Cfg = &authboss.Config{ + LayoutDataMaker: func(_ http.ResponseWriter, _ *http.Request) authboss.HTMLData { + return authboss.HTMLData{"fun": "is"} + }, + XSRFName: "do you think", + XSRFMaker: func(_ http.ResponseWriter, _ *http.Request) string { + return "that's air you're breathing now?" + }, + } + + // Set up flashes + cookies.Put(authboss.FlashSuccessKey, "no") + cookies.Put(authboss.FlashErrorKey, "spoon") + + r, _ := http.NewRequest("GET", "http://localhost", nil) + w := httptest.NewRecorder() + ctx, _ := authboss.ContextFromRequest(r) + ctx.CookieStorer = cookies + + tpls := views.Templates{ + "hello": testViewTemplate, + } + + err := View(ctx, w, r, tpls, "hello", authboss.HTMLData{"external": "there"}) + if err != nil { + t.Error(err) + } + + if w.Body.String() != "there is no spoon do you think that's air you're breathing now?" { + t.Error("Body was wrong:", w.Body.String()) + } +} + +func TestRedirect(t *testing.T) { + cookies := mocks.NewMockClientStorer() + + r, _ := http.NewRequest("GET", "http://localhost", nil) + w := httptest.NewRecorder() + ctx, _ := authboss.ContextFromRequest(r) + ctx.CookieStorer = cookies + + Redirect(ctx, w, r, "/", "success", "failure") + + if w.Code != http.StatusTemporaryRedirect { + t.Error("Expected a redirect.") + } + + if w.Header().Get("Location") != "/" { + t.Error("Expected to be redirected to root.") + } + + if val, _ := cookies.Get(authboss.FlashSuccessKey); val != "success" { + t.Error("Flash success msg wrong:", val) + } + if val, _ := cookies.Get(authboss.FlashErrorKey); val != "failure" { + t.Error("Flash failure msg wrong:", val) + } +} diff --git a/views.go b/views.go index a7634b5..0bfc0f3 100644 --- a/views.go +++ b/views.go @@ -29,9 +29,18 @@ func NewHTMLData(data ...interface{}) HTMLData { return h } -// Merge adds extra key-values to the HTMLData. The input is a key-value +// Merge adds the data from other to h. If there are conflicting keys +// they are overwritten by other's values. +func (h HTMLData) Merge(other HTMLData) HTMLData { + for k, v := range other { + h[k] = v + } + return h +} + +// MergeKV adds extra key-values to the HTMLData. The input is a key-value // slice, where odd elements are keys, and the following even element is their value. -func (h HTMLData) Merge(data ...interface{}) HTMLData { +func (h HTMLData) MergeKV(data ...interface{}) HTMLData { if len(data)%2 != 0 { panic("It should be a key value list of arguments.") } diff --git a/views_test.go b/views_test.go new file mode 100644 index 0000000..7f64ad0 --- /dev/null +++ b/views_test.go @@ -0,0 +1,49 @@ +package authboss + +import "testing" + +func TestHTMLData(t *testing.T) { + data := NewHTMLData("a", "b").MergeKV("c", "d").Merge(NewHTMLData("e", "f")) + if data["a"].(string) != "b" { + t.Error("A was wrong:", data["a"]) + } + if data["c"].(string) != "d" { + t.Error("C was wrong:", data["c"]) + } + if data["e"].(string) != "f" { + t.Error("E was wrong:", data["e"]) + } +} + +func TestHTMLData_Panics(t *testing.T) { + nPanics := 0 + panicCount := func() { + if r := recover(); r != nil { + nPanics++ + } + } + + func() { + defer panicCount() + NewHTMLData("hello") + }() + + func() { + defer panicCount() + NewHTMLData().MergeKV("hello") + }() + + func() { + defer panicCount() + NewHTMLData(5, 6) + }() + + func() { + defer panicCount() + NewHTMLData().MergeKV(7, 8) + }() + + if nPanics != 4 { + t.Error("They all should have paniced.") + } +}