|  | // Package alice implements a middleware chaining solution. | 
|  | package alice | 
|  |  | 
|  | import ( | 
|  | "net/http" | 
|  | "net/http/httptest" | 
|  | "reflect" | 
|  | "testing" | 
|  | ) | 
|  |  | 
|  | // A constructor for middleware | 
|  | // that writes its own "tag" into the RW and does nothing else. | 
|  | // Useful in checking if a chain is behaving in the right order. | 
|  | func tagMiddleware(tag string) Constructor { | 
|  | return func(h http.Handler) http.Handler { | 
|  | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 
|  | w.Write([]byte(tag)) | 
|  | h.ServeHTTP(w, r) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Not recommended (https://golang.org/pkg/reflect/#Value.Pointer), | 
|  | // but the best we can do. | 
|  | func funcsEqual(f1, f2 interface{}) bool { | 
|  | val1 := reflect.ValueOf(f1) | 
|  | val2 := reflect.ValueOf(f2) | 
|  | return val1.Pointer() == val2.Pointer() | 
|  | } | 
|  |  | 
|  | var testApp = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 
|  | w.Write([]byte("app\n")) | 
|  | }) | 
|  |  | 
|  | func TestNew(t *testing.T) { | 
|  | c1 := func(h http.Handler) http.Handler { | 
|  | return nil | 
|  | } | 
|  |  | 
|  | c2 := func(h http.Handler) http.Handler { | 
|  | return http.StripPrefix("potato", nil) | 
|  | } | 
|  |  | 
|  | slice := []Constructor{c1, c2} | 
|  |  | 
|  | chain := New(slice...) | 
|  | for k := range slice { | 
|  | if !funcsEqual(chain.constructors[k], slice[k]) { | 
|  | t.Error("New does not add constructors correctly") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestThenWorksWithNoMiddleware(t *testing.T) { | 
|  | if !funcsEqual(New().Then(testApp), testApp) { | 
|  | t.Error("Then does not work with no middleware") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestThenTreatsNilAsDefaultServeMux(t *testing.T) { | 
|  | if New().Then(nil) != http.DefaultServeMux { | 
|  | t.Error("Then does not treat nil as DefaultServeMux") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestThenFuncTreatsNilAsDefaultServeMux(t *testing.T) { | 
|  | if New().ThenFunc(nil) != http.DefaultServeMux { | 
|  | t.Error("ThenFunc does not treat nil as DefaultServeMux") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestThenFuncConstructsHandlerFunc(t *testing.T) { | 
|  | fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | 
|  | w.WriteHeader(200) | 
|  | }) | 
|  | chained := New().ThenFunc(fn) | 
|  | rec := httptest.NewRecorder() | 
|  |  | 
|  | chained.ServeHTTP(rec, (*http.Request)(nil)) | 
|  |  | 
|  | if reflect.TypeOf(chained) != reflect.TypeOf((http.HandlerFunc)(nil)) { | 
|  | t.Error("ThenFunc does not construct HandlerFunc") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestThenOrdersHandlersCorrectly(t *testing.T) { | 
|  | t1 := tagMiddleware("t1\n") | 
|  | t2 := tagMiddleware("t2\n") | 
|  | t3 := tagMiddleware("t3\n") | 
|  |  | 
|  | chained := New(t1, t2, t3).Then(testApp) | 
|  |  | 
|  | w := httptest.NewRecorder() | 
|  | r, err := http.NewRequest("GET", "/", nil) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | chained.ServeHTTP(w, r) | 
|  |  | 
|  | if w.Body.String() != "t1\nt2\nt3\napp\n" { | 
|  | t.Error("Then does not order handlers correctly") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestAppendAddsHandlersCorrectly(t *testing.T) { | 
|  | chain := New(tagMiddleware("t1\n"), tagMiddleware("t2\n")) | 
|  | newChain := chain.Append(tagMiddleware("t3\n"), tagMiddleware("t4\n")) | 
|  |  | 
|  | if len(chain.constructors) != 2 { | 
|  | t.Error("chain should have 2 constructors") | 
|  | } | 
|  | if len(newChain.constructors) != 4 { | 
|  | t.Error("newChain should have 4 constructors") | 
|  | } | 
|  |  | 
|  | chained := newChain.Then(testApp) | 
|  |  | 
|  | w := httptest.NewRecorder() | 
|  | r, err := http.NewRequest("GET", "/", nil) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | chained.ServeHTTP(w, r) | 
|  |  | 
|  | if w.Body.String() != "t1\nt2\nt3\nt4\napp\n" { | 
|  | t.Error("Append does not add handlers correctly") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestAppendRespectsImmutability(t *testing.T) { | 
|  | chain := New(tagMiddleware("")) | 
|  | newChain := chain.Append(tagMiddleware("")) | 
|  |  | 
|  | if &chain.constructors[0] == &newChain.constructors[0] { | 
|  | t.Error("Apppend does not respect immutability") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestExtendAddsHandlersCorrectly(t *testing.T) { | 
|  | chain1 := New(tagMiddleware("t1\n"), tagMiddleware("t2\n")) | 
|  | chain2 := New(tagMiddleware("t3\n"), tagMiddleware("t4\n")) | 
|  | newChain := chain1.Extend(chain2) | 
|  |  | 
|  | if len(chain1.constructors) != 2 { | 
|  | t.Error("chain1 should contain 2 constructors") | 
|  | } | 
|  | if len(chain2.constructors) != 2 { | 
|  | t.Error("chain2 should contain 2 constructors") | 
|  | } | 
|  | if len(newChain.constructors) != 4 { | 
|  | t.Error("newChain should contain 4 constructors") | 
|  | } | 
|  |  | 
|  | chained := newChain.Then(testApp) | 
|  |  | 
|  | w := httptest.NewRecorder() | 
|  | r, err := http.NewRequest("GET", "/", nil) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  |  | 
|  | chained.ServeHTTP(w, r) | 
|  |  | 
|  | if w.Body.String() != "t1\nt2\nt3\nt4\napp\n" { | 
|  | t.Error("Extend does not add handlers in correctly") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestExtendRespectsImmutability(t *testing.T) { | 
|  | chain := New(tagMiddleware("")) | 
|  | newChain := chain.Extend(New(tagMiddleware(""))) | 
|  |  | 
|  | if &chain.constructors[0] == &newChain.constructors[0] { | 
|  | t.Error("Extend does not respect immutability") | 
|  | } | 
|  | } |