From be48b32e639eacc88e075aae2a4b55b95745d685 Mon Sep 17 00:00:00 2001 From: Ethan Koenig Date: Fri, 17 Feb 2017 03:02:11 -0500 Subject: [PATCH] Unit tests for repo watching (#963) --- models/fixtures/repository.yml | 1 + models/repo.go | 103 --------------------------------- models/repo_watch.go | 103 +++++++++++++++++++++++++++++++++ models/repo_watch_test.go | 98 +++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+), 103 deletions(-) create mode 100644 models/repo_watch.go create mode 100644 models/repo_watch_test.go diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index 873eb612c7..0e2aa5fd0d 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -33,6 +33,7 @@ num_closed_issues: 0 num_pulls: 0 num_closed_pulls: 0 + num_watches: 0 - id: 4 diff --git a/models/repo.go b/models/repo.go index 13ff6dd4f7..490c8a3274 100644 --- a/models/repo.go +++ b/models/repo.go @@ -2256,109 +2256,6 @@ func (repos MirrorRepositoryList) LoadAttributes() error { return repos.loadAttributes(x) } -// __ __ __ .__ -// / \ / \_____ _/ |_ ____ | |__ -// \ \/\/ /\__ \\ __\/ ___\| | \ -// \ / / __ \| | \ \___| Y \ -// \__/\ / (____ /__| \___ >___| / -// \/ \/ \/ \/ - -// Watch is connection request for receiving repository notification. -type Watch struct { - ID int64 `xorm:"pk autoincr"` - UserID int64 `xorm:"UNIQUE(watch)"` - RepoID int64 `xorm:"UNIQUE(watch)"` -} - -func isWatching(e Engine, userID, repoID int64) bool { - has, _ := e.Get(&Watch{0, userID, repoID}) - return has -} - -// IsWatching checks if user has watched given repository. -func IsWatching(userID, repoID int64) bool { - return isWatching(x, userID, repoID) -} - -func watchRepo(e Engine, userID, repoID int64, watch bool) (err error) { - if watch { - if isWatching(e, userID, repoID) { - return nil - } - if _, err = e.Insert(&Watch{RepoID: repoID, UserID: userID}); err != nil { - return err - } - _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoID) - } else { - if !isWatching(e, userID, repoID) { - return nil - } - if _, err = e.Delete(&Watch{0, userID, repoID}); err != nil { - return err - } - _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoID) - } - return err -} - -// WatchRepo watch or unwatch repository. -func WatchRepo(userID, repoID int64, watch bool) (err error) { - return watchRepo(x, userID, repoID, watch) -} - -func getWatchers(e Engine, repoID int64) ([]*Watch, error) { - watches := make([]*Watch, 0, 10) - return watches, e.Find(&watches, &Watch{RepoID: repoID}) -} - -// GetWatchers returns all watchers of given repository. -func GetWatchers(repoID int64) ([]*Watch, error) { - return getWatchers(x, repoID) -} - -// GetWatchers returns range of users watching given repository. -func (repo *Repository) GetWatchers(page int) ([]*User, error) { - users := make([]*User, 0, ItemsPerPage) - sess := x.Where("watch.repo_id=?", repo.ID). - Join("LEFT", "watch", "`user`.id=`watch`.user_id") - if page > 0 { - sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) - } - return users, sess.Find(&users) -} - -func notifyWatchers(e Engine, act *Action) error { - // Add feeds for user self and all watchers. - watches, err := getWatchers(e, act.RepoID) - if err != nil { - return fmt.Errorf("get watchers: %v", err) - } - - // Add feed for actioner. - act.UserID = act.ActUserID - if _, err = e.InsertOne(act); err != nil { - return fmt.Errorf("insert new actioner: %v", err) - } - - for i := range watches { - if act.ActUserID == watches[i].UserID { - continue - } - - act.ID = 0 - act.UserID = watches[i].UserID - if _, err = e.InsertOne(act); err != nil { - return fmt.Errorf("insert new action: %v", err) - } - } - return nil -} - -// NotifyWatchers creates batch of actions for every watcher. -func NotifyWatchers(act *Action) error { - return notifyWatchers(x, act) -} - // ___________ __ // \_ _____/__________| | __ // | __)/ _ \_ __ \ |/ / diff --git a/models/repo_watch.go b/models/repo_watch.go new file mode 100644 index 0000000000..c3baab9d2f --- /dev/null +++ b/models/repo_watch.go @@ -0,0 +1,103 @@ +// Copyright 2017 The Gitea 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 models + +import "fmt" + +// Watch is connection request for receiving repository notification. +type Watch struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"UNIQUE(watch)"` + RepoID int64 `xorm:"UNIQUE(watch)"` +} + +func isWatching(e Engine, userID, repoID int64) bool { + has, _ := e.Get(&Watch{UserID: userID, RepoID: repoID}) + return has +} + +// IsWatching checks if user has watched given repository. +func IsWatching(userID, repoID int64) bool { + return isWatching(x, userID, repoID) +} + +func watchRepo(e Engine, userID, repoID int64, watch bool) (err error) { + if watch { + if isWatching(e, userID, repoID) { + return nil + } + if _, err = e.Insert(&Watch{RepoID: repoID, UserID: userID}); err != nil { + return err + } + _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", repoID) + } else { + if !isWatching(e, userID, repoID) { + return nil + } + if _, err = e.Delete(&Watch{0, userID, repoID}); err != nil { + return err + } + _, err = e.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repoID) + } + return err +} + +// WatchRepo watch or unwatch repository. +func WatchRepo(userID, repoID int64, watch bool) (err error) { + return watchRepo(x, userID, repoID, watch) +} + +func getWatchers(e Engine, repoID int64) ([]*Watch, error) { + watches := make([]*Watch, 0, 10) + return watches, e.Find(&watches, &Watch{RepoID: repoID}) +} + +// GetWatchers returns all watchers of given repository. +func GetWatchers(repoID int64) ([]*Watch, error) { + return getWatchers(x, repoID) +} + +// GetWatchers returns range of users watching given repository. +func (repo *Repository) GetWatchers(page int) ([]*User, error) { + users := make([]*User, 0, ItemsPerPage) + sess := x.Where("watch.repo_id=?", repo.ID). + Join("LEFT", "watch", "`user`.id=`watch`.user_id") + if page > 0 { + sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) + } + return users, sess.Find(&users) +} + +func notifyWatchers(e Engine, act *Action) error { + // Add feeds for user self and all watchers. + watches, err := getWatchers(e, act.RepoID) + if err != nil { + return fmt.Errorf("get watchers: %v", err) + } + + // Add feed for actioner. + act.UserID = act.ActUserID + if _, err = e.InsertOne(act); err != nil { + return fmt.Errorf("insert new actioner: %v", err) + } + + for i := range watches { + if act.ActUserID == watches[i].UserID { + continue + } + + act.ID = 0 + act.UserID = watches[i].UserID + if _, err = e.InsertOne(act); err != nil { + return fmt.Errorf("insert new action: %v", err) + } + } + return nil +} + +// NotifyWatchers creates batch of actions for every watcher. +func NotifyWatchers(act *Action) error { + return notifyWatchers(x, act) +} diff --git a/models/repo_watch_test.go b/models/repo_watch_test.go new file mode 100644 index 0000000000..a1543566c1 --- /dev/null +++ b/models/repo_watch_test.go @@ -0,0 +1,98 @@ +// Copyright 2017 The Gitea 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 models + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsWatching(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + assert.True(t, IsWatching(1, 1)) + assert.True(t, IsWatching(4, 1)) + + assert.False(t, IsWatching(1, 5)) + assert.False(t, IsWatching(NonexistentID, NonexistentID)) +} + +func TestWatchRepo(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + const repoID = 3 + const userID = 2 + + assert.NoError(t, WatchRepo(userID, repoID, true)) + AssertExistsAndLoadBean(t, &Watch{RepoID: repoID, UserID: userID}) + CheckConsistencyFor(t, &Repository{ID: repoID}) + + assert.NoError(t, WatchRepo(userID, repoID, false)) + AssertNotExistsBean(t, &Watch{RepoID: repoID, UserID: userID}) + CheckConsistencyFor(t, &Repository{ID: repoID}) +} + +func TestGetWatchers(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + watches, err := GetWatchers(repo.ID) + assert.NoError(t, err) + assert.Len(t, watches, repo.NumWatches) + for _, watch := range watches { + assert.EqualValues(t, repo.ID, watch.RepoID) + } + + watches, err = GetWatchers(NonexistentID) + assert.NoError(t, err) + assert.Len(t, watches, 0) +} + +func TestRepository_GetWatchers(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + watchers, err := repo.GetWatchers(1) + assert.NoError(t, err) + assert.Len(t, watchers, repo.NumWatches) + for _, watcher := range watchers { + AssertExistsAndLoadBean(t, &Watch{UserID: watcher.ID, RepoID: repo.ID}) + } + + repo = AssertExistsAndLoadBean(t, &Repository{ID: 10}).(*Repository) + watchers, err = repo.GetWatchers(1) + assert.NoError(t, err) + assert.Len(t, watchers, 0) +} + +func TestNotifyWatchers(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + action := &Action{ + ActUserID: 8, + RepoID: 1, + OpType: ActionStarRepo, + } + assert.NoError(t, NotifyWatchers(action)) + + AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 1, + RepoID: action.RepoID, + OpType: action.OpType, + }) + AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 4, + RepoID: action.RepoID, + OpType: action.OpType, + }) + AssertExistsAndLoadBean(t, &Action{ + ActUserID: action.ActUserID, + UserID: 8, + RepoID: action.RepoID, + OpType: action.OpType, + }) +}