Browse Source

Move IssueUser code to separate file (#836)

Also add unit tests
Ethan Koenig 2 years ago
parent
commit
d0960b8035
6 changed files with 226 additions and 126 deletions
  1. 1 0
      models/fixtures/issue.yml
  2. 23 0
      models/fixtures/issue_user.yml
  3. 0 125
      models/issue.go
  4. 113 0
      models/issue_user.go
  5. 74 0
      models/issue_user_test.go
  6. 15 1
      models/setup_for_test.go

+ 1 - 0
models/fixtures/issue.yml

@@ -3,6 +3,7 @@
3 3
   repo_id: 1
4 4
   index: 1
5 5
   poster_id: 1
6
+  assignee_id: 1
6 7
   name: issue1
7 8
   content: content1
8 9
   is_closed: false

+ 23 - 0
models/fixtures/issue_user.yml

@@ -0,0 +1,23 @@
1
+-
2
+  id: 1
3
+  uid: 1
4
+  issue_id: 1
5
+  is_read: true
6
+  is_assigned: true
7
+  is_mentioned: false
8
+
9
+-
10
+  id: 2
11
+  uid: 2
12
+  issue_id: 1
13
+  is_read: true
14
+  is_assigned: false
15
+  is_mentioned: false
16
+
17
+-
18
+  id: 3
19
+  uid: 4
20
+  issue_id: 1
21
+  is_read: false
22
+  is_assigned: false
23
+  is_mentioned: false

+ 0 - 125
models/issue.go

@@ -1106,70 +1106,6 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
1106 1106
 	return issues, nil
1107 1107
 }
1108 1108
 
1109
-// .___                             ____ ___
1110
-// |   | ______ ________ __   ____ |    |   \______ ___________
1111
-// |   |/  ___//  ___/  |  \_/ __ \|    |   /  ___// __ \_  __ \
1112
-// |   |\___ \ \___ \|  |  /\  ___/|    |  /\___ \\  ___/|  | \/
1113
-// |___/____  >____  >____/  \___  >______//____  >\___  >__|
1114
-//          \/     \/            \/             \/     \/
1115
-
1116
-// IssueUser represents an issue-user relation.
1117
-type IssueUser struct {
1118
-	ID          int64 `xorm:"pk autoincr"`
1119
-	UID         int64 `xorm:"INDEX"` // User ID.
1120
-	IssueID     int64
1121
-	IsRead      bool
1122
-	IsAssigned  bool
1123
-	IsMentioned bool
1124
-}
1125
-
1126
-func newIssueUsers(e *xorm.Session, repo *Repository, issue *Issue) error {
1127
-	assignees, err := repo.getAssignees(e)
1128
-	if err != nil {
1129
-		return fmt.Errorf("getAssignees: %v", err)
1130
-	}
1131
-
1132
-	// Poster can be anyone, append later if not one of assignees.
1133
-	isPosterAssignee := false
1134
-
1135
-	// Leave a seat for poster itself to append later, but if poster is one of assignee
1136
-	// and just waste 1 unit is cheaper than re-allocate memory once.
1137
-	issueUsers := make([]*IssueUser, 0, len(assignees)+1)
1138
-	for _, assignee := range assignees {
1139
-		issueUsers = append(issueUsers, &IssueUser{
1140
-			IssueID:    issue.ID,
1141
-			UID:        assignee.ID,
1142
-			IsAssigned: assignee.ID == issue.AssigneeID,
1143
-		})
1144
-		isPosterAssignee = isPosterAssignee || assignee.ID == issue.PosterID
1145
-	}
1146
-	if !isPosterAssignee {
1147
-		issueUsers = append(issueUsers, &IssueUser{
1148
-			IssueID: issue.ID,
1149
-			UID:     issue.PosterID,
1150
-		})
1151
-	}
1152
-
1153
-	if _, err = e.Insert(issueUsers); err != nil {
1154
-		return err
1155
-	}
1156
-	return nil
1157
-}
1158
-
1159
-// NewIssueUsers adds new issue-user relations for new issue of repository.
1160
-func NewIssueUsers(repo *Repository, issue *Issue) (err error) {
1161
-	sess := x.NewSession()
1162
-	defer sessionRelease(sess)
1163
-	if err = sess.Begin(); err != nil {
1164
-		return err
1165
-	}
1166
-
1167
-	if err = newIssueUsers(sess, repo, issue); err != nil {
1168
-		return err
1169
-	}
1170
-
1171
-	return sess.Commit()
1172
-}
1173 1109
 
1174 1110
 // UpdateIssueMentions extracts mentioned people from content and
1175 1111
 // updates issue-user relations for them.
@@ -1400,67 +1336,6 @@ func UpdateIssue(issue *Issue) error {
1400 1336
 	return updateIssue(x, issue)
1401 1337
 }
1402 1338
 
1403
-func updateIssueUserByAssignee(e *xorm.Session, issue *Issue) (err error) {
1404
-	if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?", false, issue.ID); err != nil {
1405
-		return err
1406
-	}
1407
-
1408
-	// Assignee ID equals to 0 means clear assignee.
1409
-	if issue.AssigneeID > 0 {
1410
-		if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?", true, issue.AssigneeID, issue.ID); err != nil {
1411
-			return err
1412
-		}
1413
-	}
1414
-
1415
-	return updateIssue(e, issue)
1416
-}
1417
-
1418
-// UpdateIssueUserByAssignee updates issue-user relation for assignee.
1419
-func UpdateIssueUserByAssignee(issue *Issue) (err error) {
1420
-	sess := x.NewSession()
1421
-	defer sessionRelease(sess)
1422
-	if err = sess.Begin(); err != nil {
1423
-		return err
1424
-	}
1425
-
1426
-	if err = updateIssueUserByAssignee(sess, issue); err != nil {
1427
-		return err
1428
-	}
1429
-
1430
-	return sess.Commit()
1431
-}
1432
-
1433
-// UpdateIssueUserByRead updates issue-user relation for reading.
1434
-func UpdateIssueUserByRead(uid, issueID int64) error {
1435
-	_, err := x.Exec("UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?", true, uid, issueID)
1436
-	return err
1437
-}
1438
-
1439
-// UpdateIssueUsersByMentions updates issue-user pairs by mentioning.
1440
-func UpdateIssueUsersByMentions(e Engine, issueID int64, uids []int64) error {
1441
-	for _, uid := range uids {
1442
-		iu := &IssueUser{
1443
-			UID:     uid,
1444
-			IssueID: issueID,
1445
-		}
1446
-		has, err := e.Get(iu)
1447
-		if err != nil {
1448
-			return err
1449
-		}
1450
-
1451
-		iu.IsMentioned = true
1452
-		if has {
1453
-			_, err = e.Id(iu.ID).AllCols().Update(iu)
1454
-		} else {
1455
-			_, err = e.Insert(iu)
1456
-		}
1457
-		if err != nil {
1458
-			return err
1459
-		}
1460
-	}
1461
-	return nil
1462
-}
1463
-
1464 1339
 //    _____  .__.__                   __
1465 1340
 //   /     \ |__|  |   ____   _______/  |_  ____   ____   ____
1466 1341
 //  /  \ /  \|  |  | _/ __ \ /  ___/\   __\/  _ \ /    \_/ __ \

+ 113 - 0
models/issue_user.go

@@ -0,0 +1,113 @@
1
+// Copyright 2017 The Gogs Authors. All rights reserved.
2
+// Use of this source code is governed by a MIT-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package models
6
+
7
+import (
8
+	"fmt"
9
+)
10
+
11
+// IssueUser represents an issue-user relation.
12
+type IssueUser struct {
13
+	ID          int64 `xorm:"pk autoincr"`
14
+	UID         int64 `xorm:"INDEX"` // User ID.
15
+	IssueID     int64
16
+	IsRead      bool
17
+	IsAssigned  bool
18
+	IsMentioned bool
19
+}
20
+
21
+func newIssueUsers(e Engine, repo *Repository, issue *Issue) error {
22
+	assignees, err := repo.getAssignees(e)
23
+	if err != nil {
24
+		return fmt.Errorf("getAssignees: %v", err)
25
+	}
26
+
27
+	// Poster can be anyone, append later if not one of assignees.
28
+	isPosterAssignee := false
29
+
30
+	// Leave a seat for poster itself to append later, but if poster is one of assignee
31
+	// and just waste 1 unit is cheaper than re-allocate memory once.
32
+	issueUsers := make([]*IssueUser, 0, len(assignees)+1)
33
+	for _, assignee := range assignees {
34
+		issueUsers = append(issueUsers, &IssueUser{
35
+			IssueID:    issue.ID,
36
+			UID:        assignee.ID,
37
+			IsAssigned: assignee.ID == issue.AssigneeID,
38
+		})
39
+		isPosterAssignee = isPosterAssignee || assignee.ID == issue.PosterID
40
+	}
41
+	if !isPosterAssignee {
42
+		issueUsers = append(issueUsers, &IssueUser{
43
+			IssueID: issue.ID,
44
+			UID:     issue.PosterID,
45
+		})
46
+	}
47
+
48
+	if _, err = e.Insert(issueUsers); err != nil {
49
+		return err
50
+	}
51
+	return nil
52
+}
53
+
54
+func updateIssueUserByAssignee(e Engine, issue *Issue) (err error) {
55
+	if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE issue_id = ?", false, issue.ID); err != nil {
56
+		return err
57
+	}
58
+
59
+	// Assignee ID equals to 0 means clear assignee.
60
+	if issue.AssigneeID > 0 {
61
+		if _, err = e.Exec("UPDATE `issue_user` SET is_assigned = ? WHERE uid = ? AND issue_id = ?", true, issue.AssigneeID, issue.ID); err != nil {
62
+			return err
63
+		}
64
+	}
65
+
66
+	return updateIssue(e, issue)
67
+}
68
+
69
+// UpdateIssueUserByAssignee updates issue-user relation for assignee.
70
+func UpdateIssueUserByAssignee(issue *Issue) (err error) {
71
+	sess := x.NewSession()
72
+	defer sessionRelease(sess)
73
+	if err = sess.Begin(); err != nil {
74
+		return err
75
+	}
76
+
77
+	if err = updateIssueUserByAssignee(sess, issue); err != nil {
78
+		return err
79
+	}
80
+
81
+	return sess.Commit()
82
+}
83
+
84
+// UpdateIssueUserByRead updates issue-user relation for reading.
85
+func UpdateIssueUserByRead(uid, issueID int64) error {
86
+	_, err := x.Exec("UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?", true, uid, issueID)
87
+	return err
88
+}
89
+
90
+// UpdateIssueUsersByMentions updates issue-user pairs by mentioning.
91
+func UpdateIssueUsersByMentions(e Engine, issueID int64, uids []int64) error {
92
+	for _, uid := range uids {
93
+		iu := &IssueUser{
94
+			UID:     uid,
95
+			IssueID: issueID,
96
+		}
97
+		has, err := e.Get(iu)
98
+		if err != nil {
99
+			return err
100
+		}
101
+
102
+		iu.IsMentioned = true
103
+		if has {
104
+			_, err = e.Id(iu.ID).AllCols().Update(iu)
105
+		} else {
106
+			_, err = e.Insert(iu)
107
+		}
108
+		if err != nil {
109
+			return err
110
+		}
111
+	}
112
+	return nil
113
+}

+ 74 - 0
models/issue_user_test.go

@@ -0,0 +1,74 @@
1
+// Copyright 2017 The Gogs Authors. All rights reserved.
2
+// Use of this source code is governed by a MIT-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package models
6
+
7
+import (
8
+	"testing"
9
+
10
+	"github.com/stretchr/testify/assert"
11
+)
12
+
13
+func Test_newIssueUsers(t *testing.T) {
14
+	assert.NoError(t, PrepareTestDatabase())
15
+
16
+	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
17
+	newIssue := &Issue{
18
+		RepoID:   repo.ID,
19
+		PosterID: 4,
20
+		Index:    5,
21
+		Title:    "newTestIssueTitle",
22
+		Content:  "newTestIssueContent",
23
+	}
24
+
25
+	// artificially insert new issue
26
+	AssertSuccessfulInsert(t, newIssue)
27
+
28
+	assert.NoError(t, newIssueUsers(x, repo, newIssue))
29
+
30
+	// issue_user table should now have entries for new issue
31
+	AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID})
32
+	AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID})
33
+}
34
+
35
+func TestUpdateIssueUserByAssignee(t *testing.T) {
36
+	assert.NoError(t, PrepareTestDatabase())
37
+	issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
38
+
39
+	// artificially change assignee in issue_user table
40
+	AssertSuccessfulInsert(t, &IssueUser{IssueID: issue.ID, UID: 5, IsAssigned: true})
41
+	_, err := x.Cols("is_assigned").
42
+		Update(&IssueUser{IsAssigned: false}, &IssueUser{IssueID: issue.ID, UID: issue.AssigneeID})
43
+	assert.NoError(t, err)
44
+
45
+	assert.NoError(t, UpdateIssueUserByAssignee(issue))
46
+
47
+	// issue_user table should now be correct again
48
+	AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: issue.AssigneeID}, "is_assigned=1")
49
+	AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 5}, "is_assigned=0")
50
+}
51
+
52
+func TestUpdateIssueUserByRead(t *testing.T) {
53
+	assert.NoError(t, PrepareTestDatabase())
54
+	issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
55
+
56
+	assert.NoError(t, UpdateIssueUserByRead(4, issue.ID))
57
+	AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
58
+
59
+	assert.NoError(t, UpdateIssueUserByRead(4, issue.ID))
60
+	AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
61
+
62
+	assert.NoError(t, UpdateIssueUserByRead(NonexistentID, NonexistentID))
63
+}
64
+
65
+func TestUpdateIssueUsersByMentions(t *testing.T) {
66
+	assert.NoError(t, PrepareTestDatabase())
67
+	issue := AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue)
68
+
69
+	uids := []int64{2, 5}
70
+	assert.NoError(t, UpdateIssueUsersByMentions(x, issue.ID, uids))
71
+	for _, uid := range uids {
72
+		AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: uid}, "is_mentioned=1")
73
+	}
74
+}

+ 15 - 1
models/setup_for_test.go

@@ -51,6 +51,7 @@ func CreateTestEngine() error {
51 51
 	if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil {
52 52
 		return err
53 53
 	}
54
+
54 55
 	fixtures, err = testfixtures.NewFolder(x.DB().DB, &testfixtures.SQLite{}, "fixtures/")
55 56
 	return err
56 57
 }
@@ -82,7 +83,8 @@ func BeanExists(t *testing.T, bean interface{}, conditions ...interface{}) bool
82 83
 func AssertExistsAndLoadBean(t *testing.T, bean interface{}, conditions ...interface{}) interface{} {
83 84
 	exists, err := loadBeanIfExists(bean, conditions...)
84 85
 	assert.NoError(t, err)
85
-	assert.True(t, exists)
86
+	assert.True(t, exists,
87
+		"Expected to find %+v (with conditions %+v), but did not", bean, conditions)
86 88
 	return bean
87 89
 }
88 90
 
@@ -92,3 +94,15 @@ func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface
92 94
 	assert.NoError(t, err)
93 95
 	assert.False(t, exists)
94 96
 }
97
+
98
+// AssertSuccessfulInsert assert that beans is successfully inserted
99
+func AssertSuccessfulInsert(t *testing.T, beans ...interface{}) {
100
+	_, err := x.Insert(beans...)
101
+	assert.NoError(t, err)
102
+}
103
+
104
+// AssertSuccessfulUpdate assert that bean is successfully updated
105
+func AssertSuccessfulUpdate(t *testing.T, bean interface{}, conditions ...interface{}) {
106
+	_, err := x.Update(bean, conditions...)
107
+	assert.NoError(t, err)
108
+}