aboutsummaryrefslogtreecommitdiff
path: root/internal/db
diff options
context:
space:
mode:
authorᴜɴᴋɴᴡᴏɴ <u@gogs.io>2020-04-17 10:14:18 +0800
committerGitHub <noreply@github.com>2020-04-17 10:14:18 +0800
commitfa497b16332c24bc4d9e788c64bda94e3c1499a7 (patch)
tree1dcf1b13f702a07dc30da791bf0e76e1ffba4027 /internal/db
parente131a4564680a3257a2d4795af7669e4260b33b9 (diff)
db: add tests for repos (#6112)
* Add Repos.create method * Fix repo name error handling * Fix all compile errors * Update github.com/go-macaron/captcha to fix http issue * Add repos tests
Diffstat (limited to 'internal/db')
-rw-r--r--internal/db/error.go40
-rw-r--r--internal/db/errors/user.go11
-rw-r--r--internal/db/org_team.go2
-rw-r--r--internal/db/repo.go85
-rw-r--r--internal/db/repos.go100
-rw-r--r--internal/db/repos_test.go102
-rw-r--r--internal/db/user.go39
7 files changed, 267 insertions, 112 deletions
diff --git a/internal/db/error.go b/internal/db/error.go
index 46e7dde5..ba07ca87 100644
--- a/internal/db/error.go
+++ b/internal/db/error.go
@@ -8,32 +8,6 @@ import (
"fmt"
)
-type ErrNameReserved struct {
- Name string
-}
-
-func IsErrNameReserved(err error) bool {
- _, ok := err.(ErrNameReserved)
- return ok
-}
-
-func (err ErrNameReserved) Error() string {
- return fmt.Sprintf("name is reserved [name: %s]", err.Name)
-}
-
-type ErrNamePatternNotAllowed struct {
- Pattern string
-}
-
-func IsErrNamePatternNotAllowed(err error) bool {
- _, ok := err.(ErrNamePatternNotAllowed)
- return ok
-}
-
-func (err ErrNamePatternNotAllowed) Error() string {
- return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
-}
-
// ____ ___
// | | \______ ___________
// | | / ___// __ \_ __ \
@@ -245,20 +219,6 @@ func (err ErrLastOrgOwner) Error() string {
// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____|
// \/ \/|__| \/ \/
-type ErrRepoAlreadyExist struct {
- Uname string
- Name string
-}
-
-func IsErrRepoAlreadyExist(err error) bool {
- _, ok := err.(ErrRepoAlreadyExist)
- return ok
-}
-
-func (err ErrRepoAlreadyExist) Error() string {
- return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
-}
-
type ErrInvalidCloneAddr struct {
IsURLError bool
IsInvalidPath bool
diff --git a/internal/db/errors/user.go b/internal/db/errors/user.go
index 54a6ad50..09572af9 100644
--- a/internal/db/errors/user.go
+++ b/internal/db/errors/user.go
@@ -8,17 +8,6 @@ import (
"fmt"
)
-type EmptyName struct{}
-
-func IsEmptyName(err error) bool {
- _, ok := err.(EmptyName)
- return ok
-}
-
-func (err EmptyName) Error() string {
- return "empty name"
-}
-
type UserNotKeyOwner struct {
KeyID int64
}
diff --git a/internal/db/org_team.go b/internal/db/org_team.go
index 6e141d25..9daca9e7 100644
--- a/internal/db/org_team.go
+++ b/internal/db/org_team.go
@@ -217,7 +217,7 @@ var reservedTeamNames = []string{"new"}
// IsUsableTeamName return an error if given name is a reserved name or pattern.
func IsUsableTeamName(name string) error {
- return isUsableName(reservedTeamNames, nil, name)
+ return isNameAllowed(reservedTeamNames, nil, name)
}
// NewTeam creates a record of new team.
diff --git a/internal/db/repo.go b/internal/db/repo.go
index 150942c1..66cecdd3 100644
--- a/internal/db/repo.go
+++ b/internal/db/repo.go
@@ -148,14 +148,14 @@ func NewRepoContext() {
// Repository contains information of a repository.
type Repository struct {
ID int64
- OwnerID int64 `xorm:"UNIQUE(s)"`
- Owner *User `xorm:"-" json:"-"`
- LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"`
- Name string `xorm:"INDEX NOT NULL"`
- Description string `xorm:"VARCHAR(512)"`
+ OwnerID int64 `xorm:"UNIQUE(s)" gorm:"UNIQUE_INDEX:s"`
+ Owner *User `xorm:"-" gorm:"-" json:"-"`
+ LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL" gorm:"UNIQUE_INDEX:s"`
+ Name string `xorm:"INDEX NOT NULL" gorm:"NOT NULL"`
+ Description string `xorm:"VARCHAR(512)" gorm:"TYPE:VARCHAR(512)"`
Website string
DefaultBranch string
- Size int64 `xorm:"NOT NULL DEFAULT 0"`
+ Size int64 `xorm:"NOT NULL DEFAULT 0" gorm:"NOT NULL;DEFAULT:0"`
UseCustomAvatar bool
// Counters
@@ -164,44 +164,44 @@ type Repository struct {
NumForks int
NumIssues int
NumClosedIssues int
- NumOpenIssues int `xorm:"-" json:"-"`
+ NumOpenIssues int `xorm:"-" gorm:"-" json:"-"`
NumPulls int
NumClosedPulls int
- NumOpenPulls int `xorm:"-" json:"-"`
- NumMilestones int `xorm:"NOT NULL DEFAULT 0"`
- NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
- NumOpenMilestones int `xorm:"-" json:"-"`
- NumTags int `xorm:"-" json:"-"`
+ NumOpenPulls int `xorm:"-" gorm:"-" json:"-"`
+ NumMilestones int `xorm:"NOT NULL DEFAULT 0" gorm:"NOT NULL;DEFAULT:0"`
+ NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0" gorm:"NOT NULL;DEFAULT:0"`
+ NumOpenMilestones int `xorm:"-" gorm:"-" json:"-"`
+ NumTags int `xorm:"-" gorm:"-" json:"-"`
IsPrivate bool
IsBare bool
IsMirror bool
- *Mirror `xorm:"-" json:"-"`
+ *Mirror `xorm:"-" gorm:"-" json:"-"`
// Advanced settings
- EnableWiki bool `xorm:"NOT NULL DEFAULT true"`
+ EnableWiki bool `xorm:"NOT NULL DEFAULT true" gorm:"NOT NULL;DEFAULT:TRUE"`
AllowPublicWiki bool
EnableExternalWiki bool
ExternalWikiURL string
- EnableIssues bool `xorm:"NOT NULL DEFAULT true"`
+ EnableIssues bool `xorm:"NOT NULL DEFAULT true" gorm:"NOT NULL;DEFAULT:TRUE"`
AllowPublicIssues bool
EnableExternalTracker bool
ExternalTrackerURL string
ExternalTrackerFormat string
ExternalTrackerStyle string
- ExternalMetas map[string]string `xorm:"-" json:"-"`
- EnablePulls bool `xorm:"NOT NULL DEFAULT true"`
- PullsIgnoreWhitespace bool `xorm:"NOT NULL DEFAULT false"`
- PullsAllowRebase bool `xorm:"NOT NULL DEFAULT false"`
+ ExternalMetas map[string]string `xorm:"-" gorm:"-" json:"-"`
+ EnablePulls bool `xorm:"NOT NULL DEFAULT true" gorm:"NOT NULL;DEFAULT:TRUE"`
+ PullsIgnoreWhitespace bool `xorm:"NOT NULL DEFAULT false" gorm:"NOT NULL;DEFAULT:FALSE"`
+ PullsAllowRebase bool `xorm:"NOT NULL DEFAULT false" gorm:"NOT NULL;DEFAULT:FALSE"`
- IsFork bool `xorm:"NOT NULL DEFAULT false"`
+ IsFork bool `xorm:"NOT NULL DEFAULT false" gorm:"NOT NULL;DEFAULT:FALSE"`
ForkID int64
- BaseRepo *Repository `xorm:"-" json:"-"`
+ BaseRepo *Repository `xorm:"-" gorm:"-" json:"-"`
- Created time.Time `xorm:"-" json:"-"`
+ Created time.Time `xorm:"-" gorm:"-" json:"-"`
CreatedUnix int64
- Updated time.Time `xorm:"-" json:"-"`
+ Updated time.Time `xorm:"-" gorm:"-" json:"-"`
UpdatedUnix int64
}
@@ -210,10 +210,6 @@ func (repo *Repository) BeforeInsert() {
repo.UpdatedUnix = repo.CreatedUnix
}
-func (repo *Repository) BeforeUpdate() {
- repo.UpdatedUnix = time.Now().Unix()
-}
-
func (repo *Repository) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "default_branch":
@@ -1057,13 +1053,13 @@ var (
reservedRepoPatterns = []string{"*.git", "*.wiki"}
)
-// IsUsableRepoName return an error if given name is a reserved name or pattern.
-func IsUsableRepoName(name string) error {
- return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
+// isRepoNameAllowed return an error if given name is a reserved name or pattern for repositories.
+func isRepoNameAllowed(name string) error {
+ return isNameAllowed(reservedRepoNames, reservedRepoPatterns, name)
}
func createRepository(e *xorm.Session, doer, owner *User, repo *Repository) (err error) {
- if err = IsUsableRepoName(repo.Name); err != nil {
+ if err = isRepoNameAllowed(repo.Name); err != nil {
return err
}
@@ -1071,7 +1067,7 @@ func createRepository(e *xorm.Session, doer, owner *User, repo *Repository) (err
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
- return ErrRepoAlreadyExist{owner.Name, repo.Name}
+ return ErrRepoAlreadyExist{args: errutil.Args{"ownerID": owner.ID, "name": repo.Name}}
}
if _, err = e.Insert(repo); err != nil {
@@ -1266,7 +1262,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
- return ErrRepoAlreadyExist{newOwnerName, repo.Name}
+ return ErrRepoAlreadyExist{args: errutil.Args{"ownerName": newOwnerName, "name": repo.Name}}
}
sess := x.NewSession()
@@ -1384,7 +1380,7 @@ func deleteRepoLocalCopy(repo *Repository) {
func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error) {
oldRepoName = strings.ToLower(oldRepoName)
newRepoName = strings.ToLower(newRepoName)
- if err = IsUsableRepoName(newRepoName); err != nil {
+ if err = isRepoNameAllowed(newRepoName); err != nil {
return err
}
@@ -1392,7 +1388,7 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
- return ErrRepoAlreadyExist{u.Name, newRepoName}
+ return ErrRepoAlreadyExist{args: errutil.Args{"ownerID": u.ID, "name": newRepoName}}
}
repo, err := GetRepositoryByName(u.ID, oldRepoName)
@@ -1647,25 +1643,6 @@ func GetRepositoryByRef(ref string) (*Repository, error) {
return GetRepositoryByName(user.ID, repoName)
}
-var _ errutil.NotFound = (*ErrRepoNotExist)(nil)
-
-type ErrRepoNotExist struct {
- args map[string]interface{}
-}
-
-func IsErrRepoNotExist(err error) bool {
- _, ok := err.(ErrRepoNotExist)
- return ok
-}
-
-func (err ErrRepoNotExist) Error() string {
- return fmt.Sprintf("repository does not exist: %v", err.args)
-}
-
-func (ErrRepoNotExist) NotFound() bool {
- return true
-}
-
// GetRepositoryByName returns the repository by given name under user if exists.
// Deprecated: Use Repos.GetByName instead.
func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
diff --git a/internal/db/repos.go b/internal/db/repos.go
index cac50b76..f97cf28d 100644
--- a/internal/db/repos.go
+++ b/internal/db/repos.go
@@ -5,9 +5,13 @@
package db
import (
+ "fmt"
"strings"
+ "time"
"github.com/jinzhu/gorm"
+
+ "gogs.io/gogs/internal/errutil"
)
// ReposStore is the persistent interface for repositories.
@@ -21,10 +25,106 @@ type ReposStore interface {
var Repos ReposStore
+// NOTE: This is a GORM create hook.
+func (r *Repository) BeforeCreate() {
+ r.CreatedUnix = gorm.NowFunc().Unix()
+}
+
+// NOTE: This is a GORM update hook.
+func (r *Repository) BeforeUpdate() {
+ r.UpdatedUnix = gorm.NowFunc().Unix()
+}
+
+// NOTE: This is a GORM query hook.
+func (r *Repository) AfterFind() {
+ r.Created = time.Unix(r.CreatedUnix, 0).Local()
+ r.Updated = time.Unix(r.UpdatedUnix, 0).Local()
+}
+
+var _ ReposStore = (*repos)(nil)
+
type repos struct {
*gorm.DB
}
+type ErrRepoAlreadyExist struct {
+ args errutil.Args
+}
+
+func IsErrRepoAlreadyExist(err error) bool {
+ _, ok := err.(ErrRepoAlreadyExist)
+ return ok
+}
+
+func (err ErrRepoAlreadyExist) Error() string {
+ return fmt.Sprintf("repository already exists: %v", err.args)
+}
+
+type createRepoOpts struct {
+ Name string
+ Description string
+ DefaultBranch string
+ Private bool
+ Mirror bool
+ EnableWiki bool
+ EnableIssues bool
+ EnablePulls bool
+ Fork bool
+ ForkID int64
+}
+
+// create creates a new repository record in the database. Fields of "repo" will be updated
+// in place upon insertion. It returns ErrNameNotAllowed when the repository name is not allowed,
+// or returns ErrRepoAlreadyExist when a repository with same name already exists for the owner.
+func (db *repos) create(ownerID int64, opts createRepoOpts) (*Repository, error) {
+ err := isRepoNameAllowed(opts.Name)
+ if err != nil {
+ return nil, err
+ }
+
+ _, err = db.GetByName(ownerID, opts.Name)
+ if err == nil {
+ return nil, ErrRepoAlreadyExist{args: errutil.Args{"ownerID": ownerID, "name": opts.Name}}
+ } else if !IsErrRepoNotExist(err) {
+ return nil, err
+ }
+
+ repo := &Repository{
+ OwnerID: ownerID,
+ LowerName: strings.ToLower(opts.Name),
+ Name: opts.Name,
+ Description: opts.Description,
+ DefaultBranch: opts.DefaultBranch,
+ IsPrivate: opts.Private,
+ IsMirror: opts.Mirror,
+ EnableWiki: opts.EnableWiki,
+ EnableIssues: opts.EnableIssues,
+ EnablePulls: opts.EnablePulls,
+ IsFork: opts.Fork,
+ ForkID: opts.ForkID,
+ }
+ return repo, db.DB.Create(repo).Error
+}
+
+var _ errutil.NotFound = (*ErrRepoNotExist)(nil)
+
+type ErrRepoNotExist struct {
+ args map[string]interface{}
+}
+
+func IsErrRepoNotExist(err error) bool {
+ _, ok := err.(ErrRepoNotExist)
+ return ok
+}
+
+func (err ErrRepoNotExist) Error() string {
+ return fmt.Sprintf("repository does not exist: %v", err.args)
+}
+
+func (ErrRepoNotExist) NotFound() bool {
+ return true
+}
+
func (db *repos) GetByName(ownerID int64, name string) (*Repository, error) {
repo := new(Repository)
err := db.Where("owner_id = ? AND lower_name = ?", ownerID, strings.ToLower(name)).First(repo).Error
diff --git a/internal/db/repos_test.go b/internal/db/repos_test.go
new file mode 100644
index 00000000..6a39c4ee
--- /dev/null
+++ b/internal/db/repos_test.go
@@ -0,0 +1,102 @@
+// Copyright 2020 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package db
+
+import (
+ "testing"
+ "time"
+
+ "github.com/jinzhu/gorm"
+ "github.com/stretchr/testify/assert"
+
+ "gogs.io/gogs/internal/errutil"
+)
+
+func Test_repos(t *testing.T) {
+ if testing.Short() {
+ t.Skip()
+ }
+
+ t.Parallel()
+
+ tables := []interface{}{new(Repository)}
+ db := &repos{
+ DB: initTestDB(t, "repos", tables...),
+ }
+
+ for _, tc := range []struct {
+ name string
+ test func(*testing.T, *repos)
+ }{
+ {"create", test_repos_create},
+ {"GetByName", test_repos_GetByName},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Cleanup(func() {
+ err := clearTables(db.DB, tables...)
+ if err != nil {
+ t.Fatal(err)
+ }
+ })
+ tc.test(t, db)
+ })
+ }
+}
+
+func test_repos_create(t *testing.T, db *repos) {
+ t.Run("name not allowed", func(t *testing.T) {
+ _, err := db.create(1, createRepoOpts{
+ Name: "my.git",
+ })
+ expErr := ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "pattern": "*.git"}}
+ assert.Equal(t, expErr, err)
+ })
+
+ t.Run("already exists", func(t *testing.T) {
+ _, err := db.create(1, createRepoOpts{
+ Name: "repo1",
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.create(1, createRepoOpts{
+ Name: "repo1",
+ })
+ expErr := ErrRepoAlreadyExist{args: errutil.Args{"ownerID": int64(1), "name": "repo1"}}
+ assert.Equal(t, expErr, err)
+ })
+
+ repo, err := db.create(1, createRepoOpts{
+ Name: "repo2",
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ repo, err = db.GetByName(repo.OwnerID, repo.Name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ assert.Equal(t, gorm.NowFunc().Format(time.RFC3339), repo.Created.Format(time.RFC3339))
+}
+
+func test_repos_GetByName(t *testing.T, db *repos) {
+ repo, err := db.create(1, createRepoOpts{
+ Name: "repo1",
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.GetByName(repo.OwnerID, repo.Name)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.GetByName(1, "bad_name")
+ expErr := ErrRepoNotExist{args: errutil.Args{"ownerID": int64(1), "name": "bad_name"}}
+ assert.Equal(t, expErr, err)
+}
diff --git a/internal/db/user.go b/internal/db/user.go
index b650fa14..c225bd1a 100644
--- a/internal/db/user.go
+++ b/internal/db/user.go
@@ -503,25 +503,52 @@ var (
reservedUserPatterns = []string{"*.keys"}
)
-// isUsableName checks if name is reserved or pattern of name is not allowed
+type ErrNameNotAllowed struct {
+ args errutil.Args
+}
+
+func IsErrNameNotAllowed(err error) bool {
+ _, ok := err.(ErrNameNotAllowed)
+ return ok
+}
+
+func (err ErrNameNotAllowed) Value() string {
+ val, ok := err.args["name"].(string)
+ if ok {
+ return val
+ }
+
+ val, ok = err.args["pattern"].(string)
+ if ok {
+ return val
+ }
+
+ return "<value not found>"
+}
+
+func (err ErrNameNotAllowed) Error() string {
+ return fmt.Sprintf("name is not allowed: %v", err.args)
+}
+
+// isNameAllowed checks if name is reserved or pattern of name is not allowed
// based on given reserved names and patterns.
// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
-func isUsableName(names, patterns []string, name string) error {
+func isNameAllowed(names, patterns []string, name string) error {
name = strings.TrimSpace(strings.ToLower(name))
if utf8.RuneCountInString(name) == 0 {
- return errors.EmptyName{}
+ return ErrNameNotAllowed{args: errutil.Args{"reason": "empty name"}}
}
for i := range names {
if name == names[i] {
- return ErrNameReserved{name}
+ return ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "name": name}}
}
}
for _, pat := range patterns {
if pat[0] == '*' && strings.HasSuffix(name, pat[1:]) ||
(pat[len(pat)-1] == '*' && strings.HasPrefix(name, pat[:len(pat)-1])) {
- return ErrNamePatternNotAllowed{pat}
+ return ErrNameNotAllowed{args: errutil.Args{"reason": "reserved", "pattern": pat}}
}
}
@@ -529,7 +556,7 @@ func isUsableName(names, patterns []string, name string) error {
}
func IsUsableUsername(name string) error {
- return isUsableName(reservedUsernames, reservedUserPatterns, name)
+ return isNameAllowed(reservedUsernames, reservedUserPatterns, name)
}
// CreateUser creates record of a new user.