diff --git a/release-notes/8.0.0/fix/3562.md b/release-notes/8.0.0/fix/3562.md new file mode 100644 index 0000000000..8099056205 --- /dev/null +++ b/release-notes/8.0.0/fix/3562.md @@ -0,0 +1 @@ +Fixed a bug where subscribing to or unsubscribing from an issue in a repository with no code produced an internal server error. diff --git a/routers/web/repo/issue_watch.go b/routers/web/repo/issue_watch.go index 8b033f3b17..6799bf8eb2 100644 --- a/routers/web/repo/issue_watch.go +++ b/routers/web/repo/issue_watch.go @@ -46,7 +46,7 @@ func IssueWatch(ctx *context.Context) { return } - watch, err := strconv.ParseBool(ctx.Req.PostForm.Get("watch")) + watch, err := strconv.ParseBool(ctx.Req.PostFormValue("watch")) if err != nil { ctx.ServerError("watch is not bool", err) return diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 8fd55d7fa8..f1c6b9f135 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -36,6 +36,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/util" @@ -45,6 +46,7 @@ import ( repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" user_service "code.gitea.io/gitea/services/user" + wiki_service "code.gitea.io/gitea/services/wiki" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" @@ -652,19 +654,39 @@ func GetHTMLTitle(t testing.TB, session *TestSession, urlStr string) string { return doc.Find("head title").Text() } -func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, enabledUnits, disabledUnits []unit_model.Type, files []*files_service.ChangeRepoFile) (*repo_model.Repository, string, func()) { +type DeclarativeRepoOptions struct { + Name optional.Option[string] + EnabledUnits optional.Option[[]unit_model.Type] + DisabledUnits optional.Option[[]unit_model.Type] + Files optional.Option[[]*files_service.ChangeRepoFile] + WikiBranch optional.Option[string] + AutoInit optional.Option[bool] +} + +func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts DeclarativeRepoOptions) (*repo_model.Repository, string, func()) { t.Helper() - repoName := name - if repoName == "" { + // Not using opts.Name.ValueOrDefault() here to avoid unnecessarily + // generating an UUID when a name is specified. + var repoName string + if opts.Name.Has() { + repoName = opts.Name.Value() + } else { repoName = gouuid.NewString() } - // Create a new repository + var autoInit bool + if opts.AutoInit.Has() { + autoInit = opts.AutoInit.Value() + } else { + autoInit = true + } + + // Create the repository repo, err := repo_service.CreateRepository(db.DefaultContext, owner, owner, repo_service.CreateRepoOptions{ Name: repoName, Description: "Temporary Repo", - AutoInit: true, + AutoInit: autoInit, Gitignores: "", License: "WTFPL", Readme: "Default", @@ -673,21 +695,32 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en assert.NoError(t, err) assert.NotEmpty(t, repo) - if enabledUnits != nil || disabledUnits != nil { - units := make([]repo_model.RepoUnit, len(enabledUnits)) - for i, unitType := range enabledUnits { - units[i] = repo_model.RepoUnit{ + // Populate `enabledUnits` if we have any enabled. + var enabledUnits []repo_model.RepoUnit + if opts.EnabledUnits.Has() { + units := opts.EnabledUnits.Value() + enabledUnits = make([]repo_model.RepoUnit, len(units)) + + for i, unitType := range units { + enabledUnits[i] = repo_model.RepoUnit{ RepoID: repo.ID, Type: unitType, } } + } - err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, units, disabledUnits) + // Adjust the repo units according to our parameters. + if opts.EnabledUnits.Has() || opts.DisabledUnits.Has() { + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, enabledUnits, opts.DisabledUnits.ValueOrDefault(nil)) assert.NoError(t, err) } + // Add files, if any. var sha string - if len(files) > 0 { + if opts.Files.Has() { + assert.True(t, autoInit, "Files cannot be specified if AutoInit is disabled") + files := opts.Files.Value() + resp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, owner, &files_service.ChangeRepoFilesOptions{ Files: files, Message: "add files", @@ -712,7 +745,46 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en sha = resp.Commit.SHA } + // If there's a Wiki branch specified, create a wiki, and a default wiki page. + if opts.WikiBranch.Has() { + // Set the wiki branch in the database first + repo.WikiBranch = opts.WikiBranch.Value() + err := repo_model.UpdateRepositoryCols(db.DefaultContext, repo, "wiki_branch") + assert.NoError(t, err) + + // Initialize the wiki + err = wiki_service.InitWiki(db.DefaultContext, repo) + assert.NoError(t, err) + + // Add a new wiki page + err = wiki_service.AddWikiPage(db.DefaultContext, owner, repo, "Home", "Welcome to the wiki!", "Add a Home page") + assert.NoError(t, err) + } + + // Return the repo, the top commit, and a defer-able function to delete the + // repo. return repo, sha, func() { repo_service.DeleteRepository(db.DefaultContext, owner, repo, false) } } + +func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, enabledUnits, disabledUnits []unit_model.Type, files []*files_service.ChangeRepoFile) (*repo_model.Repository, string, func()) { + t.Helper() + + var opts DeclarativeRepoOptions + + if name != "" { + opts.Name = optional.Some(name) + } + if enabledUnits != nil { + opts.EnabledUnits = optional.Some(enabledUnits) + } + if disabledUnits != nil { + opts.DisabledUnits = optional.Some(disabledUnits) + } + if files != nil { + opts.Files = optional.Some(files) + } + + return CreateDeclarativeRepoWithOptions(t, owner, opts) +} diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 84487a847a..94ba670b71 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/indexer/issues" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -866,3 +867,23 @@ body: }) }) } + +func TestIssueUnsubscription(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + repo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{ + AutoInit: optional.Some(false), + }) + defer f() + session := loginUser(t, user.Name) + + issueURL := testNewIssue(t, session, user.Name, repo.Name, "Issue title", "Description") + req := NewRequestWithValues(t, "POST", fmt.Sprintf("%s/watch", issueURL), map[string]string{ + "_csrf": GetCSRF(t, session, issueURL), + "watch": "0", + }) + session.MakeRequest(t, req, http.StatusOK) + }) +}