func nextFunc(shouldGzip bool) middleware.Handler { return middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { if shouldGzip { if r.Header.Get("Accept-Encoding") != "" { return 0, fmt.Errorf("Accept-Encoding header not expected") } if w.Header().Get("Content-Encoding") != "gzip" { return 0, fmt.Errorf("Content-Encoding must be gzip, found %v", r.Header.Get("Content-Encoding")) } if _, ok := w.(gzipResponseWriter); !ok { return 0, fmt.Errorf("ResponseWriter should be gzipResponseWriter, found %T", w) } return 0, nil } if r.Header.Get("Accept-Encoding") == "" { return 0, fmt.Errorf("Accept-Encoding header expected") } if w.Header().Get("Content-Encoding") == "gzip" { return 0, fmt.Errorf("Content-Encoding must not be gzip, found gzip") } if _, ok := w.(gzipResponseWriter); ok { return 0, fmt.Errorf("ResponseWriter should not be gzipResponseWriter") } return 0, nil }) }
func TestBrowseTemplate(t *testing.T) { tmpl, err := template.ParseFiles("testdata/photos.tpl") if err != nil { t.Fatalf("An error occured while parsing the template: %v", err) } b := Browse{ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { t.Fatalf("Next shouldn't be called") return 0, nil }), Root: "./testdata", Configs: []Config{ Config{ PathScope: "/photos", Template: tmpl, }, }, } req, err := http.NewRequest("GET", "/photos/", nil) if err != nil { t.Fatalf("Test: Could not create HTTP request: %v", err) } rec := httptest.NewRecorder() b.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Wrong status, expected %d, got %d", http.StatusOK, rec.Code) } respBody := rec.Body.String() expectedBody := `<!DOCTYPE html> <html> <head> <title>Template</title> </head> <body> <h1>Header</h1> <h1>/photos/</h1> <a href="test.html">test.html</a><br> <a href="test2.html">test2.html</a><br> </body> </html> ` if respBody != expectedBody { t.Fatalf("Expected body: %v got: %v", expectedBody, respBody) } }
func TestBasicAuth(t *testing.T) { rw := BasicAuth{ Next: middleware.HandlerFunc(contentHandler), Rules: []Rule{ {Username: "******", Password: "******", Resources: []string{"/testing"}}, }, } tests := []struct { from string result int cred string }{ {"/testing", http.StatusUnauthorized, "ttest:test"}, {"/testing", http.StatusOK, "test:ttest"}, {"/testing", http.StatusUnauthorized, ""}, } for i, test := range tests { req, err := http.NewRequest("GET", test.from, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request %v", i, err) } auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(test.cred)) req.Header.Set("Authorization", auth) rec := httptest.NewRecorder() result, err := rw.ServeHTTP(rec, req) if err != nil { t.Fatalf("Test %d: Could not ServeHTTP %v", i, err) } if result != test.result { t.Errorf("Test %d: Expected Header '%d' but was '%d'", i, test.result, result) } if result == http.StatusUnauthorized { headers := rec.Header() if val, ok := headers["Www-Authenticate"]; ok { if val[0] != "Basic" { t.Errorf("Test %d, Www-Authenticate should be %s provided %s", i, "Basic", val[0]) } } else { t.Errorf("Test %d, should provide a header Www-Authenticate", i) } } } }
func TestRedirect(t *testing.T) { for i, test := range []struct { from string expectedLocation string }{ {"/from", "/to"}, {"/a", "/b"}, {"/aa", ""}, {"/", ""}, {"/a?foo=bar", "/b"}, {"/asdf?foo=bar", ""}, {"/foo#bar", ""}, {"/a#foo", "/b"}, } { var nextCalled bool re := Redirect{ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { nextCalled = true return 0, nil }), Rules: []Rule{ {From: "/from", To: "/to"}, {From: "/a", To: "/b"}, }, } req, err := http.NewRequest("GET", test.from, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) } rec := httptest.NewRecorder() re.ServeHTTP(rec, req) if rec.Header().Get("Location") != test.expectedLocation { t.Errorf("Test %d: Expected Location header to be %q but was %q", i, test.expectedLocation, rec.Header().Get("Location")) } if nextCalled && test.expectedLocation != "" { t.Errorf("Test %d: Next handler was unexpectedly called", i) } } }
func TestMultipleOverlappingRules(t *testing.T) { rw := BasicAuth{ Next: middleware.HandlerFunc(contentHandler), Rules: []Rule{ {Username: "******", Password: "******", Resources: []string{"/t"}}, {Username: "******", Password: "******", Resources: []string{"/t/t"}}, }, } tests := []struct { from string result int cred string }{ {"/t", http.StatusOK, "t:p1"}, {"/t/t", http.StatusOK, "t:p1"}, {"/t/t", http.StatusOK, "t1:p2"}, {"/a", http.StatusOK, "t1:p2"}, {"/t/t", http.StatusUnauthorized, "t1:p3"}, {"/t", http.StatusUnauthorized, "t1:p2"}, } for i, test := range tests { req, err := http.NewRequest("GET", test.from, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request %v", i, err) } auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(test.cred)) req.Header.Set("Authorization", auth) rec := httptest.NewRecorder() result, err := rw.ServeHTTP(rec, req) if err != nil { t.Fatalf("Test %d: Could not ServeHTTP %v", i, err) } if result != test.result { t.Errorf("Test %d: Expected Header '%d' but was '%d'", i, test.result, result) } } }
func TestExtensions(t *testing.T) { rootDir := os.TempDir() // create a temporary page path := filepath.Join(rootDir, "extensions_test.html") _, err := os.Create(path) if err != nil { t.Fatal(err) } defer os.Remove(path) for i, test := range []struct { path string extensions []string expectedURL string }{ {"/extensions_test", []string{".html"}, "/extensions_test.html"}, {"/extensions_test/", []string{".html"}, "/extensions_test/"}, {"/extensions_test", []string{".json"}, "/extensions_test"}, {"/another_test", []string{".html"}, "/another_test"}, } { ex := Ext{ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil }), Root: rootDir, Extensions: test.extensions, } req, err := http.NewRequest("GET", test.path, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) } rec := httptest.NewRecorder() ex.ServeHTTP(rec, req) if got := req.URL.String(); got != test.expectedURL { t.Fatalf("Test %d: Got unexpected request URL: %q, wanted %q", i, got, test.expectedURL) } } }
func TestHeaders(t *testing.T) { for i, test := range []struct { from string name string value string }{ {"/a", "Foo", "Bar"}, {"/a", "Bar", ""}, {"/a", "Baz", ""}, {"/b", "Foo", ""}, {"/b", "Bar", "Removed in /a"}, } { he := Headers{ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil }), Rules: []Rule{ {Path: "/a", Headers: []Header{ {Name: "Foo", Value: "Bar"}, {Name: "-Bar"}, }}, }, } req, err := http.NewRequest("GET", test.from, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) } rec := httptest.NewRecorder() rec.Header().Set("Bar", "Removed in /a") he.ServeHTTP(rec, req) if got := rec.Header().Get(test.name); got != test.value { t.Errorf("Test %d: Expected %s header to be %q but was %q", i, test.name, test.value, got) } } }
func TestInternal(t *testing.T) { im := Internal{ Next: middleware.HandlerFunc(internalTestHandlerFunc), Paths: []string{"/internal"}, } tests := []struct { url string expectedCode int expectedBody string }{ {"/internal", http.StatusNotFound, ""}, {"/public", 0, "/public"}, {"/public/internal", 0, "/public/internal"}, {"/redirect", 0, "/internal"}, {"/cycle", http.StatusInternalServerError, ""}, } for i, test := range tests { req, err := http.NewRequest("GET", test.url, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) } rec := httptest.NewRecorder() code, err := im.ServeHTTP(rec, req) if code != test.expectedCode { t.Errorf("Test %d: Expected status code %d for %s, but got %d", i, test.expectedCode, test.url, code) } if rec.Body.String() != test.expectedBody { t.Errorf("Test %d: Expected body '%s' for %s, but got '%s'", i, test.expectedBody, test.url, rec.Body.String()) } } }
func TestRewrite(t *testing.T) { rw := Rewrite{ Next: middleware.HandlerFunc(urlPrinter), Rules: []Rule{ NewSimpleRule("/from", "/to"), NewSimpleRule("/a", "/b"), }, } regexpRules := [][]string{ []string{"/reg/", ".*", "/to", ""}, []string{"/r/", "[a-z]+", "/toaz", "!.html|"}, []string{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{path}", ""}, []string{"/ab/", "ab", "/ab?{query}", ".txt|"}, []string{"/ab/", "ab", "/ab?type=html&{query}", ".html|"}, []string{"/abc/", "ab", "/abc/{file}", ".html|"}, []string{"/abcd/", "ab", "/a/{dir}/{file}", ".html|"}, []string{"/abcde/", "ab", "/a#{frag}", ".html|"}, []string{"/ab/", `.*\.jpg`, "/ajpg", ""}, } for _, regexpRule := range regexpRules { var ext []string if s := strings.Split(regexpRule[3], "|"); len(s) > 1 { ext = s[:len(s)-1] } rule, err := NewRegexpRule(regexpRule[0], regexpRule[1], regexpRule[2], ext) if err != nil { t.Fatal(err) } rw.Rules = append(rw.Rules, rule) } tests := []struct { from string expectedTo string }{ {"/from", "/to"}, {"/a", "/b"}, {"/aa", "/aa"}, {"/", "/"}, {"/a?foo=bar", "/b?foo=bar"}, {"/asdf?foo=bar", "/asdf?foo=bar"}, {"/foo#bar", "/foo#bar"}, {"/a#foo", "/b#foo"}, {"/reg/foo", "/to"}, {"/re", "/re"}, {"/r/", "/r/"}, {"/r/123", "/r/123"}, {"/r/a123", "/toaz"}, {"/r/abcz", "/toaz"}, {"/r/z", "/toaz"}, {"/r/z.html", "/r/z.html"}, {"/r/z.js", "/toaz"}, {"/url/asAB", "/to/url/asAB"}, {"/url/aBsAB", "/url/aBsAB"}, {"/url/a00sAB", "/to/url/a00sAB"}, {"/url/a0z0sAB", "/to/url/a0z0sAB"}, {"/ab/aa", "/ab/aa"}, {"/ab/ab", "/ab/ab"}, {"/ab/ab.txt", "/ab"}, {"/ab/ab.txt?name=name", "/ab?name=name"}, {"/ab/ab.html?name=name", "/ab?type=html&name=name"}, {"/abc/ab.html", "/abc/ab.html"}, {"/abcd/abcd.html", "/a/abcd/abcd.html"}, {"/abcde/abcde.html", "/a"}, {"/abcde/abcde.html#1234", "/a#1234"}, {"/ab/ab.jpg", "/ajpg"}, } for i, test := range tests { req, err := http.NewRequest("GET", test.from, nil) if err != nil { t.Fatalf("Test %d: Could not create HTTP request: %v", i, err) } rec := httptest.NewRecorder() rw.ServeHTTP(rec, req) if rec.Body.String() != test.expectedTo { t.Errorf("Test %d: Expected URL to be '%s' but was '%s'", i, test.expectedTo, rec.Body.String()) } } }
func Test(t *testing.T) { tmpl := Templates{ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil }), Rules: []Rule{ Rule{ Extensions: []string{".html"}, IndexFiles: []string{"index.html"}, Path: "/photos", }, Rule{ Extensions: []string{".html", ".htm"}, IndexFiles: []string{"index.html", "index.htm"}, Path: "/images", }, }, Root: "./testdata", FileSys: http.Dir("./testdata"), } tmplroot := Templates{ Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil }), Rules: []Rule{ Rule{ Extensions: []string{".html"}, IndexFiles: []string{"index.html"}, Path: "/", }, }, Root: "./testdata", FileSys: http.Dir("./testdata"), } /* * Test tmpl on /photos/test.html */ req, err := http.NewRequest("GET", "/photos/test.html", nil) if err != nil { t.Fatalf("Test: Could not create HTTP request: %v", err) } rec := httptest.NewRecorder() tmpl.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK) } respBody := rec.Body.String() expectedBody := `<!DOCTYPE html><html><head><title>test page</title></head><body><h1>Header title</h1> </body></html> ` if respBody != expectedBody { t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody) } /* * Test tmpl on /images/img.htm */ req, err = http.NewRequest("GET", "/images/img.htm", nil) if err != nil { t.Fatalf("Could not create HTTP request: %v", err) } rec = httptest.NewRecorder() tmpl.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK) } respBody = rec.Body.String() expectedBody = `<!DOCTYPE html><html><head><title>img</title></head><body><h1>Header title</h1> </body></html> ` if respBody != expectedBody { t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody) } /* * Test tmplroot on /root.html */ req, err = http.NewRequest("GET", "/root.html", nil) if err != nil { t.Fatalf("Could not create HTTP request: %v", err) } rec = httptest.NewRecorder() tmplroot.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Test: Wrong response code: %d, should be %d", rec.Code, http.StatusOK) } respBody = rec.Body.String() expectedBody = `<!DOCTYPE html><html><head><title>root</title></head><body><h1>Header title</h1> </body></html> ` if respBody != expectedBody { t.Fatalf("Test: the expected body %v is different from the response one: %v", expectedBody, respBody) } }
func TestMarkdownStaticGen(t *testing.T) { c := NewTestController(`markdown /blog { ext .md template tpl_with_include.html sitegen }`) c.Root = "./testdata" mid, err := Markdown(c) if err != nil { t.Errorf("Expected no errors, got: %v", err) } if mid == nil { t.Fatal("Expected middleware, was nil instead") } for _, start := range c.Startup { err := start() if err != nil { t.Errorf("Startup error: %v", err) } } next := middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { t.Fatalf("Next shouldn't be called") return 0, nil }) hndlr := mid(next) mkdwn, ok := hndlr.(markdown.Markdown) if !ok { t.Fatalf("Was expecting a markdown.Markdown but got %T", hndlr) } expectedStaticFiles := map[string]string{"/blog/first_post.md": "testdata/generated_site/blog/first_post.md/index.html"} if fmt.Sprint(expectedStaticFiles) != fmt.Sprint(mkdwn.Configs[0].StaticFiles) { t.Fatalf("Test expected StaticFiles to be %s, but got %s", fmt.Sprint(expectedStaticFiles), fmt.Sprint(mkdwn.Configs[0].StaticFiles)) } filePath := "testdata/generated_site/blog/first_post.md/index.html" if _, err := os.Stat(filePath); err != nil { t.Fatalf("An error occured when getting the file information: %v", err) } html, err := ioutil.ReadFile(filePath) if err != nil { t.Fatalf("An error occured when getting the file content: %v", err) } expectedBody := `<!DOCTYPE html> <html> <head> <title>first_post</title> </head> <body> <h1>Header title</h1> <h1>Test h1</h1> </body> </html> ` if string(html) != expectedBody { t.Fatalf("Expected file content: %v got: %v", expectedBody, html) } fp := filepath.Join(c.Root, markdown.DefaultStaticDir) if err = os.RemoveAll(fp); err != nil { t.Errorf("Error while removing the generated static files: ", err) } }
func TestMarkdown(t *testing.T) { templates := make(map[string]string) templates[DefaultTemplate] = "testdata/markdown_tpl.html" md := Markdown{ Root: "./testdata", FileSys: http.Dir("./testdata"), Configs: []Config{ Config{ Renderer: blackfriday.HtmlRenderer(0, "", ""), PathScope: "/blog", Extensions: []string{".md"}, Styles: []string{}, Scripts: []string{}, Templates: templates, StaticDir: DefaultStaticDir, StaticFiles: make(map[string]string), }, Config{ Renderer: blackfriday.HtmlRenderer(0, "", ""), PathScope: "/log", Extensions: []string{".md"}, Styles: []string{"/resources/css/log.css", "/resources/css/default.css"}, Scripts: []string{"/resources/js/log.js", "/resources/js/default.js"}, Templates: make(map[string]string), StaticDir: DefaultStaticDir, StaticFiles: make(map[string]string), }, Config{ Renderer: blackfriday.HtmlRenderer(0, "", ""), PathScope: "/og", Extensions: []string{".md"}, Styles: []string{}, Scripts: []string{}, Templates: templates, StaticDir: "testdata/og_static", StaticFiles: map[string]string{"/og/first.md": "testdata/og_static/og/first.md/index.html"}, Links: []PageLink{ PageLink{ Title: "first", Summary: "", Date: time.Now(), URL: "/og/first.md", }, }, }, }, IndexFiles: []string{"index.html"}, Next: middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { t.Fatalf("Next shouldn't be called") return 0, nil }), } for i := range md.Configs { c := &md.Configs[i] if err := GenerateStatic(md, c); err != nil { t.Fatalf("Error: %v", err) } Watch(md, c, time.Millisecond*100) } req, err := http.NewRequest("GET", "/blog/test.md", nil) if err != nil { t.Fatalf("Could not create HTTP request: %v", err) } rec := httptest.NewRecorder() md.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, rec.Code) } respBody := rec.Body.String() expectedBody := `<!DOCTYPE html> <html> <head> <title>Markdown test</title> </head> <body> <h1>Header</h1> Welcome to A Caddy website! <h2>Welcome on the blog</h2> <p>Body</p> <p><code>go func getTrue() bool { return true } </code></p> </body> </html> ` if !equalStrings(respBody, expectedBody) { t.Fatalf("Expected body: %v got: %v", expectedBody, respBody) } req, err = http.NewRequest("GET", "/log/test.md", nil) if err != nil { t.Fatalf("Could not create HTTP request: %v", err) } rec = httptest.NewRecorder() md.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, rec.Code) } respBody = rec.Body.String() expectedBody = `<!DOCTYPE html> <html> <head> <title>Markdown test</title> <meta charset="utf-8"> <link rel="stylesheet" href="/resources/css/log.css"> <link rel="stylesheet" href="/resources/css/default.css"> <script src="/resources/js/log.js"></script> <script src="/resources/js/default.js"></script> </head> <body> <h2>Welcome on the blog</h2> <p>Body</p> <p><code>go func getTrue() bool { return true } </code></p> </body> </html>` if !equalStrings(respBody, expectedBody) { t.Fatalf("Expected body: %v got: %v", expectedBody, respBody) } req, err = http.NewRequest("GET", "/og/first.md", nil) if err != nil { t.Fatalf("Could not create HTTP request: %v", err) } rec = httptest.NewRecorder() currenttime := time.Now().Local().Add(-time.Second) err = os.Chtimes("testdata/og/first.md", currenttime, currenttime) currenttime = time.Now().Local() err = os.Chtimes("testdata/og_static/og/first.md/index.html", currenttime, currenttime) time.Sleep(time.Millisecond * 200) md.ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("Wrong status, expected: %d and got %d", http.StatusOK, rec.Code) } respBody = rec.Body.String() expectedBody = `<!DOCTYPE html> <html> <head> <title>first_post</title> </head> <body> <h1>Header</h1> Welcome to title! <h1>Test h1</h1> </body> </html>` if !equalStrings(respBody, expectedBody) { t.Fatalf("Expected body: %v got: %v", expectedBody, respBody) } expectedLinks := []string{ "/blog/test.md", "/log/test.md", } for i, c := range md.Configs[:2] { log.Printf("Test number: %d, configuration links: %v, config: %v", i, c.Links, c) if c.Links[0].URL != expectedLinks[i] { t.Fatalf("Expected %v got %v", expectedLinks[i], c.Links[0].URL) } } // attempt to trigger race conditions var w sync.WaitGroup f := func() { req, err := http.NewRequest("GET", "/log/test.md", nil) if err != nil { t.Fatalf("Could not create HTTP request: %v", err) } rec := httptest.NewRecorder() md.ServeHTTP(rec, req) w.Done() } for i := 0; i < 5; i++ { w.Add(1) go f() } w.Wait() f = func() { GenerateStatic(md, &md.Configs[0]) w.Done() } for i := 0; i < 5; i++ { w.Add(1) go f() } w.Wait() if err = os.RemoveAll(DefaultStaticDir); err != nil { t.Errorf("Error while removing the generated static files: %v", err) } }
import ( "fmt" "net/http" "strings" "github.com/osfx/snail/config/parse" "github.com/osfx/snail/middleware" "github.com/osfx/snail/server" ) // NewTestController creates a new *Controller for // the input specified, with a filename of "Testfile" func NewTestController(input string) *Controller { return &Controller{ Config: &server.Config{}, Dispenser: parse.NewDispenser("Testfile", strings.NewReader(input)), } } // EmptyNext is a no-op function that can be passed into // middleware.Middleware functions so that the assignment // to the Next field of the Handler can be tested. var EmptyNext = middleware.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil }) // SameNext does a pointer comparison between next1 and next2. func SameNext(next1, next2 middleware.Handler) bool { return fmt.Sprintf("%p", next1) == fmt.Sprintf("%p", next2) }