瀏覽代碼

test: remove the use of goconvey (#6123)

ᴜɴᴋɴᴡᴏɴ 4 年之前
父節點
當前提交
c0fd6042fd

+ 0 - 1
go.mod

@@ -47,7 +47,6 @@ require (
 	github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
 	github.com/satori/go.uuid v1.2.0
 	github.com/sergi/go-diff v1.1.0
-	github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337
 	github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
 	github.com/stretchr/testify v1.5.1
 	github.com/t-tiger/gorm-bulk-insert v1.3.0

+ 0 - 2
go.sum

@@ -56,8 +56,6 @@ github.com/go-macaron/binding v1.1.0 h1:A5jpr5UdHr81Hfmb6QUAMTHyvniudOMcgtEg13TJ
 github.com/go-macaron/binding v1.1.0/go.mod h1:dJU/AtPKG0gUiFra1K5TTGduFGMNxMvfJzV/zmXwyGM=
 github.com/go-macaron/cache v0.0.0-20190810181446-10f7c57e2196 h1:fqWZxyMLF6RVGmjvsZ9FijiU9UlAjuE6nu9RfNBZ+iE=
 github.com/go-macaron/cache v0.0.0-20190810181446-10f7c57e2196/go.mod h1:O6fSdaYZbGh4clVMGMGO5k2KbMO0Cz8YdBnPrD0I8dM=
-github.com/go-macaron/captcha v0.0.0-20190813234938-24f40749f36d h1:aSJXLVjEjbLeHo8aCTDcD3/gMWizaRjMBb3VCsEWEHs=
-github.com/go-macaron/captcha v0.0.0-20190813234938-24f40749f36d/go.mod h1:lmhlZnu9cTRGNQEkSh1qZi2IK3HJH4Z1MXkg6ARQKZA=
 github.com/go-macaron/captcha v0.2.0 h1:d38eYDDF8tdqoM0hJbk+Jb7WQGWlwYNnQwRqLRmSk1Y=
 github.com/go-macaron/captcha v0.2.0/go.mod h1:lmhlZnu9cTRGNQEkSh1qZi2IK3HJH4Z1MXkg6ARQKZA=
 github.com/go-macaron/csrf v0.0.0-20190812063352-946f6d303a4c h1:kFFz1OpaH3+efG7RA33z+D0piwpA/a3x/Zn2d8z9rfw=

+ 7 - 9
internal/avatar/avatar_test.go

@@ -7,17 +7,15 @@ package avatar
 import (
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 )
 
 func Test_RandomImage(t *testing.T) {
-	Convey("Generate a random avatar from email", t, func() {
-		_, err := RandomImage([]byte("gogs@local"))
-		So(err, ShouldBeNil)
+	_, err := RandomImage([]byte("gogs@local"))
+	if err != nil {
+		t.Fatal(err)
+	}
 
-		Convey("Try to generate an image with size zero", func() {
-			_, err := RandomImageSize(0, []byte("gogs@local"))
-			So(err, ShouldNotBeNil)
-		})
-	})
+	_, err = RandomImageSize(0, []byte("gogs@local"))
+	assert.Error(t, err)
 }

+ 19 - 20
internal/db/repo_editor_test.go

@@ -5,30 +5,29 @@
 package db
 
 import (
-	"os"
+	"path/filepath"
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 )
 
 func Test_isRepositoryGitPath(t *testing.T) {
-	Convey("Check if path is or resides inside '.git'", t, func() {
-		sep := string(os.PathSeparator)
-		testCases := []struct {
-			path   string
-			expect bool
-		}{
-			{"." + sep + ".git", true},
-			{"." + sep + ".git" + sep + "", true},
-			{"." + sep + ".git" + sep + "hooks" + sep + "pre-commit", true},
-			{".git" + sep + "hooks", true},
-			{"dir" + sep + ".git", true},
+	tests := []struct {
+		path   string
+		expVal bool
+	}{
+		{path: filepath.Join(".", ".git"), expVal: true},
+		{path: filepath.Join(".", ".git", ""), expVal: true},
+		{path: filepath.Join(".", ".git", "hooks", "pre-commit"), expVal: true},
+		{path: filepath.Join(".git", "hooks"), expVal: true},
+		{path: filepath.Join("dir", ".git"), expVal: true},
 
-			{".gitignore", false},
-			{"dir" + sep + ".gitkeep", false},
-		}
-		for _, tc := range testCases {
-			So(isRepositoryGitPath(tc.path), ShouldEqual, tc.expect)
-		}
-	})
+		{path: filepath.Join(".gitignore"), expVal: false},
+		{path: filepath.Join("dir", ".gitkeep"), expVal: false},
+	}
+	for _, test := range tests {
+		t.Run("", func(t *testing.T) {
+			assert.Equal(t, test.expVal, isRepositoryGitPath(test.path))
+		})
+	}
 }

+ 39 - 53
internal/db/repo_test.go

@@ -1,63 +1,49 @@
-package db_test
+package db
 
 import (
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 
-	"gogs.io/gogs/internal/db"
 	"gogs.io/gogs/internal/markup"
 )
 
-func TestRepo(t *testing.T) {
-	Convey("The metas map", t, func() {
-		var repo = new(db.Repository)
-		repo.Name = "testrepo"
-		repo.Owner = new(db.User)
-		repo.Owner.Name = "testuser"
-		repo.ExternalTrackerFormat = "https://someurl.com/{user}/{repo}/{issue}"
-
-		Convey("When no external tracker is configured", func() {
-			Convey("It should be nil", func() {
-				repo.EnableExternalTracker = false
-				So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
-			})
-			Convey("It should be nil even if other settings are present", func() {
-				repo.EnableExternalTracker = false
-				repo.ExternalTrackerFormat = "http://someurl.com/{user}/{repo}/{issue}"
-				repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
-				So(repo.ComposeMetas(), ShouldEqual, map[string]string(nil))
-			})
-		})
-
-		Convey("When an external issue tracker is configured", func() {
-			repo.EnableExternalTracker = true
-			Convey("It should default to numeric issue style", func() {
-				metas := repo.ComposeMetas()
-				So(metas["style"], ShouldEqual, markup.ISSUE_NAME_STYLE_NUMERIC)
-			})
-			Convey("It should pass through numeric issue style setting", func() {
-				repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
-				metas := repo.ComposeMetas()
-				So(metas["style"], ShouldEqual, markup.ISSUE_NAME_STYLE_NUMERIC)
-			})
-			Convey("It should pass through alphanumeric issue style setting", func() {
-				repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_ALPHANUMERIC
-				metas := repo.ComposeMetas()
-				So(metas["style"], ShouldEqual, markup.ISSUE_NAME_STYLE_ALPHANUMERIC)
-			})
-			Convey("It should contain the user name", func() {
-				metas := repo.ComposeMetas()
-				So(metas["user"], ShouldEqual, "testuser")
-			})
-			Convey("It should contain the repo name", func() {
-				metas := repo.ComposeMetas()
-				So(metas["repo"], ShouldEqual, "testrepo")
-			})
-			Convey("It should contain the URL format", func() {
-				metas := repo.ComposeMetas()
-				So(metas["format"], ShouldEqual, "https://someurl.com/{user}/{repo}/{issue}")
-			})
-		})
+func TestRepository_ComposeMetas(t *testing.T) {
+	repo := &Repository{
+		Name: "testrepo",
+		Owner: &User{
+			Name: "testuser",
+		},
+		ExternalTrackerFormat: "https://someurl.com/{user}/{repo}/{issue}",
+	}
+
+	t.Run("no external tracker is configured", func(t *testing.T) {
+		repo.EnableExternalTracker = false
+		assert.Equal(t, map[string]string(nil), repo.ComposeMetas())
+
+		// Should be nil even if other settings are present
+		repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
+		assert.Equal(t, map[string]string(nil), repo.ComposeMetas())
+	})
+
+	t.Run("an external issue tracker is configured", func(t *testing.T) {
+		repo.EnableExternalTracker = true
+
+		// Default to numeric issue style
+		assert.Equal(t, markup.ISSUE_NAME_STYLE_NUMERIC, repo.ComposeMetas()["style"])
+		repo.ExternalMetas = nil
+
+		repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_NUMERIC
+		assert.Equal(t, markup.ISSUE_NAME_STYLE_NUMERIC, repo.ComposeMetas()["style"])
+		repo.ExternalMetas = nil
+
+		repo.ExternalTrackerStyle = markup.ISSUE_NAME_STYLE_ALPHANUMERIC
+		assert.Equal(t, markup.ISSUE_NAME_STYLE_ALPHANUMERIC, repo.ComposeMetas()["style"])
+		repo.ExternalMetas = nil
+
+		metas := repo.ComposeMetas()
+		assert.Equal(t, "testuser", metas["user"])
+		assert.Equal(t, "testrepo", metas["repo"])
+		assert.Equal(t, "https://someurl.com/{user}/{repo}/{issue}", metas["format"])
 	})
 }

+ 58 - 33
internal/db/ssh_key_test.go

@@ -5,49 +5,74 @@
 package db
 
 import (
-	"fmt"
-	"strings"
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 
 	"gogs.io/gogs/internal/conf"
 )
 
 func Test_SSHParsePublicKey(t *testing.T) {
+	// TODO: Refactor SSHKeyGenParsePublicKey to accept a tempPath and remove this init.
 	conf.MustInit("")
-	testKeys := map[string]struct {
-		typeName string
-		length   int
-		content  string
+	tests := []struct {
+		name      string
+		content   string
+		expType   string
+		expLength int
 	}{
-		"dsa-1024":  {"dsa", 1024, "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment"},
-		"rsa-1024":  {"rsa", 1024, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n"},
-		"rsa-2048":  {"rsa", 2048, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment"},
-		"ecdsa-256": {"ecdsa", 256, "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment"},
-		"ecdsa-384": {"ecdsa", 384, "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment"},
-		// "ecdsa-521": {"ecdsa", 521, "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment"},
+		{
+			name:      "dsa-1024",
+			content:   "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment",
+			expType:   "dsa",
+			expLength: 1024,
+		},
+		{
+			name:      "rsa-1024",
+			content:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment",
+			expType:   "rsa",
+			expLength: 1024,
+		},
+		{
+			name:      "rsa-2048",
+			content:   "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMZXh+1OBUwSH9D45wTaxErQIN9IoC9xl7MKJkqvTvv6O5RR9YW/IK9FbfjXgXsppYGhsCZo1hFOOsXHMnfOORqu/xMDx4yPuyvKpw4LePEcg4TDipaDFuxbWOqc/BUZRZcXu41QAWfDLrInwsltWZHSeG7hjhpacl4FrVv9V1pS6Oc5Q1NxxEzTzuNLS/8diZrTm/YAQQ/+B+mzWI3zEtF4miZjjAljWd1LTBPvU23d29DcBmmFahcZ441XZsTeAwGxG/Q6j8NgNXj9WxMeWwxXV2jeAX/EBSpZrCVlCQ1yJswT6xCp8TuBnTiGWYMBNTbOZvPC4e0WI2/yZW/s5F nocomment",
+			expType:   "rsa",
+			expLength: 2048,
+		},
+		{
+			name:      "ecdsa-256",
+			content:   "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFQacN3PrOll7PXmN5B/ZNVahiUIqI05nbBlZk1KXsO3d06ktAWqbNflv2vEmA38bTFTfJ2sbn2B5ksT52cDDbA= nocomment",
+			expType:   "ecdsa",
+			expLength: 256,
+		},
+		{
+			name:      "ecdsa-384",
+			content:   "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBINmioV+XRX1Fm9Qk2ehHXJ2tfVxW30ypUWZw670Zyq5GQfBAH6xjygRsJ5wWsHXBsGYgFUXIHvMKVAG1tpw7s6ax9oA+dJOJ7tj+vhn8joFqT+sg3LYHgZkHrfqryRasQ== nocomment",
+			expType:   "ecdsa",
+			expLength: 384,
+		},
+		{
+			name:      "ecdsa-521",
+			content:   "ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACGt3UG3EzRwNOI17QR84l6PgiAcvCE7v6aXPj/SC6UWKg4EL8vW9ZBcdYL9wzs4FZXh4MOV8jAzu3KRWNTwb4k2wFNUpGOt7l28MztFFEtH5BDDrtAJSPENPy8pvPLMfnPg5NhvWycqIBzNcHipem5wSJFN5PdpNOC2xMrPWKNqj+ZjQ== nocomment",
+			expType:   "ecdsa",
+			expLength: 521,
+		},
 	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			typ, length, err := SSHNativeParsePublicKey(test.content)
+			if err != nil {
+				t.Fatal(err)
+			}
+			assert.Equal(t, test.expType, typ)
+			assert.Equal(t, test.expLength, length)
 
-	Convey("Parse public keys in both native and ssh-keygen", t, func() {
-		for name, key := range testKeys {
-			fmt.Println("\nTesting key:", name)
-
-			keyTypeN, lengthN, errN := SSHNativeParsePublicKey(key.content)
-			So(errN, ShouldBeNil)
-			So(keyTypeN, ShouldEqual, key.typeName)
-			So(lengthN, ShouldEqual, key.length)
-
-			keyTypeK, lengthK, errK := SSHKeyGenParsePublicKey(key.content)
-			if errK != nil {
-				// Some server just does not support ecdsa format.
-				if strings.Contains(errK.Error(), "line 1 too long:") {
-					continue
-				}
-				So(errK, ShouldBeNil)
+			typ, length, err = SSHKeyGenParsePublicKey(test.content)
+			if err != nil {
+				t.Fatal(err)
 			}
-			So(keyTypeK, ShouldEqual, key.typeName)
-			So(lengthK, ShouldEqual, key.length)
-		}
-	})
+			assert.Equal(t, test.expType, typ)
+			assert.Equal(t, test.expLength, length)
+		})
+	}
 }

+ 49 - 84
internal/markup/markdown_test.go

@@ -10,102 +10,67 @@ import (
 	"testing"
 
 	"github.com/russross/blackfriday"
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 
 	"gogs.io/gogs/internal/conf"
 	. "gogs.io/gogs/internal/markup"
 )
 
 func Test_IsMarkdownFile(t *testing.T) {
+	// TODO: Refactor to accept a list of extensions
 	conf.Markdown.FileExtensions = strings.Split(".md,.markdown,.mdown,.mkd", ",")
-	Convey("Detect Markdown file extension", t, func() {
-		testCases := []struct {
-			ext   string
-			match bool
-		}{
-			{".md", true},
-			{".markdown", true},
-			{".mdown", true},
-			{".mkd", true},
-			{".org", false},
-			{".rst", false},
-			{".asciidoc", false},
-		}
-
-		for _, tc := range testCases {
-			So(IsMarkdownFile(tc.ext), ShouldEqual, tc.match)
-		}
-	})
+	tests := []struct {
+		ext    string
+		expVal bool
+	}{
+		{ext: ".md", expVal: true},
+		{ext: ".markdown", expVal: true},
+		{ext: ".mdown", expVal: true},
+		{ext: ".mkd", expVal: true},
+		{ext: ".org", expVal: false},
+		{ext: ".rst", expVal: false},
+		{ext: ".asciidoc", expVal: false},
+	}
+	for _, test := range tests {
+		assert.Equal(t, test.expVal, IsMarkdownFile(test.ext))
+	}
 }
 
 func Test_Markdown(t *testing.T) {
-	Convey("Rendering an issue URL", t, func() {
-		conf.Server.ExternalURL = "http://localhost:3000/"
-		htmlFlags := 0
-		htmlFlags |= blackfriday.HTML_SKIP_STYLE
-		htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
-		renderer := &MarkdownRenderer{
-			Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
-		}
-		buffer := new(bytes.Buffer)
-		Convey("To the internal issue tracker", func() {
-			Convey("It should render valid issue URLs", func() {
-				testCases := []string{
-					"http://localhost:3000/user/repo/issues/3333", "<a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a>",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
-
-					line, _ := buffer.ReadString(0)
-					So(line, ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should render but not change non-issue URLs", func() {
-				testCases := []string{
-					"http://1111/2222/ssss-issues/3333?param=blah&blahh=333", "<a href=\"http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333</a>",
-					"http://test.com/issues/33333", "<a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a>",
-					"http://test.com/issues/3", "<a href=\"http://test.com/issues/3\">http://test.com/issues/3</a>",
-					"http://issues/333", "<a href=\"http://issues/333\">http://issues/333</a>",
-					"https://issues/333", "<a href=\"https://issues/333\">https://issues/333</a>",
-					"http://tissues/0", "<a href=\"http://tissues/0\">http://tissues/0</a>",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
-
-					line, _ := buffer.ReadString(0)
-					So(line, ShouldEqual, testCases[i+1])
-				}
-			})
-		})
-	})
+	// TODO: Refactor to accept URL
+	conf.Server.ExternalURL = "http://localhost:3000/"
 
-	Convey("Rendering a commit URL", t, func() {
-		conf.Server.ExternalURL = "http://localhost:3000/"
-		htmlFlags := 0
-		htmlFlags |= blackfriday.HTML_SKIP_STYLE
-		htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
-		renderer := &MarkdownRenderer{
-			Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
-		}
-		buffer := new(bytes.Buffer)
-		Convey("To the internal issue tracker", func() {
-			Convey("It should correctly convert URLs", func() {
-				testCases := []string{
-					"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae", " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code>",
-					"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code>",
-					"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", "<a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a>",
-					"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", "<a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a>",
-				}
+	htmlFlags := 0
+	htmlFlags |= blackfriday.HTML_SKIP_STYLE
+	htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
+	renderer := &MarkdownRenderer{
+		Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""),
+	}
 
-				for i := 0; i < len(testCases); i += 2 {
-					renderer.AutoLink(buffer, []byte(testCases[i]), blackfriday.LINK_TYPE_NORMAL)
+	tests := []struct {
+		input  string
+		expVal string
+	}{
+		// Issue URL
+		{input: "http://localhost:3000/user/repo/issues/3333", expVal: "<a href=\"http://localhost:3000/user/repo/issues/3333\">#3333</a>"},
+		{input: "http://1111/2222/ssss-issues/3333?param=blah&blahh=333", expVal: "<a href=\"http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333\">http://1111/2222/ssss-issues/3333?param=blah&amp;blahh=333</a>"},
+		{input: "http://test.com/issues/33333", expVal: "<a href=\"http://test.com/issues/33333\">http://test.com/issues/33333</a>"},
+		{input: "http://test.com/issues/3", expVal: "<a href=\"http://test.com/issues/3\">http://test.com/issues/3</a>"},
+		{input: "http://issues/333", expVal: "<a href=\"http://issues/333\">http://issues/333</a>"},
+		{input: "https://issues/333", expVal: "<a href=\"https://issues/333\">https://issues/333</a>"},
+		{input: "http://tissues/0", expVal: "<a href=\"http://tissues/0\">http://tissues/0</a>"},
 
-					line, _ := buffer.ReadString(0)
-					So(line, ShouldEqual, testCases[i+1])
-				}
-			})
+		// Commit URL
+		{input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae", expVal: " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">d8a994ef24</a></code>"},
+		{input: "http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", expVal: " <code><a href=\"http://localhost:3000/user/project/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">d8a994ef24</a></code>"},
+		{input: "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2", expVal: "<a href=\"https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2\">https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2</a>"},
+		{input: "https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae", expVal: "<a href=\"https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae\">https://commit/d8a994ef243349f321568f9e36d5c3f444b99cae</a>"},
+	}
+	for _, test := range tests {
+		t.Run("", func(t *testing.T) {
+			buf := new(bytes.Buffer)
+			renderer.AutoLink(buf, []byte(test.input), blackfriday.LINK_TYPE_NORMAL)
+			assert.Equal(t, test.expVal, buf.String())
 		})
-	})
+	}
 }

+ 190 - 283
internal/markup/markup_test.go

@@ -5,306 +5,213 @@
 package markup_test
 
 import (
-	"strings"
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 
-	"gogs.io/gogs/internal/conf"
 	. "gogs.io/gogs/internal/markup"
 )
 
 func Test_IsReadmeFile(t *testing.T) {
-	Convey("Detect README file extension", t, func() {
-		testCases := []struct {
-			ext   string
-			match bool
-		}{
-			{"readme", true},
-			{"README", true},
-			{"readme.md", true},
-			{"readme.markdown", true},
-			{"readme.mdown", true},
-			{"readme.mkd", true},
-			{"readme.org", true},
-			{"readme.rst", true},
-			{"readme.asciidoc", true},
-			{"readme_ZH", true},
-		}
-
-		for _, tc := range testCases {
-			So(IsReadmeFile(tc.ext), ShouldEqual, tc.match)
-		}
-	})
+	tests := []struct {
+		name   string
+		expVal bool
+	}{
+		{name: "readme", expVal: true},
+		{name: "README", expVal: true},
+		{name: "readme.md", expVal: true},
+		{name: "readme.markdown", expVal: true},
+		{name: "readme.mdown", expVal: true},
+		{name: "readme.mkd", expVal: true},
+		{name: "readme.org", expVal: true},
+		{name: "readme.rst", expVal: true},
+		{name: "readme.asciidoc", expVal: true},
+		{name: "readme_ZH", expVal: true},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			assert.Equal(t, test.expVal, IsReadmeFile(test.name))
+		})
+	}
 }
 
 func Test_FindAllMentions(t *testing.T) {
-	Convey("Find all mention patterns", t, func() {
-		testCases := []struct {
-			content string
-			matches string
-		}{
-			{"@Unknwon, what do you think?", "Unknwon"},
-			{"@Unknwon what do you think?", "Unknwon"},
-			{"Hi @Unknwon, sounds good to me", "Unknwon"},
-			{"cc/ @Unknwon @User", "Unknwon,User"},
-		}
-
-		for _, tc := range testCases {
-			So(strings.Join(FindAllMentions(tc.content), ","), ShouldEqual, tc.matches)
-		}
-	})
+	tests := []struct {
+		input      string
+		expMatches []string
+	}{
+		{input: "@unknwon, what do you think?", expMatches: []string{"unknwon"}},
+		{input: "@unknwon what do you think?", expMatches: []string{"unknwon"}},
+		{input: "Hi @unknwon, sounds good to me", expMatches: []string{"unknwon"}},
+		{input: "cc/ @unknwon @eddycjy", expMatches: []string{"unknwon", "eddycjy"}},
+	}
+	for _, test := range tests {
+		t.Run("", func(t *testing.T) {
+			assert.Equal(t, test.expMatches, FindAllMentions(test.input))
+		})
+	}
 }
 
 func Test_RenderIssueIndexPattern(t *testing.T) {
-	Convey("Rendering an issue reference", t, func() {
-		var (
-			urlPrefix                   = "/prefix"
-			metas     map[string]string = nil
-		)
-		conf.Server.SubpathDepth = 0
-
-		Convey("To the internal issue tracker", func() {
-			Convey("It should not render anything when there are no mentions", func() {
-				testCases := []string{
-					"",
-					"this is a test",
-					"test 123 123 1234",
-					"#",
-					"# # #",
-					"# 123",
-					"#abcd",
-					"##1234",
-					"test#1234",
-					"#1234test",
-					" test #1234test",
-				}
-
-				for i := 0; i < len(testCases); i++ {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i])
-				}
-			})
-			Convey("It should render freestanding mentions", func() {
-				testCases := []string{
-					"#1234 test", "<a href=\"/prefix/issues/1234\">#1234</a> test",
-					"test #1234 issue", "test <a href=\"/prefix/issues/1234\">#1234</a> issue",
-					"test issue #1234", "test issue <a href=\"/prefix/issues/1234\">#1234</a>",
-					"#5 test", "<a href=\"/prefix/issues/5\">#5</a> test",
-					"test #5 issue", "test <a href=\"/prefix/issues/5\">#5</a> issue",
-					"test issue #5", "test issue <a href=\"/prefix/issues/5\">#5</a>",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should not render issue mention without leading space", func() {
-				input := []byte("test#54321 issue")
-				expected := "test#54321 issue"
-				So(string(RenderIssueIndexPattern(input, urlPrefix, metas)), ShouldEqual, expected)
-			})
-			Convey("It should not render issue mention without trailing space", func() {
-				input := []byte("test #54321issue")
-				expected := "test #54321issue"
-				So(string(RenderIssueIndexPattern(input, urlPrefix, metas)), ShouldEqual, expected)
-			})
-			Convey("It should render issue mention in parentheses", func() {
-				testCases := []string{
-					"(#54321 issue)", "(<a href=\"/prefix/issues/54321\">#54321</a> issue)",
-					"test (#54321) issue", "test (<a href=\"/prefix/issues/54321\">#54321</a>) issue",
-					"test (#54321 extra) issue", "test (<a href=\"/prefix/issues/54321\">#54321</a> extra) issue",
-					"test (#54321 issue)", "test (<a href=\"/prefix/issues/54321\">#54321</a> issue)",
-					"test (#54321)", "test (<a href=\"/prefix/issues/54321\">#54321</a>)",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should render issue mention in square brackets", func() {
-				testCases := []string{
-					"[#54321 issue]", "[<a href=\"/prefix/issues/54321\">#54321</a> issue]",
-					"test [#54321] issue", "test [<a href=\"/prefix/issues/54321\">#54321</a>] issue",
-					"test [#54321 extra] issue", "test [<a href=\"/prefix/issues/54321\">#54321</a> extra] issue",
-					"test [#54321 issue]", "test [<a href=\"/prefix/issues/54321\">#54321</a> issue]",
-					"test [#54321]", "test [<a href=\"/prefix/issues/54321\">#54321</a>]",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should render multiple issue mentions in the same line", func() {
-				testCases := []string{
-					"#54321 #1243", "<a href=\"/prefix/issues/54321\">#54321</a> <a href=\"/prefix/issues/1243\">#1243</a>",
-					"test #54321 #1243", "test <a href=\"/prefix/issues/54321\">#54321</a> <a href=\"/prefix/issues/1243\">#1243</a>",
-					"(#54321 #1243)", "(<a href=\"/prefix/issues/54321\">#54321</a> <a href=\"/prefix/issues/1243\">#1243</a>)",
-					"(#54321)(#1243)", "(<a href=\"/prefix/issues/54321\">#54321</a>)(<a href=\"/prefix/issues/1243\">#1243</a>)",
-					"text #54321 test #1243 issue", "text <a href=\"/prefix/issues/54321\">#54321</a> test <a href=\"/prefix/issues/1243\">#1243</a> issue",
-					"#1 (#4321) test", "<a href=\"/prefix/issues/1\">#1</a> (<a href=\"/prefix/issues/4321\">#4321</a>) test",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-		})
-		Convey("To an external issue tracker with numeric style", func() {
-			metas = make(map[string]string)
-			metas["format"] = "https://someurl.com/{user}/{repo}/{index}"
-			metas["user"] = "someuser"
-			metas["repo"] = "somerepo"
-			metas["style"] = ISSUE_NAME_STYLE_NUMERIC
-
-			Convey("should not render anything when there are no mentions", func() {
-				testCases := []string{
-					"this is a test",
-					"test 123 123 1234",
-					"#",
-					"# # #",
-					"# 123",
-					"#abcd",
-				}
-
-				for i := 0; i < len(testCases); i++ {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i])
-				}
-			})
-			Convey("It should render freestanding issue mentions", func() {
-				testCases := []string{
-					"#1234 test", "<a href=\"https://someurl.com/someuser/somerepo/1234\">#1234</a> test",
-					"test #1234 issue", "test <a href=\"https://someurl.com/someuser/somerepo/1234\">#1234</a> issue",
-					"test issue #1234", "test issue <a href=\"https://someurl.com/someuser/somerepo/1234\">#1234</a>",
-					"#5 test", "<a href=\"https://someurl.com/someuser/somerepo/5\">#5</a> test",
-					"test #5 issue", "test <a href=\"https://someurl.com/someuser/somerepo/5\">#5</a> issue",
-					"test issue #5", "test issue <a href=\"https://someurl.com/someuser/somerepo/5\">#5</a>",
-				}
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should not render issue mention without leading space", func() {
-				input := []byte("test#54321 issue")
-				expected := "test#54321 issue"
-				So(string(RenderIssueIndexPattern(input, urlPrefix, metas)), ShouldEqual, expected)
-			})
-			Convey("It should not render issue mention without trailing space", func() {
-				input := []byte("test #54321issue")
-				expected := "test #54321issue"
-				So(string(RenderIssueIndexPattern(input, urlPrefix, metas)), ShouldEqual, expected)
-			})
-			Convey("It should render issue mention in parentheses", func() {
-				testCases := []string{
-					"(#54321 issue)", "(<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> issue)",
-					"test (#54321) issue", "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a>) issue",
-					"test (#54321 extra) issue", "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> extra) issue",
-					"test (#54321 issue)", "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> issue)",
-					"test (#54321)", "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a>)",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
+	urlPrefix := "/prefix"
+	t.Run("render to internal issue tracker", func(t *testing.T) {
+		tests := []struct {
+			input  string
+			expVal string
+		}{
+			{input: "", expVal: ""},
+			{input: "this is a test", expVal: "this is a test"},
+			{input: "test 123 123 1234", expVal: "test 123 123 1234"},
+			{input: "#", expVal: "#"},
+			{input: "# # #", expVal: "# # #"},
+			{input: "# 123", expVal: "# 123"},
+			{input: "#abcd", expVal: "#abcd"},
+			{input: "##1234", expVal: "##1234"},
+			{input: "test#1234", expVal: "test#1234"},
+			{input: "#1234test", expVal: "#1234test"},
+			{input: " test #1234test", expVal: " test #1234test"},
+
+			{input: "#1234 test", expVal: "<a href=\"/prefix/issues/1234\">#1234</a> test"},
+			{input: "test #1234 issue", expVal: "test <a href=\"/prefix/issues/1234\">#1234</a> issue"},
+			{input: "test issue #1234", expVal: "test issue <a href=\"/prefix/issues/1234\">#1234</a>"},
+			{input: "#5 test", expVal: "<a href=\"/prefix/issues/5\">#5</a> test"},
+			{input: "test #5 issue", expVal: "test <a href=\"/prefix/issues/5\">#5</a> issue"},
+			{input: "test issue #5", expVal: "test issue <a href=\"/prefix/issues/5\">#5</a>"},
+
+			{input: "(#54321 issue)", expVal: "(<a href=\"/prefix/issues/54321\">#54321</a> issue)"},
+			{input: "test (#54321) issue", expVal: "test (<a href=\"/prefix/issues/54321\">#54321</a>) issue"},
+			{input: "test (#54321 extra) issue", expVal: "test (<a href=\"/prefix/issues/54321\">#54321</a> extra) issue"},
+			{input: "test (#54321 issue)", expVal: "test (<a href=\"/prefix/issues/54321\">#54321</a> issue)"},
+			{input: "test (#54321)", expVal: "test (<a href=\"/prefix/issues/54321\">#54321</a>)"},
+
+			{input: "[#54321 issue]", expVal: "[<a href=\"/prefix/issues/54321\">#54321</a> issue]"},
+			{input: "test [#54321] issue", expVal: "test [<a href=\"/prefix/issues/54321\">#54321</a>] issue"},
+			{input: "test [#54321 extra] issue", expVal: "test [<a href=\"/prefix/issues/54321\">#54321</a> extra] issue"},
+			{input: "test [#54321 issue]", expVal: "test [<a href=\"/prefix/issues/54321\">#54321</a> issue]"},
+			{input: "test [#54321]", expVal: "test [<a href=\"/prefix/issues/54321\">#54321</a>]"},
+
+			{input: "#54321 #1243", expVal: "<a href=\"/prefix/issues/54321\">#54321</a> <a href=\"/prefix/issues/1243\">#1243</a>"},
+			{input: "test #54321 #1243", expVal: "test <a href=\"/prefix/issues/54321\">#54321</a> <a href=\"/prefix/issues/1243\">#1243</a>"},
+			{input: "(#54321 #1243)", expVal: "(<a href=\"/prefix/issues/54321\">#54321</a> <a href=\"/prefix/issues/1243\">#1243</a>)"},
+			{input: "(#54321)(#1243)", expVal: "(<a href=\"/prefix/issues/54321\">#54321</a>)(<a href=\"/prefix/issues/1243\">#1243</a>)"},
+			{input: "text #54321 test #1243 issue", expVal: "text <a href=\"/prefix/issues/54321\">#54321</a> test <a href=\"/prefix/issues/1243\">#1243</a> issue"},
+			{input: "#1 (#4321) test", expVal: "<a href=\"/prefix/issues/1\">#1</a> (<a href=\"/prefix/issues/4321\">#4321</a>) test"},
+		}
+		for _, test := range tests {
+			t.Run(test.input, func(t *testing.T) {
+				assert.Equal(t, test.expVal, string(RenderIssueIndexPattern([]byte(test.input), urlPrefix, nil)))
 			})
-			Convey("It should render multiple issue mentions in the same line", func() {
-				testCases := []string{
-					"#54321 #1243", "<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>",
-					"test #54321 #1243", "test <a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>",
-					"(#54321 #1243)", "(<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>)",
-					"(#54321)(#1243)", "(<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a>)(<a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>)",
-					"text #54321 test #1243 issue", "text <a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> test <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a> issue",
-					"#1 (#4321) test", "<a href=\"https://someurl.com/someuser/somerepo/1\">#1</a> (<a href=\"https://someurl.com/someuser/somerepo/4321\">#4321</a>) test",
-				}
+		}
+	})
 
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
+	t.Run("render to external issue tracker", func(t *testing.T) {
+		t.Run("numeric style", func(t *testing.T) {
+			metas := map[string]string{
+				"format": "https://someurl.com/{user}/{repo}/{index}",
+				"user":   "someuser",
+				"repo":   "somerepo",
+				"style":  ISSUE_NAME_STYLE_NUMERIC,
+			}
+
+			tests := []struct {
+				input  string
+				expVal string
+			}{
+				{input: "this is a test", expVal: "this is a test"},
+				{input: "test 123 123 1234", expVal: "test 123 123 1234"},
+				{input: "#", expVal: "#"},
+				{input: "# # #", expVal: "# # #"},
+				{input: "# 123", expVal: "# 123"},
+				{input: "#abcd", expVal: "#abcd"},
+
+				{input: "#1234 test", expVal: "<a href=\"https://someurl.com/someuser/somerepo/1234\">#1234</a> test"},
+				{input: "test #1234 issue", expVal: "test <a href=\"https://someurl.com/someuser/somerepo/1234\">#1234</a> issue"},
+				{input: "test issue #1234", expVal: "test issue <a href=\"https://someurl.com/someuser/somerepo/1234\">#1234</a>"},
+				{input: "#5 test", expVal: "<a href=\"https://someurl.com/someuser/somerepo/5\">#5</a> test"},
+				{input: "test #5 issue", expVal: "test <a href=\"https://someurl.com/someuser/somerepo/5\">#5</a> issue"},
+				{input: "test issue #5", expVal: "test issue <a href=\"https://someurl.com/someuser/somerepo/5\">#5</a>"},
+
+				{input: "(#54321 issue)", expVal: "(<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> issue)"},
+				{input: "test (#54321) issue", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a>) issue"},
+				{input: "test (#54321 extra) issue", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> extra) issue"},
+				{input: "test (#54321 issue)", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> issue)"},
+				{input: "test (#54321)", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a>)"},
+
+				{input: "#54321 #1243", expVal: "<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>"},
+				{input: "test #54321 #1243", expVal: "test <a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>"},
+				{input: "(#54321 #1243)", expVal: "(<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>)"},
+				{input: "(#54321)(#1243)", expVal: "(<a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a>)(<a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a>)"},
+				{input: "text #54321 test #1243 issue", expVal: "text <a href=\"https://someurl.com/someuser/somerepo/54321\">#54321</a> test <a href=\"https://someurl.com/someuser/somerepo/1243\">#1243</a> issue"},
+				{input: "#1 (#4321) test", expVal: "<a href=\"https://someurl.com/someuser/somerepo/1\">#1</a> (<a href=\"https://someurl.com/someuser/somerepo/4321\">#4321</a>) test"},
+			}
+			for _, test := range tests {
+				t.Run(test.input, func(t *testing.T) {
+					assert.Equal(t, test.expVal, string(RenderIssueIndexPattern([]byte(test.input), urlPrefix, metas)))
+				})
+			}
 		})
-		Convey("To an external issue tracker with alphanumeric style", func() {
-			metas = make(map[string]string)
-			metas["format"] = "https://someurl.com/{user}/{repo}/?b={index}"
-			metas["user"] = "someuser"
-			metas["repo"] = "somerepo"
-			metas["style"] = ISSUE_NAME_STYLE_ALPHANUMERIC
-			Convey("It should not render anything when there are no mentions", func() {
-				testCases := []string{
-					"",
-					"this is a test",
-					"test 123 123 1234",
-					"#",
-					"##1234",
-					"# 123",
-					"#abcd",
-					"test #123",
-					"abc-1234",         // issue prefix must be capital
-					"ABc-1234",         // issue prefix must be _all_ capital
-					"ABCDEFGHIJK-1234", // the limit is 10 characters in the prefix
-					"ABC1234",          // dash is required
-					"test ABC- test",   // number is required
-					"test -1234 test",  // prefix is required
-					"testABC-123 test", // leading space is required
-					"test ABC-123test", // trailing space is required
-					"ABC-0123",         // no leading zero
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i])
-				}
-			})
-			Convey("It should render freestanding issue mention", func() {
-				testCases := []string{
-					"OTT-1234 test", "<a href=\"https://someurl.com/someuser/somerepo/?b=OTT-1234\">OTT-1234</a> test",
-					"test T-12 issue", "test <a href=\"https://someurl.com/someuser/somerepo/?b=T-12\">T-12</a> issue",
-					"test issue ABCDEFGHIJ-1234567890", "test issue <a href=\"https://someurl.com/someuser/somerepo/?b=ABCDEFGHIJ-1234567890\">ABCDEFGHIJ-1234567890</a>",
-					"A-1 test", "<a href=\"https://someurl.com/someuser/somerepo/?b=A-1\">A-1</a> test",
-					"test ZED-1 issue", "test <a href=\"https://someurl.com/someuser/somerepo/?b=ZED-1\">ZED-1</a> issue",
-					"test issue DEED-7154", "test issue <a href=\"https://someurl.com/someuser/somerepo/?b=DEED-7154\">DEED-7154</a>",
-				}
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should render issue mention in parentheses", func() {
-				testCases := []string{
-					"(ABG-124 issue)", "(<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> issue)",
-					"test (ABG-124) issue", "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>) issue",
-					"test (ABG-124 extra) issue", "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> extra) issue",
-					"test (ABG-124 issue)", "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> issue)",
-					"test (ABG-124)", "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>)",
-				}
 
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should render issue mention in square brackets", func() {
-				testCases := []string{
-					"[ABG-124] issue", "[<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>] issue",
-					"test [ABG-124] issue", "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>] issue",
-					"test [ABG-124 extra] issue", "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> extra] issue",
-					"test [ABG-124 issue]", "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> issue]",
-					"test [ABG-124]", "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>]",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
-			Convey("It should render multiple issue mentions in the same line", func() {
-				testCases := []string{
-					"ABG-124 OTT-4321", "<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>",
-					"test ABG-124 OTT-4321", "test <a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>",
-					"(ABG-124 OTT-4321)", "(<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>)",
-					"(ABG-124)(OTT-4321)", "(<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>)(<a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>)",
-					"text ABG-124 test OTT-4321 issue", "text <a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> test <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a> issue",
-					"A-1 (RRE-345) test", "<a href=\"https://someurl.com/someuser/somerepo/?b=A-1\">A-1</a> (<a href=\"https://someurl.com/someuser/somerepo/?b=RRE-345\">RRE-345</a>) test",
-				}
-
-				for i := 0; i < len(testCases); i += 2 {
-					So(string(RenderIssueIndexPattern([]byte(testCases[i]), urlPrefix, metas)), ShouldEqual, testCases[i+1])
-				}
-			})
+		t.Run("alphanumeric style", func(t *testing.T) {
+			metas := map[string]string{
+				"format": "https://someurl.com/{user}/{repo}/?b={index}",
+				"user":   "someuser",
+				"repo":   "somerepo",
+				"style":  ISSUE_NAME_STYLE_ALPHANUMERIC,
+			}
+
+			tests := []struct {
+				input  string
+				expVal string
+			}{
+				{input: "", expVal: ""},
+				{input: "this is a test", expVal: "this is a test"},
+				{input: "test 123 123 1234", expVal: "test 123 123 1234"},
+				{input: "#", expVal: "#"},
+				{input: "##1234", expVal: "##1234"},
+				{input: "# 123", expVal: "# 123"},
+				{input: "#abcd", expVal: "#abcd"},
+				{input: "test #123", expVal: "test #123"},
+				{input: "abc-1234", expVal: "abc-1234"},                 // issue prefix must be capital
+				{input: "ABc-1234", expVal: "ABc-1234"},                 // issue prefix must be _all_ capital
+				{input: "ABCDEFGHIJK-1234", expVal: "ABCDEFGHIJK-1234"}, // the limit is 10 characters in the prefix
+				{input: "ABC1234", expVal: "ABC1234"},                   // dash is required
+				{input: "test ABC- test", expVal: "test ABC- test"},     // number is required
+				{input: "test -1234 test", expVal: "test -1234 test"},   // prefix is required
+				{input: "testABC-123 test", expVal: "testABC-123 test"}, // leading space is required
+				{input: "test ABC-123test", expVal: "test ABC-123test"}, // trailing space is required
+				{input: "ABC-0123", expVal: "ABC-0123"},                 // no leading zero
+
+				{input: "OTT-1234 test", expVal: "<a href=\"https://someurl.com/someuser/somerepo/?b=OTT-1234\">OTT-1234</a> test"},
+				{input: "test T-12 issue", expVal: "test <a href=\"https://someurl.com/someuser/somerepo/?b=T-12\">T-12</a> issue"},
+				{input: "test issue ABCDEFGHIJ-1234567890", expVal: "test issue <a href=\"https://someurl.com/someuser/somerepo/?b=ABCDEFGHIJ-1234567890\">ABCDEFGHIJ-1234567890</a>"},
+				{input: "A-1 test", expVal: "<a href=\"https://someurl.com/someuser/somerepo/?b=A-1\">A-1</a> test"},
+				{input: "test ZED-1 issue", expVal: "test <a href=\"https://someurl.com/someuser/somerepo/?b=ZED-1\">ZED-1</a> issue"},
+				{input: "test issue DEED-7154", expVal: "test issue <a href=\"https://someurl.com/someuser/somerepo/?b=DEED-7154\">DEED-7154</a>"},
+
+				{input: "(ABG-124 issue)", expVal: "(<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> issue)"},
+				{input: "test (ABG-124) issue", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>) issue"},
+				{input: "test (ABG-124 extra) issue", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> extra) issue"},
+				{input: "test (ABG-124 issue)", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> issue)"},
+				{input: "test (ABG-124)", expVal: "test (<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>)"},
+
+				{input: "[ABG-124] issue", expVal: "[<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>] issue"},
+				{input: "test [ABG-124] issue", expVal: "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>] issue"},
+				{input: "test [ABG-124 extra] issue", expVal: "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> extra] issue"},
+				{input: "test [ABG-124 issue]", expVal: "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> issue]"},
+				{input: "test [ABG-124]", expVal: "test [<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>]"},
+
+				{input: "ABG-124 OTT-4321", expVal: "<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>"},
+				{input: "test ABG-124 OTT-4321", expVal: "test <a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>"},
+				{input: "(ABG-124 OTT-4321)", expVal: "(<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>)"},
+				{input: "(ABG-124)(OTT-4321)", expVal: "(<a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a>)(<a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a>)"},
+				{input: "text ABG-124 test OTT-4321 issue", expVal: "text <a href=\"https://someurl.com/someuser/somerepo/?b=ABG-124\">ABG-124</a> test <a href=\"https://someurl.com/someuser/somerepo/?b=OTT-4321\">OTT-4321</a> issue"},
+				{input: "A-1 (RRE-345) test", expVal: "<a href=\"https://someurl.com/someuser/somerepo/?b=A-1\">A-1</a> (<a href=\"https://someurl.com/someuser/somerepo/?b=RRE-345\">RRE-345</a>) test"},
+			}
+			for _, test := range tests {
+				t.Run(test.input, func(t *testing.T) {
+					assert.Equal(t, test.expVal, string(RenderIssueIndexPattern([]byte(test.input), urlPrefix, metas)))
+				})
+			}
 		})
 	})
 }

+ 24 - 22
internal/markup/sanitizer_test.go

@@ -7,32 +7,34 @@ package markup_test
 import (
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 
 	. "gogs.io/gogs/internal/markup"
 )
 
 func Test_Sanitizer(t *testing.T) {
 	NewSanitizer()
-	Convey("Sanitize HTML string and bytes", t, func() {
-		testCases := []string{
-			// Regular
-			`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, `<a href="http://www.google.com" rel="nofollow">Google</a>`,
-
-			// Code highlighting class
-			`<code class="random string"></code>`, `<code></code>`,
-			`<code class="language-random ui tab active menu attached animating sidebar following bar center"></code>`, `<code></code>`,
-			`<code class="language-go"></code>`, `<code class="language-go"></code>`,
-
-			// Input checkbox
-			`<input type="hidden">`, ``,
-			`<input type="checkbox">`, `<input type="checkbox">`,
-			`<input checked disabled autofocus>`, `<input checked="" disabled="">`,
-		}
-
-		for i := 0; i < len(testCases); i += 2 {
-			So(Sanitize(testCases[i]), ShouldEqual, testCases[i+1])
-			So(string(SanitizeBytes([]byte(testCases[i]))), ShouldEqual, testCases[i+1])
-		}
-	})
+	tests := []struct {
+		input  string
+		expVal string
+	}{
+		// Regular
+		{input: `<a onblur="alert(secret)" href="http://www.google.com">Google</a>`, expVal: `<a href="http://www.google.com" rel="nofollow">Google</a>`},
+
+		// Code highlighting class
+		{input: `<code class="random string"></code>`, expVal: `<code></code>`},
+		{input: `<code class="language-random ui tab active menu attached animating sidebar following bar center"></code>`, expVal: `<code></code>`},
+		{input: `<code class="language-go"></code>`, expVal: `<code class="language-go"></code>`},
+
+		// Input checkbox
+		{input: `<input type="hidden">`, expVal: ``},
+		{input: `<input type="checkbox">`, expVal: `<input type="checkbox">`},
+		{input: `<input checked disabled autofocus>`, expVal: `<input checked="" disabled="">`},
+	}
+	for _, test := range tests {
+		t.Run(test.input, func(t *testing.T) {
+			assert.Equal(t, test.expVal, Sanitize(test.input))
+			assert.Equal(t, test.expVal, string(SanitizeBytes([]byte(test.input))))
+		})
+	}
 }

+ 37 - 37
internal/tool/path_test.go

@@ -7,47 +7,47 @@ package tool
 import (
 	"testing"
 
-	. "github.com/smartystreets/goconvey/convey"
+	"github.com/stretchr/testify/assert"
 )
 
 func Test_IsSameSiteURLPath(t *testing.T) {
-	Convey("Check if a path belongs to the same site", t, func() {
-		testCases := []struct {
-			url    string
-			expect bool
-		}{
-			{"//github.com", false},
-			{"http://github.com", false},
-			{"https://github.com", false},
-			{"/\\github.com", false},
-
-			{"/admin", true},
-			{"/user/repo", true},
-		}
-
-		for _, tc := range testCases {
-			So(IsSameSiteURLPath(tc.url), ShouldEqual, tc.expect)
-		}
-	})
+	tests := []struct {
+		url    string
+		expVal bool
+	}{
+		{url: "//github.com", expVal: false},
+		{url: "http://github.com", expVal: false},
+		{url: "https://github.com", expVal: false},
+		{url: "/\\github.com", expVal: false},
+
+		{url: "/admin", expVal: true},
+		{url: "/user/repo", expVal: true},
+	}
+
+	for _, test := range tests {
+		t.Run(test.url, func(t *testing.T) {
+			assert.Equal(t, test.expVal, IsSameSiteURLPath(test.url))
+		})
+	}
 }
 
 func Test_IsMaliciousPath(t *testing.T) {
-	Convey("Detects malicious path", t, func() {
-		testCases := []struct {
-			path   string
-			expect bool
-		}{
-			{"../../../../../../../../../data/gogs/data/sessions/a/9/a9f0ab6c3ef63dd8", true},
-			{"..\\/..\\/../data/gogs/data/sessions/a/9/a9f0ab6c3ef63dd8", true},
-			{"data/gogs/../../../../../../../../../data/sessions/a/9/a9f0ab6c3ef63dd8", true},
-			{"..\\..\\..\\..\\..\\..\\..\\..\\..\\data\\gogs\\data\\sessions\\a\\9\\a9f0ab6c3ef63dd8", true},
-			{"data\\gogs\\..\\..\\..\\..\\..\\..\\..\\..\\..\\data\\sessions\\a\\9\\a9f0ab6c3ef63dd8", true},
-
-			{"data/sessions/a/9/a9f0ab6c3ef63dd8", false},
-			{"data\\sessions\\a\\9\\a9f0ab6c3ef63dd8", false},
-		}
-		for _, tc := range testCases {
-			So(IsMaliciousPath(tc.path), ShouldEqual, tc.expect)
-		}
-	})
+	tests := []struct {
+		path   string
+		expVal bool
+	}{
+		{path: "../../../../../../../../../data/gogs/data/sessions/a/9/a9f0ab6c3ef63dd8", expVal: true},
+		{path: "..\\/..\\/../data/gogs/data/sessions/a/9/a9f0ab6c3ef63dd8", expVal: true},
+		{path: "data/gogs/../../../../../../../../../data/sessions/a/9/a9f0ab6c3ef63dd8", expVal: true},
+		{path: "..\\..\\..\\..\\..\\..\\..\\..\\..\\data\\gogs\\data\\sessions\\a\\9\\a9f0ab6c3ef63dd8", expVal: true},
+		{path: "data\\gogs\\..\\..\\..\\..\\..\\..\\..\\..\\..\\data\\sessions\\a\\9\\a9f0ab6c3ef63dd8", expVal: true},
+
+		{path: "data/sessions/a/9/a9f0ab6c3ef63dd8", expVal: false},
+		{path: "data\\sessions\\a\\9\\a9f0ab6c3ef63dd8", expVal: false},
+	}
+	for _, test := range tests {
+		t.Run(test.path, func(t *testing.T) {
+			assert.Equal(t, test.expVal, IsMaliciousPath(test.path))
+		})
+	}
 }