mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-26 06:16:56 +09:00
Consolidate all Go tests. Coverage is identical to before. Go tests are around 5% faster while being 7% smaller. No non-test code is touched.
287 lines
16 KiB
Go
287 lines
16 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package project
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
audit_model "gitea.dev/models/audit"
|
|
"gitea.dev/models/db"
|
|
issues_model "gitea.dev/models/issues"
|
|
"gitea.dev/models/organization"
|
|
"gitea.dev/models/perm"
|
|
project_model "gitea.dev/models/project"
|
|
repo_model "gitea.dev/models/repo"
|
|
"gitea.dev/models/unit"
|
|
"gitea.dev/models/unittest"
|
|
user_model "gitea.dev/models/user"
|
|
"gitea.dev/modules/container"
|
|
"gitea.dev/modules/optional"
|
|
"gitea.dev/modules/setting"
|
|
api "gitea.dev/modules/structs"
|
|
"gitea.dev/modules/test"
|
|
"gitea.dev/modules/util"
|
|
issue_service "gitea.dev/services/issue"
|
|
notify_service "gitea.dev/services/notify"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMatchWorkflowFilters(t *testing.T) {
|
|
issue := &issues_model.Issue{Labels: []*issues_model.Label{{ID: 1}}}
|
|
pull := &issues_model.Issue{IsPull: true}
|
|
moved := workflowTrigger{sourceColumnID: 1, targetColumnID: 2}
|
|
added, columnChanged := project_model.WorkflowEventItemAddedToProject, project_model.WorkflowEventItemColumnChanged
|
|
tests := []struct {
|
|
name string
|
|
event project_model.WorkflowEvent
|
|
filters project_model.WorkflowFilters
|
|
issue *issues_model.Issue
|
|
trigger workflowTrigger
|
|
want bool
|
|
}{
|
|
{"no filters", added, project_model.WorkflowFilters{}, issue, workflowTrigger{}, true},
|
|
{"issue type", added, project_model.WorkflowFilters{IssueType: "issue"}, issue, workflowTrigger{}, true},
|
|
{"other issue type", added, project_model.WorkflowFilters{IssueType: "issue"}, pull, workflowTrigger{}, false},
|
|
{"label", added, project_model.WorkflowFilters{LabelIDs: []int64{1}}, issue, workflowTrigger{}, true},
|
|
{"missing label", added, project_model.WorkflowFilters{LabelIDs: []int64{1, 2}}, issue, workflowTrigger{}, false},
|
|
{"columns", columnChanged, project_model.WorkflowFilters{SourceColumnID: 1, TargetColumnID: 2}, issue, moved, true},
|
|
{"other target", columnChanged, project_model.WorkflowFilters{TargetColumnID: 3}, issue, moved, false},
|
|
{"no column supplied", columnChanged, project_model.WorkflowFilters{SourceColumnID: 1}, issue, workflowTrigger{}, false},
|
|
{"unsupported by event", added, project_model.WorkflowFilters{TargetColumnID: 2}, issue, moved, false},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
wf := &project_model.Workflow{WorkflowEvent: test.event, WorkflowFilters: test.filters}
|
|
assert.Equal(t, test.want, matchWorkflowFilters(wf, test.issue, test.trigger))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCanManageWorkflows(t *testing.T) {
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
user := func(id int64) *user_model.User { return unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: id}) }
|
|
publicOnly := func(id int64) *user_model.User {
|
|
doer := *user(id)
|
|
doer.ExtDoerData = user_model.NewTokenExtDoerData(user_model.CredentialAccessToken, 1, "public-only,write:issue")
|
|
return &doer
|
|
}
|
|
repoProject := unittest.AssertExistsAndLoadBean(t, &project_model.Project{ID: 2})
|
|
orgProject := &project_model.Project{Title: "org", OwnerID: 3, Type: project_model.TypeOrganization}
|
|
userProject := &project_model.Project{Title: "user", OwnerID: 2, Type: project_model.TypeIndividual}
|
|
for _, tc := range []struct {
|
|
project *project_model.Project
|
|
doer *user_model.User
|
|
want bool
|
|
}{
|
|
{repoProject, user(4), true},
|
|
{repoProject, user(5), false},
|
|
{orgProject, user(2), true},
|
|
{orgProject, user(1), true},
|
|
{orgProject, user(4), false},
|
|
{orgProject, publicOnly(2), false},
|
|
{userProject, user(2), true},
|
|
{userProject, user(1), false},
|
|
{userProject, publicOnly(2), false},
|
|
} {
|
|
allowed, err := CanManageWorkflows(t.Context(), tc.doer, tc.project)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.want, allowed, "project %q, user %d", tc.project.Title, tc.doer.ID)
|
|
}
|
|
for _, removedUnit := range []unit.Type{unit.TypeIssues, unit.TypePullRequests} {
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
_, err := db.Exec(t.Context(), "UPDATE team SET authorize = ? WHERE id = ?", perm.AccessModeNone, 2)
|
|
require.NoError(t, err)
|
|
_, err = db.DeleteByBean(t.Context(), &organization.TeamUnit{TeamID: 2, Type: removedUnit})
|
|
require.NoError(t, err)
|
|
allowed, err := CanManageWorkflows(t.Context(), user(4), repoProject)
|
|
require.NoError(t, err)
|
|
assert.False(t, allowed, removedUnit.LogString())
|
|
}
|
|
}
|
|
|
|
func TestCreateUpdateWorkflow(t *testing.T) {
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
defer test.MockVariableValue(&setting.Audit.RecordOutput, setting.AuditRecordOutputDatabase)()
|
|
ctx := t.Context()
|
|
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
reader := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
|
project := unittest.AssertExistsAndLoadBean(t, &project_model.Project{ID: 1})
|
|
|
|
t.Run("Rejects", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
doer *user_model.User
|
|
opts api.CreateProjectWorkflowOption
|
|
code int
|
|
}{
|
|
{"unknown event", owner, api.CreateProjectWorkflowOption{Event: "bogus", Actions: api.ProjectWorkflowActions{ColumnID: 2}}, 422},
|
|
{"no actions", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project"}, 422},
|
|
{"other project column", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Actions: api.ProjectWorkflowActions{ColumnID: 4}}, 422},
|
|
{"other owner label", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Actions: api.ProjectWorkflowActions{AddLabelIDs: []int64{3}}}, 422},
|
|
{"unsupported filter", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Filters: api.ProjectWorkflowFilters{SourceColumnID: 1}, Actions: api.ProjectWorkflowActions{ColumnID: 2}}, 422},
|
|
{"unsupported action", owner, api.CreateProjectWorkflowOption{Event: "item_removed_from_project", Actions: api.ProjectWorkflowActions{ColumnID: 2}}, 422},
|
|
{"unknown issue state", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Actions: api.ProjectWorkflowActions{IssueState: "bogus"}}, 422},
|
|
{"reopen pull requests", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Filters: api.ProjectWorkflowFilters{IssueType: "pull_request"}, Actions: api.ProjectWorkflowActions{IssueState: "reopen"}}, 422},
|
|
{"unknown issue type", owner, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Filters: api.ProjectWorkflowFilters{IssueType: "bogus"}, Actions: api.ProjectWorkflowActions{ColumnID: 2}}, 422},
|
|
{"without issue and pull request write", reader, api.CreateProjectWorkflowOption{Event: "item_added_to_project", Actions: api.ProjectWorkflowActions{ColumnID: 2}}, 403},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, err := CreateWorkflow(ctx, test.doer, project, &test.opts)
|
|
_, code := util.ErrorUnwrapForUser(err)
|
|
assert.Equal(t, test.code, code)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("PartialUpdate", func(t *testing.T) {
|
|
wf, err := CreateWorkflow(ctx, owner, project, &api.CreateProjectWorkflowOption{
|
|
Event: "item_added_to_project",
|
|
Filters: api.ProjectWorkflowFilters{IssueType: "issue", LabelIDs: []int64{2, 1, 2}},
|
|
Actions: api.ProjectWorkflowActions{ColumnID: 2},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, UpdateWorkflow(ctx, owner, project, wf, &api.EditProjectWorkflowOption{Enabled: new(false)}))
|
|
require.NoError(t, UpdateWorkflow(ctx, owner, project, wf, &api.EditProjectWorkflowOption{Actions: &api.ProjectWorkflowActions{AddLabelIDs: []int64{1}}}))
|
|
|
|
unittest.AssertExistsAndLoadBean(t, &audit_model.Event{Action: audit_model.ProjectWorkflowCreate, ActorID: owner.ID, ScopeType: audit_model.ScopeRepository, ScopeID: project.RepoID})
|
|
|
|
stored := unittest.AssertExistsAndLoadBean(t, &project_model.Workflow{ID: wf.ID})
|
|
assert.False(t, stored.Enabled)
|
|
assert.Equal(t, project_model.WorkflowFilters{IssueType: "issue", LabelIDs: []int64{1, 2}}, stored.WorkflowFilters)
|
|
assert.Equal(t, project_model.WorkflowActions{AddLabelIDs: []int64{1}}, stored.WorkflowActions)
|
|
|
|
assert.ErrorIs(t, UpdateWorkflow(ctx, reader, project, stored, &api.EditProjectWorkflowOption{Enabled: new(true)}), util.ErrPermissionDenied)
|
|
assert.ErrorIs(t, DeleteWorkflow(ctx, reader, project, stored), util.ErrPermissionDenied)
|
|
require.NoError(t, DeleteWorkflow(ctx, owner, project, stored))
|
|
unittest.AssertNotExistsBean(t, &project_model.Workflow{ID: wf.ID})
|
|
for _, action := range []audit_model.Action{audit_model.ProjectWorkflowUpdate, audit_model.ProjectWorkflowDelete} {
|
|
unittest.AssertExistsAndLoadBean(t, &audit_model.Event{Action: action, ActorID: owner.ID, ScopeType: audit_model.ScopeRepository, ScopeID: project.RepoID})
|
|
}
|
|
})
|
|
|
|
userProject := &project_model.Project{Title: "workflows", OwnerID: owner.ID, CreatorID: owner.ID, Type: project_model.TypeIndividual}
|
|
require.NoError(t, project_model.NewProject(ctx, userProject))
|
|
_, err := CreateWorkflow(ctx, owner, userProject, &api.CreateProjectWorkflowOption{Event: "item_added_to_project", Actions: api.ProjectWorkflowActions{IssueState: "close"}})
|
|
require.NoError(t, err)
|
|
event := unittest.AssertExistsAndLoadBean(t, &audit_model.Event{Action: audit_model.ProjectWorkflowCreate, ScopeType: audit_model.ScopeUser, ScopeID: userProject.OwnerID})
|
|
assert.Contains(t, event.Message, "item_added_to_project")
|
|
}
|
|
|
|
type labelChangeRecorder struct {
|
|
notify_service.NullNotifier
|
|
added [][]int64
|
|
}
|
|
|
|
func (r *labelChangeRecorder) IssueChangeLabels(_ctx context.Context, _doer *user_model.User, _issue *issues_model.Issue, added, _removed []*issues_model.Label) {
|
|
r.added = append(r.added, container.FilterSlice(added, func(l *issues_model.Label) (int64, bool) { return l.ID, true }))
|
|
}
|
|
|
|
func TestWorkflowExecution(t *testing.T) {
|
|
const labelID = 2
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
|
|
addLabel := project_model.WorkflowActions{AddLabelIDs: []int64{labelID}}
|
|
addWorkflow := func(t *testing.T, wf project_model.Workflow) {
|
|
wf.ProjectID, wf.Enabled = 1, true
|
|
require.NoError(t, project_model.CreateWorkflow(t.Context(), &wf))
|
|
}
|
|
setup := func(t *testing.T, wf project_model.Workflow) {
|
|
require.NoError(t, unittest.PrepareTestDatabase())
|
|
addWorkflow(t, wf)
|
|
}
|
|
hasLabel := func(t *testing.T, issueID int64) bool {
|
|
return unittest.GetCount(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) == 1
|
|
}
|
|
moveIssue := func(t *testing.T, ctx context.Context, issueID, columnID int64) {
|
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID})
|
|
column := unittest.AssertExistsAndLoadBean(t, &project_model.Column{ID: columnID})
|
|
require.NoError(t, MoveIssueToColumn(ctx, doer, issue, column, optional.None[int64]()))
|
|
}
|
|
|
|
t.Run("ColumnChangedFromDefaultColumn", func(t *testing.T) {
|
|
addWorkflow(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemColumnChanged, WorkflowFilters: project_model.WorkflowFilters{SourceColumnID: 1, TargetColumnID: 2}, WorkflowActions: addLabel})
|
|
moveIssue(t, t.Context(), 2, 2)
|
|
assert.True(t, hasLabel(t, 2))
|
|
})
|
|
|
|
t.Run("NoOpMoveWithinDefaultColumnAndNoCascadeFromWorkflows", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemColumnChanged, WorkflowActions: addLabel})
|
|
moveIssue(t, t.Context(), 2, 1)
|
|
assert.False(t, hasLabel(t, 2))
|
|
unittest.AssertNotExistsBean(t, &issues_model.Comment{IssueID: 2, Type: issues_model.CommentTypeProjectColumn})
|
|
moveIssue(t, issues_model.WithProjectWorkflow(t.Context(), project_model.WorkflowEventItemAddedToProject), 1, 2)
|
|
assert.False(t, hasLabel(t, 1))
|
|
})
|
|
|
|
t.Run("LabelsAndClosesPullRequestAsTriggeringReader", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemColumnChanged, WorkflowActions: project_model.WorkflowActions{AddLabelIDs: []int64{labelID}, IssueState: project_model.WorkflowIssueStateClose}})
|
|
reader := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
|
require.NoError(t, MoveIssueToColumn(t.Context(), reader, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}), unittest.AssertExistsAndLoadBean(t, &project_model.Column{ID: 3}), optional.None[int64]()))
|
|
assert.True(t, hasLabel(t, 3))
|
|
assert.True(t, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).IsClosed)
|
|
for _, commentType := range []issues_model.CommentType{issues_model.CommentTypeLabel, issues_model.CommentTypeClose} {
|
|
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: 3, Type: commentType})
|
|
assert.Equal(t, reader.ID, comment.PosterID)
|
|
assert.Equal(t, &issues_model.CommentMetaData{
|
|
ProjectWorkflowEvent: project_model.WorkflowEventItemColumnChanged,
|
|
SpecialDoerName: issues_model.SpecialDoerNameProjectWorkflow,
|
|
}, comment.CommentMetaData)
|
|
}
|
|
})
|
|
|
|
t.Run("ArchivedRepository", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemClosed, WorkflowActions: addLabel})
|
|
require.NoError(t, repo_model.UpdateRepositoryColsNoAutoTime(t.Context(), &repo_model.Repository{ID: 1, IsArchived: true}, "is_archived"))
|
|
(&workflowNotifier{}).IssueChangeStatus(t.Context(), doer, "", unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}), nil, true)
|
|
assert.False(t, hasLabel(t, 1))
|
|
})
|
|
|
|
t.Run("ReopenSkipsPullRequests", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemColumnChanged, WorkflowActions: project_model.WorkflowActions{IssueState: project_model.WorkflowIssueStateReopen}})
|
|
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), &issues_model.Issue{ID: 3, IsClosed: true}, "is_closed"))
|
|
moveIssue(t, t.Context(), 3, 1)
|
|
moveIssue(t, t.Context(), 5, 1)
|
|
assert.True(t, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).IsClosed)
|
|
assert.False(t, unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 5}).IsClosed)
|
|
})
|
|
|
|
t.Run("OfficialReviewsOnly", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventCodeReviewApproved, WorkflowActions: addLabel})
|
|
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{IssueID: 3})
|
|
review := &issues_model.Review{Type: issues_model.ReviewTypeApprove, Reviewer: doer}
|
|
(&workflowNotifier{}).PullRequestReview(t.Context(), pr, review, nil, nil)
|
|
assert.False(t, hasLabel(t, 3))
|
|
review.Official = true
|
|
(&workflowNotifier{}).PullRequestReview(t.Context(), pr, review, nil, nil)
|
|
assert.True(t, hasLabel(t, 3))
|
|
})
|
|
|
|
t.Run("AddedToAndRemovedFromProject", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemAddedToProject, WorkflowActions: addLabel})
|
|
addWorkflow(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemRemovedFromProject, WorkflowActions: project_model.WorkflowActions{RemoveLabelIDs: []int64{labelID}}})
|
|
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 11})
|
|
column := unittest.AssertExistsAndLoadBean(t, &project_model.Column{ID: 2})
|
|
require.NoError(t, AddIssueToColumn(t.Context(), doer, issue, column))
|
|
assert.True(t, hasLabel(t, 11))
|
|
require.NoError(t, RemoveIssueFromColumn(t.Context(), doer, issue, column))
|
|
assert.False(t, hasLabel(t, 11))
|
|
})
|
|
|
|
t.Run("NewItemInProject", func(t *testing.T) {
|
|
setup(t, project_model.Workflow{WorkflowEvent: project_model.WorkflowEventItemAddedToProject, WorkflowActions: project_model.WorkflowActions{ColumnID: 3, AddLabelIDs: []int64{labelID}}})
|
|
*labelChanges = labelChangeRecorder{}
|
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
issue := &issues_model.Issue{RepoID: repo.ID, PosterID: doer.ID, Title: "new item"}
|
|
require.NoError(t, issue_service.NewIssue(t.Context(), repo, issue, []int64{1}, nil, nil, []int64{1}))
|
|
assert.Equal(t, [][]int64{{1}, {labelID}}, labelChanges.added)
|
|
assert.EqualValues(t, 3, unittest.AssertExistsAndLoadBean(t, &project_model.ProjectIssue{IssueID: issue.ID}).ProjectColumnID)
|
|
})
|
|
}
|