From 9110dc4745cf10940b0122ab749ceb992268e80e Mon Sep 17 00:00:00 2001
From: Abiola Ibrahim <abiola89@gmail.com>
Date: Wed, 23 Dec 2015 12:11:11 +0100
Subject: [PATCH] Refactor. Tests and tests data.

---
 caddy/setup/rewrite_test.go               | 29 ++++++--
 middleware/rewrite/condition.go           |  2 +-
 middleware/rewrite/condition_test.go      | 90 +++++++++++++++++++++++
 middleware/rewrite/rewrite_test.go        |  4 +-
 middleware/rewrite/testdata/testdir/empty |  0
 middleware/rewrite/testdata/testfile      |  1 +
 middleware/rewrite/to.go                  | 15 +++-
 middleware/rewrite/to_test.go             | 44 +++++++++++
 8 files changed, 173 insertions(+), 12 deletions(-)
 create mode 100644 middleware/rewrite/condition_test.go
 create mode 100644 middleware/rewrite/testdata/testdir/empty
 create mode 100644 middleware/rewrite/testdata/testfile
 create mode 100644 middleware/rewrite/to_test.go

diff --git a/caddy/setup/rewrite_test.go b/caddy/setup/rewrite_test.go
index 13c1372..f3d2e92 100644
--- a/caddy/setup/rewrite_test.go
+++ b/caddy/setup/rewrite_test.go
@@ -1,10 +1,9 @@
 package setup
 
 import (
-	"testing"
-
 	"fmt"
 	"regexp"
+	"testing"
 
 	"github.com/mholt/caddy/middleware/rewrite"
 )
@@ -96,9 +95,9 @@ func TestRewriteParse(t *testing.T) {
 	}{
 		{`rewrite {
 			r	.*
-			to	/to
+			to	/to /index.php?
 		 }`, false, []rewrite.Rule{
-			&rewrite.ComplexRule{Base: "/", To: "/to", Regexp: regexp.MustCompile(".*")},
+			&rewrite.ComplexRule{Base: "/", To: "/to /index.php?", Regexp: regexp.MustCompile(".*")},
 		}},
 		{`rewrite {
 			regexp	.*
@@ -113,11 +112,11 @@ func TestRewriteParse(t *testing.T) {
 		 }
 		 rewrite / {
 		 	regexp	[a-z]+
-		 	to 		/to
+		 	to 		/to /to2
 		 }
 		 `, false, []rewrite.Rule{
 			&rewrite.ComplexRule{Base: "/path", To: "/dest", Regexp: regexp.MustCompile("rr")},
-			&rewrite.ComplexRule{Base: "/", To: "/to", Regexp: regexp.MustCompile("[a-z]+")},
+			&rewrite.ComplexRule{Base: "/", To: "/to /to2", Regexp: regexp.MustCompile("[a-z]+")},
 		}},
 		{`rewrite {
 			r	.*
@@ -132,6 +131,12 @@ func TestRewriteParse(t *testing.T) {
 		{`rewrite /`, true, []rewrite.Rule{
 			&rewrite.ComplexRule{},
 		}},
+		{`rewrite {
+			to	/to
+			if {path} is a
+		 }`, false, []rewrite.Rule{
+			&rewrite.ComplexRule{Base: "/", To: "/to", Ifs: []rewrite.If{rewrite.If{"{path}", "is", "a"}}},
+		}},
 	}
 
 	for i, test := range regexpTests {
@@ -170,10 +175,18 @@ func TestRewriteParse(t *testing.T) {
 					i, j, expectedRule.To, actualRule.To)
 			}
 
-			if actualRule.String() != expectedRule.String() {
+			if actualRule.Regexp != nil {
+				if actualRule.String() != expectedRule.String() {
+					t.Errorf("Test %d, rule %d: Expected Pattern=%s, got %s",
+						i, j, expectedRule.String(), actualRule.String())
+				}
+			}
+
+			if fmt.Sprint(actualRule.Ifs) != fmt.Sprint(expectedRule.Ifs) {
 				t.Errorf("Test %d, rule %d: Expected Pattern=%s, got %s",
-					i, j, expectedRule.String(), actualRule.String())
+					i, j, fmt.Sprint(expectedRule.Ifs), fmt.Sprint(actualRule.Ifs))
 			}
+
 		}
 	}
 
diff --git a/middleware/rewrite/condition.go b/middleware/rewrite/condition.go
index 51d4b3a..c863af4 100644
--- a/middleware/rewrite/condition.go
+++ b/middleware/rewrite/condition.go
@@ -70,7 +70,7 @@ func endsWithFunc(a, b string) bool {
 }
 
 // matchFunc is condition for Match operator.
-// It does regexp matching of
+// It does regexp matching of a against pattern in b
 func matchFunc(a, b string) bool {
 	matched, _ := regexp.MatchString(b, a)
 	return matched
diff --git a/middleware/rewrite/condition_test.go b/middleware/rewrite/condition_test.go
new file mode 100644
index 0000000..7db2064
--- /dev/null
+++ b/middleware/rewrite/condition_test.go
@@ -0,0 +1,90 @@
+package rewrite
+
+import (
+	"net/http"
+	"strings"
+	"testing"
+)
+
+func TestConditions(t *testing.T) {
+	tests := []struct {
+		condition string
+		isTrue    bool
+	}{
+		{"a is b", false},
+		{"a is a", true},
+		{"a not b", true},
+		{"a not a", false},
+		{"a has a", true},
+		{"a has b", false},
+		{"ba has b", true},
+		{"bab has b", true},
+		{"bab has bb", false},
+		{"bab starts_with bb", false},
+		{"bab starts_with ba", true},
+		{"bab starts_with bab", true},
+		{"bab ends_with bb", false},
+		{"bab ends_with bab", true},
+		{"bab ends_with ab", true},
+		{"a match *", false},
+		{"a match a", true},
+		{"a match .*", true},
+		{"a match a.*", true},
+		{"a match b.*", false},
+		{"ba match b.*", true},
+		{"ba match b[a-z]", true},
+		{"b0 match b[a-z]", false},
+		{"b0a match b[a-z]", false},
+		{"b0a match b[a-z]+", false},
+		{"b0a match b[a-z0-9]+", true},
+	}
+
+	for i, test := range tests {
+		str := strings.Fields(test.condition)
+		ifCond, err := NewIf(str[0], str[1], str[2])
+		if err != nil {
+			t.Error(err)
+		}
+		isTrue := ifCond.True(nil)
+		if isTrue != test.isTrue {
+			t.Errorf("Test %v: expected %v found %v", i, test.isTrue, isTrue)
+		}
+	}
+
+	invalidOperators := []string{"ss", "and", "if"}
+	for _, op := range invalidOperators {
+		_, err := NewIf("a", op, "b")
+		if err == nil {
+			t.Error("Invalid operator %v used, expected error.", op)
+		}
+	}
+
+	replaceTests := []struct {
+		url       string
+		condition string
+		isTrue    bool
+	}{
+		{"/home", "{uri} match /home", true},
+		{"/hom", "{uri} match /home", false},
+		{"/hom", "{uri} starts_with /home", false},
+		{"/hom", "{uri} starts_with /h", true},
+		{"/home/.hiddenfile", `{uri} match \/\.(.*)`, true},
+		{"/home/.hiddendir/afile", `{uri} match \/\.(.*)`, true},
+	}
+
+	for i, test := range replaceTests {
+		r, err := http.NewRequest("GET", test.url, nil)
+		if err != nil {
+			t.Error(err)
+		}
+		str := strings.Fields(test.condition)
+		ifCond, err := NewIf(str[0], str[1], str[2])
+		if err != nil {
+			t.Error(err)
+		}
+		isTrue := ifCond.True(r)
+		if isTrue != test.isTrue {
+			t.Errorf("Test %v: expected %v found %v", i, test.isTrue, isTrue)
+		}
+	}
+}
diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go
index 6230f0d..a538b79 100644
--- a/middleware/rewrite/rewrite_test.go
+++ b/middleware/rewrite/rewrite_test.go
@@ -21,7 +21,7 @@ func TestRewrite(t *testing.T) {
 		FileSys: http.Dir("."),
 	}
 
-	regexpRules := [][]string{
+	regexps := [][]string{
 		{"/reg/", ".*", "/to", ""},
 		{"/r/", "[a-z]+", "/toaz", "!.html|"},
 		{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{path}", ""},
@@ -33,7 +33,7 @@ func TestRewrite(t *testing.T) {
 		{"/ab/", `.*\.jpg`, "/ajpg", ""},
 	}
 
-	for _, regexpRule := range regexpRules {
+	for _, regexpRule := range regexps {
 		var ext []string
 		if s := strings.Split(regexpRule[3], "|"); len(s) > 1 {
 			ext = s[:len(s)-1]
diff --git a/middleware/rewrite/testdata/testdir/empty b/middleware/rewrite/testdata/testdir/empty
new file mode 100644
index 0000000..e69de29
diff --git a/middleware/rewrite/testdata/testfile b/middleware/rewrite/testdata/testfile
new file mode 100644
index 0000000..7b4d68d
--- /dev/null
+++ b/middleware/rewrite/testdata/testfile
@@ -0,0 +1 @@
+empty
\ No newline at end of file
diff --git a/middleware/rewrite/to.go b/middleware/rewrite/to.go
index 1dc48fd..d6c7f52 100644
--- a/middleware/rewrite/to.go
+++ b/middleware/rewrite/to.go
@@ -19,6 +19,13 @@ func To(fs http.FileSystem, r *http.Request, to string) bool {
 	t := ""
 	for _, v := range tos {
 		t = path.Clean(replacer.Replace(v))
+
+		// add trailing slash for directories, if present
+		if strings.HasSuffix(v, "/") && !strings.HasSuffix(t, "/") {
+			t += "/"
+		}
+
+		// validate file
 		if isValidFile(fs, t) {
 			break
 		}
@@ -69,5 +76,11 @@ func isValidFile(fs http.FileSystem, file string) bool {
 		return false
 	}
 
-	return strings.HasSuffix(file, "/") && stat.IsDir()
+	// directory
+	if strings.HasSuffix(file, "/") {
+		return stat.IsDir()
+	}
+
+	// file
+	return !stat.IsDir()
 }
diff --git a/middleware/rewrite/to_test.go b/middleware/rewrite/to_test.go
new file mode 100644
index 0000000..2d8b535
--- /dev/null
+++ b/middleware/rewrite/to_test.go
@@ -0,0 +1,44 @@
+package rewrite
+
+import (
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+func TestTo(t *testing.T) {
+	fs := http.Dir("testdata")
+	tests := []struct {
+		url      string
+		to       string
+		expected string
+	}{
+		{"/", "/somefiles", "/somefiles"},
+		{"/somefiles", "/somefiles /index.php{uri}", "/index.php/somefiles"},
+		{"/somefiles", "/testfile /index.php{uri}", "/testfile"},
+		{"/somefiles", "/testfile/ /index.php{uri}", "/index.php/somefiles"},
+		{"/somefiles", "/somefiles /index.php{uri}", "/index.php/somefiles"},
+		{"/?a=b", "/somefiles /index.php?{query}", "/index.php?a=b"},
+		{"/?a=b", "/testfile /index.php?{query}", "/testfile?a=b"},
+		{"/?a=b", "/testdir /index.php?{query}", "/index.php?a=b"},
+		{"/?a=b", "/testdir/ /index.php?{query}", "/testdir/?a=b"},
+	}
+
+	uri := func(r *url.URL) string {
+		uri := r.Path
+		if r.RawQuery != "" {
+			uri += "?" + r.RawQuery
+		}
+		return uri
+	}
+	for i, test := range tests {
+		r, err := http.NewRequest("GET", test.url, nil)
+		if err != nil {
+			t.Error(err)
+		}
+		To(fs, r, test.to)
+		if uri(r.URL) != test.expected {
+			t.Errorf("Test %v: expected %v found %v", i, test.expected, uri(r.URL))
+		}
+	}
+}
-- 
2.30.9