@@ 2,6 2,7 @@ package gemini
import (
"context"
+ "io"
"net/url"
"testing"
)
@@ 10,6 11,221 @@ type nopHandler struct{}
func (*nopHandler) ServeGemini(context.Context, ResponseWriter, *Request) {}
+type nopResponseWriter struct {
+ Status Status
+ Meta string
+}
+
+func (w *nopResponseWriter) WriteHeader(status Status, meta string) {
+ w.Status = status
+ w.Meta = meta
+}
+
+func (nopResponseWriter) SetMediaType(mediatype string) {}
+func (nopResponseWriter) Write(b []byte) (int, error) { return 0, io.EOF }
+func (nopResponseWriter) Flush() error { return nil }
+
+func TestMux(t *testing.T) {
+ type Test struct {
+ URL string
+ Pattern string
+ Redirect string
+ NotFound bool
+ }
+
+ tests := []struct {
+ Patterns []string
+ Tests []Test
+ }{
+ {
+ Patterns: []string{"/a", "/b/", "/b/c/d", "/b/c/d/"},
+ Tests: []Test{
+ {
+ URL: "gemini://example.com",
+ Redirect: "gemini://example.com/",
+ },
+ {
+ URL: "gemini://example.com/",
+ NotFound: true,
+ },
+ {
+ URL: "gemini://example.com/c",
+ NotFound: true,
+ },
+ {
+ URL: "gemini://example.com/a",
+ Pattern: "/a",
+ },
+ {
+ URL: "gemini://example.com/a/",
+ NotFound: true,
+ },
+ {
+ URL: "gemini://example.com/b",
+ Redirect: "gemini://example.com/b/",
+ },
+ {
+ URL: "gemini://example.com/b/",
+ Pattern: "/b/",
+ },
+ {
+ URL: "gemini://example.com/b/c",
+ Pattern: "/b/",
+ },
+ {
+ URL: "gemini://example.com/b/c/d",
+ Pattern: "/b/c/d",
+ },
+ {
+ URL: "gemini://example.com/b/c/d/e/",
+ Pattern: "/b/c/d/",
+ },
+ },
+ },
+ {
+ Patterns: []string{
+ "/", "/a", "/b/",
+ "example.com", "example.com/a", "example.com/b/",
+ "*.example.com", "*.example.com/a", "*.example.com/b/",
+ },
+ Tests: []Test{
+ {
+ URL: "gemini://example.net/",
+ Pattern: "/",
+ },
+ {
+ URL: "gemini://example.net/a",
+ Pattern: "/a",
+ },
+ {
+ URL: "gemini://example.net/b",
+ Redirect: "gemini://example.net/b/",
+ },
+ {
+ URL: "gemini://example.net/b/",
+ Pattern: "/b/",
+ },
+ {
+ URL: "gemini://example.com/",
+ Pattern: "example.com",
+ },
+ {
+ URL: "gemini://example.com/b",
+ Redirect: "gemini://example.com/b/",
+ },
+ {
+ URL: "gemini://example.com/b/",
+ Pattern: "example.com/b/",
+ },
+ {
+ URL: "gemini://a.example.com/",
+ Pattern: "*.example.com",
+ },
+ {
+ URL: "gemini://b.example.com/a",
+ Pattern: "*.example.com/a",
+ },
+ {
+ URL: "gemini://c.example.com/b",
+ Redirect: "gemini://c.example.com/b/",
+ },
+ {
+ URL: "gemini://d.example.com/b/",
+ Pattern: "*.example.com/b/",
+ },
+ },
+ },
+ {
+ Patterns: []string{"example.net", "*.example.org"},
+ Tests: []Test{
+ {
+ // The following redirect occurs as a result of cleaning
+ // the path provided to the Mux. This happens even if there
+ // are no matching handlers.
+ URL: "gemini://example.com",
+ Redirect: "gemini://example.com/",
+ },
+ {
+ URL: "gemini://example.com/",
+ NotFound: true,
+ },
+ {
+ URL: "gemini://example.net",
+ Redirect: "gemini://example.net/",
+ },
+ {
+ URL: "gemini://example.org/",
+ NotFound: true,
+ },
+ {
+ URL: "gemini://gemini.example.org",
+ Redirect: "gemini://gemini.example.org/",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ type handler struct {
+ nopHandler
+ Pattern string
+ }
+
+ mux := &Mux{}
+ for _, pattern := range test.Patterns {
+ mux.Handle(pattern, &handler{
+ Pattern: pattern,
+ })
+ }
+
+ for _, test := range test.Tests {
+ u, err := url.Parse(test.URL)
+ if err != nil {
+ panic(err)
+ }
+
+ req := &Request{URL: u}
+
+ h := mux.Handler(req)
+
+ if h, ok := h.(*handler); ok {
+ if h.Pattern != test.Pattern {
+ t.Errorf("wrong pattern for %q: expected %q, got %q", test.URL, test.Pattern, h.Pattern)
+ }
+ continue
+ }
+
+ // Check redirects and NotFounds
+ w := &nopResponseWriter{}
+ h.ServeGemini(context.Background(), w, req)
+
+ switch w.Status {
+ case StatusNotFound:
+ if !test.NotFound {
+ t.Errorf("expected pattern for %q, got NotFound", test.URL)
+ }
+
+ case StatusPermanentRedirect:
+ if test.Redirect == "" {
+ t.Errorf("expected pattern for %q, got redirect to %q", test.URL, w.Meta)
+ break
+ }
+
+ res, err := url.Parse(test.Redirect)
+ if err != nil {
+ panic(err)
+ }
+ if w.Meta != res.String() {
+ t.Errorf("bad redirect for %q: expected %q, got %q", test.URL, res.String(), w.Meta)
+ }
+
+ default:
+ t.Errorf("unexpected response for %q: %d %s", test.URL, w.Status, w.Meta)
+ }
+ }
+ }
+}
+
func TestMuxMatch(t *testing.T) {
type Match struct {
URL string
@@ 115,12 331,12 @@ func TestMuxMatch(t *testing.T) {
},
}
- for i, test := range tests {
+ for _, test := range tests {
h := &nopHandler{}
var mux Mux
mux.Handle(test.Pattern, h)
- for _, match := range tests[i].Matches {
+ for _, match := range test.Matches {
u, err := url.Parse(match.URL)
if err != nil {
panic(err)