mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	 Ethan Koenig
					Ethan Koenig
				
			
				
					committed by
					
						 Kim "BKC" Carlbäcker
						Kim "BKC" Carlbäcker
					
				
			
			
				
	
			
			
			 Kim "BKC" Carlbäcker
						Kim "BKC" Carlbäcker
					
				
			
						parent
						
							bf48c8ebdd
						
					
				
				
					commit
					8fcda0442e
				
			| @@ -6,11 +6,30 @@ package integrations | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
|  | 	"github.com/PuerkitoBio/goquery" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func getIssuesSelection(htmlDoc *HtmlDoc) *goquery.Selection { | ||||||
|  | 	return htmlDoc.doc.Find(".issue.list").Find("li").Find(".title") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *models.Issue { | ||||||
|  | 	href, exists := issueSelection.Attr("href") | ||||||
|  | 	assert.True(t, exists) | ||||||
|  | 	indexStr := href[strings.LastIndexByte(href, '/')+1:] | ||||||
|  | 	index, err := strconv.Atoi(indexStr) | ||||||
|  | 	assert.NoError(t, err, "Invalid issue href: %s", href) | ||||||
|  | 	return models.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repoID, Index: int64(index)}).(*models.Issue) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestNoLoginViewIssues(t *testing.T) { | func TestNoLoginViewIssues(t *testing.T) { | ||||||
| 	prepareTestEnv(t) | 	prepareTestEnv(t) | ||||||
|  |  | ||||||
| @@ -19,6 +38,37 @@ func TestNoLoginViewIssues(t *testing.T) { | |||||||
| 	assert.EqualValues(t, http.StatusOK, resp.HeaderCode) | 	assert.EqualValues(t, http.StatusOK, resp.HeaderCode) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestNoLoginViewIssuesSortByType(t *testing.T) { | ||||||
|  | 	prepareTestEnv(t) | ||||||
|  |  | ||||||
|  | 	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) | ||||||
|  | 	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) | ||||||
|  | 	repo.Owner = models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User) | ||||||
|  |  | ||||||
|  | 	session := loginUser(t, user.Name, "password") | ||||||
|  | 	req := NewRequest(t, "GET", repo.RelLink()+"/issues?type=created_by") | ||||||
|  | 	resp := session.MakeRequest(t, req) | ||||||
|  | 	assert.EqualValues(t, http.StatusOK, resp.HeaderCode) | ||||||
|  |  | ||||||
|  | 	htmlDoc, err := NewHtmlParser(resp.Body) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	issuesSelection := getIssuesSelection(htmlDoc) | ||||||
|  | 	expectedNumIssues := models.GetCount(t, | ||||||
|  | 		&models.Issue{RepoID: repo.ID, PosterID: user.ID}, | ||||||
|  | 		models.Cond("is_closed=?", false), | ||||||
|  | 		models.Cond("is_pull=?", false), | ||||||
|  | 	) | ||||||
|  | 	if expectedNumIssues > setting.UI.IssuePagingNum { | ||||||
|  | 		expectedNumIssues = setting.UI.IssuePagingNum | ||||||
|  | 	} | ||||||
|  | 	assert.EqualValues(t, expectedNumIssues, issuesSelection.Length()) | ||||||
|  |  | ||||||
|  | 	issuesSelection.Each(func(_ int, selection *goquery.Selection) { | ||||||
|  | 		issue := getIssue(t, repo.ID, selection) | ||||||
|  | 		assert.EqualValues(t, user.ID, issue.PosterID) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestNoLoginViewIssue(t *testing.T) { | func TestNoLoginViewIssue(t *testing.T) { | ||||||
| 	prepareTestEnv(t) | 	prepareTestEnv(t) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1223,7 +1223,6 @@ func parseCountResult(results []map[string][]byte) int64 { | |||||||
|  |  | ||||||
| // IssueStatsOptions contains parameters accepted by GetIssueStats. | // IssueStatsOptions contains parameters accepted by GetIssueStats. | ||||||
| type IssueStatsOptions struct { | type IssueStatsOptions struct { | ||||||
| 	FilterMode  int |  | ||||||
| 	RepoID      int64 | 	RepoID      int64 | ||||||
| 	Labels      string | 	Labels      string | ||||||
| 	MilestoneID int64 | 	MilestoneID int64 | ||||||
| @@ -1241,7 +1240,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | |||||||
| 	countSession := func(opts *IssueStatsOptions) *xorm.Session { | 	countSession := func(opts *IssueStatsOptions) *xorm.Session { | ||||||
| 		sess := x. | 		sess := x. | ||||||
| 			Where("issue.repo_id = ?", opts.RepoID). | 			Where("issue.repo_id = ?", opts.RepoID). | ||||||
| 			And("is_pull = ?", opts.IsPull) | 			And("issue.is_pull = ?", opts.IsPull) | ||||||
|  |  | ||||||
| 		if len(opts.IssueIDs) > 0 { | 		if len(opts.IssueIDs) > 0 { | ||||||
| 			sess.In("issue.id", opts.IssueIDs) | 			sess.In("issue.id", opts.IssueIDs) | ||||||
| @@ -1252,8 +1251,8 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | |||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Warn("Malformed Labels argument: %s", opts.Labels) | 				log.Warn("Malformed Labels argument: %s", opts.Labels) | ||||||
| 			} else if len(labelIDs) > 0 { | 			} else if len(labelIDs) > 0 { | ||||||
| 				sess.Join("INNER", "issue_label", "issue.id = issue_id"). | 				sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id"). | ||||||
| 					In("label_id", labelIDs) | 					In("issue_label.label_id", labelIDs) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -1262,11 +1261,11 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if opts.AssigneeID > 0 { | 		if opts.AssigneeID > 0 { | ||||||
| 			sess.And("assignee_id = ?", opts.AssigneeID) | 			sess.And("issue.assignee_id = ?", opts.AssigneeID) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if opts.PosterID > 0 { | 		if opts.PosterID > 0 { | ||||||
| 			sess.And("poster_id = ?", opts.PosterID) | 			sess.And("issue.poster_id = ?", opts.PosterID) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if opts.MentionedID > 0 { | 		if opts.MentionedID > 0 { | ||||||
| @@ -1279,40 +1278,15 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var err error | 	var err error | ||||||
| 	switch opts.FilterMode { | 	stats.OpenCount, err = countSession(opts). | ||||||
| 	case FilterModeAll, FilterModeAssign: | 		And("issue.is_closed = ?", false). | ||||||
| 		stats.OpenCount, err = countSession(opts). | 		Count(new(Issue)) | ||||||
| 			And("is_closed = ?", false). | 	if err != nil { | ||||||
| 			Count(new(Issue)) | 		return stats, err | ||||||
|  |  | ||||||
| 		stats.ClosedCount, err = countSession(opts). |  | ||||||
| 			And("is_closed = ?", true). |  | ||||||
| 			Count(new(Issue)) |  | ||||||
| 	case FilterModeCreate: |  | ||||||
| 		stats.OpenCount, err = countSession(opts). |  | ||||||
| 			And("poster_id = ?", opts.PosterID). |  | ||||||
| 			And("is_closed = ?", false). |  | ||||||
| 			Count(new(Issue)) |  | ||||||
|  |  | ||||||
| 		stats.ClosedCount, err = countSession(opts). |  | ||||||
| 			And("poster_id = ?", opts.PosterID). |  | ||||||
| 			And("is_closed = ?", true). |  | ||||||
| 			Count(new(Issue)) |  | ||||||
| 	case FilterModeMention: |  | ||||||
| 		stats.OpenCount, err = countSession(opts). |  | ||||||
| 			Join("INNER", "issue_user", "issue.id = issue_user.issue_id"). |  | ||||||
| 			And("issue_user.uid = ?", opts.PosterID). |  | ||||||
| 			And("issue_user.is_mentioned = ?", true). |  | ||||||
| 			And("issue.is_closed = ?", false). |  | ||||||
| 			Count(new(Issue)) |  | ||||||
|  |  | ||||||
| 		stats.ClosedCount, err = countSession(opts). |  | ||||||
| 			Join("INNER", "issue_user", "issue.id = issue_user.issue_id"). |  | ||||||
| 			And("issue_user.uid = ?", opts.PosterID). |  | ||||||
| 			And("issue_user.is_mentioned = ?", true). |  | ||||||
| 			And("issue.is_closed = ?", true). |  | ||||||
| 			Count(new(Issue)) |  | ||||||
| 	} | 	} | ||||||
|  | 	stats.ClosedCount, err = countSession(opts). | ||||||
|  | 		And("issue.is_closed = ?", true). | ||||||
|  | 		Count(new(Issue)) | ||||||
| 	return stats, err | 	return stats, err | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -37,13 +37,31 @@ func PrepareTestDatabase() error { | |||||||
| 	return LoadFixtures() | 	return LoadFixtures() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type testCond struct { | ||||||
|  | 	query interface{} | ||||||
|  | 	args  []interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Cond create a condition with arguments for a test | ||||||
|  | func Cond(query interface{}, args ...interface{}) interface{} { | ||||||
|  | 	return &testCond{query: query, args: args} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func whereConditions(sess *xorm.Session, conditions []interface{}) { | ||||||
|  | 	for _, condition := range conditions { | ||||||
|  | 		switch cond := condition.(type) { | ||||||
|  | 		case *testCond: | ||||||
|  | 			sess.Where(cond.query, cond.args...) | ||||||
|  | 		default: | ||||||
|  | 			sess.Where(cond) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error) { | func loadBeanIfExists(bean interface{}, conditions ...interface{}) (bool, error) { | ||||||
| 	sess := x.NewSession() | 	sess := x.NewSession() | ||||||
| 	defer sess.Close() | 	defer sess.Close() | ||||||
|  | 	whereConditions(sess, conditions) | ||||||
| 	for _, cond := range conditions { |  | ||||||
| 		sess = sess.Where(cond) |  | ||||||
| 	} |  | ||||||
| 	return sess.Get(bean) | 	return sess.Get(bean) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -65,6 +83,16 @@ func AssertExistsAndLoadBean(t *testing.T, bean interface{}, conditions ...inter | |||||||
| 	return bean | 	return bean | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // GetCount get the count of a bean | ||||||
|  | func GetCount(t *testing.T, bean interface{}, conditions ...interface{}) int { | ||||||
|  | 	sess := x.NewSession() | ||||||
|  | 	defer sess.Close() | ||||||
|  | 	whereConditions(sess, conditions) | ||||||
|  | 	count, err := sess.Count(bean) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	return int(count) | ||||||
|  | } | ||||||
|  |  | ||||||
| // AssertNotExistsBean assert that a bean does not exist in the test database | // AssertNotExistsBean assert that a bean does not exist in the test database | ||||||
| func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface{}) { | func AssertNotExistsBean(t *testing.T, bean interface{}, conditions ...interface{}) { | ||||||
| 	exists, err := loadBeanIfExists(bean, conditions...) | 	exists, err := loadBeanIfExists(bean, conditions...) | ||||||
| @@ -80,7 +108,5 @@ func AssertSuccessfulInsert(t *testing.T, beans ...interface{}) { | |||||||
|  |  | ||||||
| // AssertCount assert the count of a bean | // AssertCount assert the count of a bean | ||||||
| func AssertCount(t *testing.T, bean interface{}, expected interface{}) { | func AssertCount(t *testing.T, bean interface{}, expected interface{}) { | ||||||
| 	actual, err := x.Count(bean) | 	assert.EqualValues(t, expected, GetCount(t, bean)) | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, expected, actual) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -120,6 +120,15 @@ func Issues(ctx *context.Context) { | |||||||
| 		forceEmpty  bool | 		forceEmpty  bool | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
|  | 	if ctx.IsSigned { | ||||||
|  | 		switch viewType { | ||||||
|  | 		case "created_by": | ||||||
|  | 			posterID = ctx.User.ID | ||||||
|  | 		case "mentioned": | ||||||
|  | 			mentionedID = ctx.User.ID | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	repo := ctx.Repo.Repository | 	repo := ctx.Repo.Repository | ||||||
| 	selectLabels := ctx.Query("labels") | 	selectLabels := ctx.Query("labels") | ||||||
| 	milestoneID := ctx.QueryInt64("milestone") | 	milestoneID := ctx.QueryInt64("milestone") | ||||||
| @@ -150,11 +159,12 @@ func Issues(ctx *context.Context) { | |||||||
| 			MilestoneID: milestoneID, | 			MilestoneID: milestoneID, | ||||||
| 			AssigneeID:  assigneeID, | 			AssigneeID:  assigneeID, | ||||||
| 			MentionedID: mentionedID, | 			MentionedID: mentionedID, | ||||||
|  | 			PosterID:    posterID, | ||||||
| 			IsPull:      isPullList, | 			IsPull:      isPullList, | ||||||
| 			IssueIDs:    issueIDs, | 			IssueIDs:    issueIDs, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.Error(500, "GetSearchIssueStats") | 			ctx.Handle(500, "GetIssueStats", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -68,19 +68,21 @@ | |||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				</div> | ||||||
|  |  | ||||||
| 				<!-- Type --> | 				{{if .IsSigned}} | ||||||
| 				<div class="ui dropdown type jump item"> | 					<!-- Type --> | ||||||
| 					<span class="text"> | 					<div class="ui dropdown type jump item"> | ||||||
| 						{{.i18n.Tr "repo.issues.filter_type"}} | 						<span class="text"> | ||||||
| 						<i class="dropdown icon"></i> | 							{{.i18n.Tr "repo.issues.filter_type"}} | ||||||
| 					</span> | 							<i class="dropdown icon"></i> | ||||||
| 					<div class="menu"> | 						</span> | ||||||
| 						<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.all_issues"}}</a> | 						<div class="menu"> | ||||||
| 						<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}</a> | 							<a class="{{if eq .ViewType "all"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.all_issues"}}</a> | ||||||
| 						<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}</a> | 							<a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{.SignedUser.ID}}">{{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}}</a> | ||||||
| 						<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.mentioning_you"}}</a> | 							<a class="{{if eq .ViewType "created_by"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.created_by_you"}}</a> | ||||||
|  | 							<a class="{{if eq .ViewType "mentioned"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_type.mentioning_you"}}</a> | ||||||
|  | 						</div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</div> | 				{{end}} | ||||||
|  |  | ||||||
| 				<!-- Sort --> | 				<!-- Sort --> | ||||||
| 				<div class="ui dropdown type jump item"> | 				<div class="ui dropdown type jump item"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user