mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Fix Bug in Issue Config when only contact links are set (#26521)
Blank Issues should be enabled if they are not explicit disabled through the `blank_issues_enabled` field of the Issue Config. The Implementation has currently a Bug: If you create a Issue Config file with only `contact_links` and without a `blank_issues_enabled` field, `blank_issues_enabled` is set to false by default. The fix is only one line, but I decided to also improve the tests to make sure there are no other problems with the Implementation. This is a bugfix, so it should be backported to 1.20.
This commit is contained in:
		| @@ -72,7 +72,7 @@ func GetTemplateConfig(gitRepo *git.Repository, path string, commit *git.Commit) | |||||||
| 		return GetDefaultTemplateConfig(), err | 		return GetDefaultTemplateConfig(), err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	issueConfig := api.IssueConfig{} | 	issueConfig := GetDefaultTemplateConfig() | ||||||
| 	if err := yaml.Unmarshal(configContent, &issueConfig); err != nil { | 	if err := yaml.Unmarshal(configContent, &issueConfig); err != nil { | ||||||
| 		return GetDefaultTemplateConfig(), err | 		return GetDefaultTemplateConfig(), err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -15,38 +15,167 @@ import ( | |||||||
| 	"code.gitea.io/gitea/tests" | 	"code.gitea.io/gitea/tests" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"gopkg.in/yaml.v3" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestAPIReposGetDefaultIssueConfig(t *testing.T) { | func createIssueConfig(t *testing.T, user *user_model.User, repo *repo_model.Repository, issueConfig map[string]any) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	config, err := yaml.Marshal(issueConfig) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	err = createOrReplaceFileInBranch(user, repo, ".gitea/ISSUE_TEMPLATE/config.yaml", repo.DefaultBranch, string(config)) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issue_config", owner.Name, repo.Name) | func getIssueConfig(t *testing.T, owner, repo string) api.IssueConfig { | ||||||
|  | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issue_config", owner, repo) | ||||||
| 	req := NewRequest(t, "GET", urlStr) | 	req := NewRequest(t, "GET", urlStr) | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) | 	resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 	var issueConfig api.IssueConfig | 	var issueConfig api.IssueConfig | ||||||
| 	DecodeJSON(t, resp, &issueConfig) | 	DecodeJSON(t, resp, &issueConfig) | ||||||
|  |  | ||||||
| 	assert.True(t, issueConfig.BlankIssuesEnabled) | 	return issueConfig | ||||||
| 	assert.Equal(t, issueConfig.ContactLinks, make([]api.IssueConfigContactLink, 0)) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIReposValidateDefaultIssueConfig(t *testing.T) { | func TestAPIRepoGetIssueConfig(t *testing.T) { | ||||||
| 	defer tests.PrepareTestEnv(t)() | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49}) | ||||||
|  | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
|  | 	t.Run("Default", func(t *testing.T) { | ||||||
|  | 		issueConfig := getIssueConfig(t, owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | 		assert.True(t, issueConfig.BlankIssuesEnabled) | ||||||
|  | 		assert.Len(t, issueConfig.ContactLinks, 0) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("DisableBlankIssues", func(t *testing.T) { | ||||||
|  | 		config := make(map[string]any) | ||||||
|  | 		config["blank_issues_enabled"] = false | ||||||
|  |  | ||||||
|  | 		createIssueConfig(t, owner, repo, config) | ||||||
|  |  | ||||||
|  | 		issueConfig := getIssueConfig(t, owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | 		assert.False(t, issueConfig.BlankIssuesEnabled) | ||||||
|  | 		assert.Len(t, issueConfig.ContactLinks, 0) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("ContactLinks", func(t *testing.T) { | ||||||
|  | 		contactLink := make(map[string]string) | ||||||
|  | 		contactLink["name"] = "TestName" | ||||||
|  | 		contactLink["url"] = "https://example.com" | ||||||
|  | 		contactLink["about"] = "TestAbout" | ||||||
|  |  | ||||||
|  | 		config := make(map[string]any) | ||||||
|  | 		config["contact_links"] = []map[string]string{contactLink} | ||||||
|  |  | ||||||
|  | 		createIssueConfig(t, owner, repo, config) | ||||||
|  |  | ||||||
|  | 		issueConfig := getIssueConfig(t, owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | 		assert.True(t, issueConfig.BlankIssuesEnabled) | ||||||
|  | 		assert.Len(t, issueConfig.ContactLinks, 1) | ||||||
|  |  | ||||||
|  | 		assert.Equal(t, "TestName", issueConfig.ContactLinks[0].Name) | ||||||
|  | 		assert.Equal(t, "https://example.com", issueConfig.ContactLinks[0].URL) | ||||||
|  | 		assert.Equal(t, "TestAbout", issueConfig.ContactLinks[0].About) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("Full", func(t *testing.T) { | ||||||
|  | 		contactLink := make(map[string]string) | ||||||
|  | 		contactLink["name"] = "TestName" | ||||||
|  | 		contactLink["url"] = "https://example.com" | ||||||
|  | 		contactLink["about"] = "TestAbout" | ||||||
|  |  | ||||||
|  | 		config := make(map[string]any) | ||||||
|  | 		config["blank_issues_enabled"] = false | ||||||
|  | 		config["contact_links"] = []map[string]string{contactLink} | ||||||
|  |  | ||||||
|  | 		createIssueConfig(t, owner, repo, config) | ||||||
|  |  | ||||||
|  | 		issueConfig := getIssueConfig(t, owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | 		assert.False(t, issueConfig.BlankIssuesEnabled) | ||||||
|  | 		assert.Len(t, issueConfig.ContactLinks, 1) | ||||||
|  |  | ||||||
|  | 		assert.Equal(t, "TestName", issueConfig.ContactLinks[0].Name) | ||||||
|  | 		assert.Equal(t, "https://example.com", issueConfig.ContactLinks[0].URL) | ||||||
|  | 		assert.Equal(t, "TestAbout", issueConfig.ContactLinks[0].About) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAPIRepoIssueConfigPaths(t *testing.T) { | ||||||
|  | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
|  | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49}) | ||||||
|  | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
|  | 	templateConfigCandidates := []string{ | ||||||
|  | 		".gitea/ISSUE_TEMPLATE/config", | ||||||
|  | 		".gitea/issue_template/config", | ||||||
|  | 		".github/ISSUE_TEMPLATE/config", | ||||||
|  | 		".github/issue_template/config", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, canidate := range templateConfigCandidates { | ||||||
|  | 		for _, extension := range []string{".yaml", ".yml"} { | ||||||
|  | 			fullPath := canidate + extension | ||||||
|  | 			t.Run(fullPath, func(t *testing.T) { | ||||||
|  | 				configMap := make(map[string]any) | ||||||
|  | 				configMap["blank_issues_enabled"] = false | ||||||
|  |  | ||||||
|  | 				configData, err := yaml.Marshal(configMap) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 				_, err = createFileInBranch(owner, repo, fullPath, repo.DefaultBranch, string(configData)) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 				issueConfig := getIssueConfig(t, owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | 				assert.False(t, issueConfig.BlankIssuesEnabled) | ||||||
|  | 				assert.Len(t, issueConfig.ContactLinks, 0) | ||||||
|  |  | ||||||
|  | 				_, err = deleteFileInBranch(owner, repo, fullPath, repo.DefaultBranch) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestAPIRepoValidateIssueConfig(t *testing.T) { | ||||||
|  | 	defer tests.PrepareTestEnv(t)() | ||||||
|  |  | ||||||
|  | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 49}) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) | ||||||
|  |  | ||||||
| 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issue_config/validate", owner.Name, repo.Name) | 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issue_config/validate", owner.Name, repo.Name) | ||||||
| 	req := NewRequest(t, "GET", urlStr) |  | ||||||
| 	resp := MakeRequest(t, req, http.StatusOK) |  | ||||||
|  |  | ||||||
| 	var issueConfigValidation api.IssueConfigValidation | 	t.Run("Valid", func(t *testing.T) { | ||||||
| 	DecodeJSON(t, resp, &issueConfigValidation) | 		req := NewRequest(t, "GET", urlStr) | ||||||
|  | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
| 	assert.True(t, issueConfigValidation.Valid) | 		var issueConfigValidation api.IssueConfigValidation | ||||||
| 	assert.Empty(t, issueConfigValidation.Message) | 		DecodeJSON(t, resp, &issueConfigValidation) | ||||||
|  |  | ||||||
|  | 		assert.True(t, issueConfigValidation.Valid) | ||||||
|  | 		assert.Empty(t, issueConfigValidation.Message) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("Invalid", func(t *testing.T) { | ||||||
|  | 		config := make(map[string]any) | ||||||
|  | 		config["blank_issues_enabled"] = "Test" | ||||||
|  |  | ||||||
|  | 		createIssueConfig(t, owner, repo, config) | ||||||
|  |  | ||||||
|  | 		req := NewRequest(t, "GET", urlStr) | ||||||
|  | 		resp := MakeRequest(t, req, http.StatusOK) | ||||||
|  |  | ||||||
|  | 		var issueConfigValidation api.IssueConfigValidation | ||||||
|  | 		DecodeJSON(t, resp, &issueConfigValidation) | ||||||
|  |  | ||||||
|  | 		assert.False(t, issueConfigValidation.Valid) | ||||||
|  | 		assert.NotEmpty(t, issueConfigValidation.Message) | ||||||
|  | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package integration | |||||||
| import ( | import ( | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| @@ -29,6 +30,32 @@ func createFileInBranch(user *user_model.User, repo *repo_model.Repository, tree | |||||||
| 	return files_service.ChangeRepoFiles(git.DefaultContext, repo, user, opts) | 	return files_service.ChangeRepoFiles(git.DefaultContext, repo, user, opts) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func deleteFileInBranch(user *user_model.User, repo *repo_model.Repository, treePath, branchName string) (*api.FilesResponse, error) { | ||||||
|  | 	opts := &files_service.ChangeRepoFilesOptions{ | ||||||
|  | 		Files: []*files_service.ChangeRepoFile{ | ||||||
|  | 			{ | ||||||
|  | 				Operation: "delete", | ||||||
|  | 				TreePath:  treePath, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		OldBranch: branchName, | ||||||
|  | 		Author:    nil, | ||||||
|  | 		Committer: nil, | ||||||
|  | 	} | ||||||
|  | 	return files_service.ChangeRepoFiles(git.DefaultContext, repo, user, opts) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func createOrReplaceFileInBranch(user *user_model.User, repo *repo_model.Repository, treePath, branchName, content string) error { | ||||||
|  | 	_, err := deleteFileInBranch(user, repo, treePath, branchName) | ||||||
|  |  | ||||||
|  | 	if err != nil && !models.IsErrRepoFileDoesNotExist(err) { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = createFileInBranch(user, repo, treePath, branchName, content) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| func createFile(user *user_model.User, repo *repo_model.Repository, treePath string) (*api.FilesResponse, error) { | func createFile(user *user_model.User, repo *repo_model.Repository, treePath string) (*api.FilesResponse, error) { | ||||||
| 	return createFileInBranch(user, repo, treePath, repo.DefaultBranch, "This is a NEW file") | 	return createFileInBranch(user, repo, treePath, repo.DefaultBranch, "This is a NEW file") | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user