From f78e397dd691dc4e48553ccdf8ce84baa41239bc Mon Sep 17 00:00:00 2001
From: Gusted <postmaster@gusted.xyz>
Date: Sun, 25 Aug 2024 02:47:35 +0200
Subject: [PATCH] [TESTS] Move `CreateDeclarativeRepo` to more accessible
 location

- This allows `CreateDeclarativeRepo` to be used by other testing
packages such as E2EE testing.
- Removes unused function in `services/webhook/sourcehut/builds_test.go`.
---
 services/webhook/sourcehut/builds_test.go     |  68 --------
 tests/integration/actions_route_test.go       |   6 +-
 tests/integration/actions_trigger_test.go     |   7 +-
 tests/integration/api_push_mirror_test.go     |   2 +-
 tests/integration/api_quota_use_test.go       |  10 +-
 tests/integration/api_repo_activities_test.go |   2 +-
 tests/integration/api_wiki_test.go            |   4 +-
 tests/integration/codeowner_test.go           |   2 +-
 tests/integration/compare_test.go             |   2 +-
 tests/integration/integration_test.go         | 145 -----------------
 tests/integration/issue_test.go               |   6 +-
 tests/integration/linguist_test.go            |   2 +-
 tests/integration/mirror_push_test.go         |   2 +-
 tests/integration/pull_create_test.go         |   2 +-
 tests/integration/pull_icon_test.go           |   2 +-
 tests/integration/pull_merge_test.go          |   3 +-
 tests/integration/pull_reopen_test.go         |   2 +-
 tests/integration/pull_update_test.go         |   3 +-
 tests/integration/quota_use_test.go           |   4 +-
 tests/integration/repo_activity_test.go       |   2 +-
 tests/integration/repo_archive_text_test.go   |   3 +-
 tests/integration/repo_badges_test.go         |   2 +-
 tests/integration/repo_citation_test.go       |   4 +-
 tests/integration/repo_settings_test.go       |   4 +-
 tests/integration/repo_signed_tag_test.go     |   2 +-
 tests/integration/repo_test.go                |   2 +-
 tests/integration/repo_view_test.go           |   4 +-
 tests/integration/size_translations_test.go   |   3 +-
 tests/integration/user_profile_test.go        |   2 +-
 tests/integration/user_test.go                |   2 +-
 tests/integration/view_test.go                |   4 +-
 tests/test_utils.go                           | 146 ++++++++++++++++++
 32 files changed, 196 insertions(+), 258 deletions(-)

diff --git a/services/webhook/sourcehut/builds_test.go b/services/webhook/sourcehut/builds_test.go
index 020bbfc9ad..1a37279c99 100644
--- a/services/webhook/sourcehut/builds_test.go
+++ b/services/webhook/sourcehut/builds_test.go
@@ -6,12 +6,7 @@ package sourcehut
 import (
 	"context"
 	"testing"
-	"time"
 
-	"code.gitea.io/gitea/models/db"
-	repo_model "code.gitea.io/gitea/models/repo"
-	unit_model "code.gitea.io/gitea/models/unit"
-	user_model "code.gitea.io/gitea/models/user"
 	webhook_model "code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/json"
@@ -19,8 +14,6 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/test"
 	webhook_module "code.gitea.io/gitea/modules/webhook"
-	repo_service "code.gitea.io/gitea/services/repository"
-	files_service "code.gitea.io/gitea/services/repository/files"
 	"code.gitea.io/gitea/services/webhook/shared"
 
 	"github.com/stretchr/testify/assert"
@@ -391,64 +384,3 @@ func TestSourcehutJSONPayload(t *testing.T) {
 	require.NoError(t, err)
 	assert.Equal(t, "json test", body.Variables.Note)
 }
-
-func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, enabledUnits, disabledUnits []unit_model.Type, files []*files_service.ChangeRepoFile) (*repo_model.Repository, string) {
-	t.Helper()
-
-	// Create a new repository
-	repo, err := repo_service.CreateRepository(db.DefaultContext, owner, owner, repo_service.CreateRepoOptions{
-		Name:          name,
-		Description:   "Temporary Repo",
-		AutoInit:      true,
-		Gitignores:    "",
-		License:       "WTFPL",
-		Readme:        "Default",
-		DefaultBranch: "main",
-	})
-	require.NoError(t, err)
-	assert.NotEmpty(t, repo)
-	t.Cleanup(func() {
-		repo_service.DeleteRepository(db.DefaultContext, owner, repo, false)
-	})
-
-	if enabledUnits != nil || disabledUnits != nil {
-		units := make([]repo_model.RepoUnit, len(enabledUnits))
-		for i, unitType := range enabledUnits {
-			units[i] = repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unitType,
-			}
-		}
-
-		err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, units, disabledUnits)
-		require.NoError(t, err)
-	}
-
-	var sha string
-	if len(files) > 0 {
-		resp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, owner, &files_service.ChangeRepoFilesOptions{
-			Files:     files,
-			Message:   "add files",
-			OldBranch: "main",
-			NewBranch: "main",
-			Author: &files_service.IdentityOptions{
-				Name:  owner.Name,
-				Email: owner.Email,
-			},
-			Committer: &files_service.IdentityOptions{
-				Name:  owner.Name,
-				Email: owner.Email,
-			},
-			Dates: &files_service.CommitDateOptions{
-				Author:    time.Now(),
-				Committer: time.Now(),
-			},
-		})
-		require.NoError(t, err)
-		assert.NotEmpty(t, resp)
-
-		sha = resp.Commit.SHA
-	}
-
-	return repo, sha
-}
diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go
index 2277da3cff..10618c89c7 100644
--- a/tests/integration/actions_route_test.go
+++ b/tests/integration/actions_route_test.go
@@ -37,7 +37,7 @@ func TestActionsWebRouteLatestWorkflowRun(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, _, f := CreateDeclarativeRepo(t, user2, "actionsTestRepo",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "actionsTestRepo",
 			[]unit_model.Type{unit_model.TypeActions}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
@@ -120,7 +120,7 @@ func TestActionsWebRouteLatestRun(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, _, f := CreateDeclarativeRepo(t, user2, "",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypeActions}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
@@ -153,7 +153,7 @@ func TestActionsArtifactDeletion(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, _, f := CreateDeclarativeRepo(t, user2, "",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypeActions}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go
index 0f27e204ca..2da3376ec6 100644
--- a/tests/integration/actions_trigger_test.go
+++ b/tests/integration/actions_trigger_test.go
@@ -29,6 +29,7 @@ import (
 	release_service "code.gitea.io/gitea/services/release"
 	repo_service "code.gitea.io/gitea/services/repository"
 	files_service "code.gitea.io/gitea/services/repository/files"
+	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -40,7 +41,7 @@ func TestPullRequestTargetEvent(t *testing.T) {
 		org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})  // owner of the forked repo
 
 		// create the base repo
-		baseRepo, _, f := CreateDeclarativeRepo(t, user2, "repo-pull-request-target",
+		baseRepo, _, f := tests.CreateDeclarativeRepo(t, user2, "repo-pull-request-target",
 			[]unit_model.Type{unit_model.TypeActions}, nil, nil,
 		)
 		defer f()
@@ -197,7 +198,7 @@ func TestSkipCI(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, _, f := CreateDeclarativeRepo(t, user2, "skip-ci",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "skip-ci",
 			[]unit_model.Type{unit_model.TypeActions}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
@@ -403,7 +404,7 @@ func TestWorkflowDispatchEvent(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, sha, f := CreateDeclarativeRepo(t, user2, "repo-workflow-dispatch",
+		repo, sha, f := tests.CreateDeclarativeRepo(t, user2, "repo-workflow-dispatch",
 			[]unit_model.Type{unit_model.TypeActions}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
diff --git a/tests/integration/api_push_mirror_test.go b/tests/integration/api_push_mirror_test.go
index a797a5fbf0..fb78e1bfaa 100644
--- a/tests/integration/api_push_mirror_test.go
+++ b/tests/integration/api_push_mirror_test.go
@@ -152,7 +152,7 @@ func TestAPIPushMirrorSSH(t *testing.T) {
 		assert.False(t, srcRepo.HasWiki())
 		session := loginUser(t, user.Name)
 		token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
-		pushToRepo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{
+		pushToRepo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{
 			Name:         optional.Some("push-mirror-test"),
 			AutoInit:     optional.Some(false),
 			EnabledUnits: optional.Some([]unit.Type{unit.TypeCode}),
diff --git a/tests/integration/api_quota_use_test.go b/tests/integration/api_quota_use_test.go
index 846ced34b1..11cbdcf145 100644
--- a/tests/integration/api_quota_use_test.go
+++ b/tests/integration/api_quota_use_test.go
@@ -292,7 +292,7 @@ func prepareQuotaEnv(t *testing.T, username string) *quotaEnv {
 	env.cleanups = append(env.cleanups, userCleanup)
 
 	// Create a repository
-	repo, _, repoCleanup := CreateDeclarativeRepoWithOptions(t, env.User.User, DeclarativeRepoOptions{})
+	repo, _, repoCleanup := tests.CreateDeclarativeRepoWithOptions(t, env.User.User, tests.DeclarativeRepoOptions{})
 	env.Repo = repo
 	env.cleanups = append(env.cleanups, repoCleanup)
 
@@ -487,7 +487,7 @@ func testAPIQuotaEnforcement(t *testing.T) {
 		defer tests.PrintCurrentTest(t)()
 
 		// Create a template repository
-		template, _, cleanup := CreateDeclarativeRepoWithOptions(t, env.User.User, DeclarativeRepoOptions{
+		template, _, cleanup := tests.CreateDeclarativeRepoWithOptions(t, env.User.User, tests.DeclarativeRepoOptions{
 			IsTemplate: optional.Some(true),
 		})
 		defer cleanup()
@@ -524,7 +524,7 @@ func testAPIQuotaEnforcement(t *testing.T) {
 
 	t.Run("#/repos/{username}/{reponame}", func(t *testing.T) {
 		// Lets create a new repo to play with.
-		repo, _, repoCleanup := CreateDeclarativeRepoWithOptions(t, env.User.User, DeclarativeRepoOptions{})
+		repo, _, repoCleanup := tests.CreateDeclarativeRepoWithOptions(t, env.User.User, tests.DeclarativeRepoOptions{})
 		defer repoCleanup()
 
 		// Drop the quota to 0
@@ -1209,7 +1209,7 @@ func testAPIQuotaEnforcement(t *testing.T) {
 				defer tests.PrintCurrentTest(t)()
 
 				// Create a repository to transfer
-				repo, _, cleanup := CreateDeclarativeRepoWithOptions(t, env.User.User, DeclarativeRepoOptions{})
+				repo, _, cleanup := tests.CreateDeclarativeRepoWithOptions(t, env.User.User, tests.DeclarativeRepoOptions{})
 				defer cleanup()
 
 				// Initiate repo transfer
@@ -1244,7 +1244,7 @@ func testAPIQuotaEnforcement(t *testing.T) {
 				defer env.SetRuleLimit(t, "deny-all", -1)()
 
 				// Create a repository to transfer
-				repo, _, cleanup := CreateDeclarativeRepoWithOptions(t, env.User.User, DeclarativeRepoOptions{})
+				repo, _, cleanup := tests.CreateDeclarativeRepoWithOptions(t, env.User.User, tests.DeclarativeRepoOptions{})
 				defer cleanup()
 
 				// Initiate repo transfer
diff --git a/tests/integration/api_repo_activities_test.go b/tests/integration/api_repo_activities_test.go
index 2b23ffc30e..dbdedec372 100644
--- a/tests/integration/api_repo_activities_test.go
+++ b/tests/integration/api_repo_activities_test.go
@@ -21,7 +21,7 @@ func TestAPIRepoActivitiyFeeds(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	repo, _, f := CreateDeclarativeRepoWithOptions(t, owner, DeclarativeRepoOptions{})
+	repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, owner, tests.DeclarativeRepoOptions{})
 	defer f()
 
 	feedURL := fmt.Sprintf("/api/v1/repos/%s/activities/feeds", repo.FullName())
diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go
index 5a033edb10..e5eb7a52c1 100644
--- a/tests/integration/api_wiki_test.go
+++ b/tests/integration/api_wiki_test.go
@@ -296,7 +296,7 @@ func TestAPISetWikiGlobalEditability(t *testing.T) {
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)
 
 	// Create a new repository for testing purposes
-	repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit_model.Type{
 		unit_model.TypeCode,
 		unit_model.TypeWiki,
 	}, nil, nil)
@@ -389,7 +389,7 @@ func TestAPIWikiNonMasterBranch(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-	repo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{
+	repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{
 		WikiBranch: optional.Some("main"),
 	})
 	defer f()
diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go
index 7bb6cc9dcf..71661bc56b 100644
--- a/tests/integration/codeowner_test.go
+++ b/tests/integration/codeowner_test.go
@@ -30,7 +30,7 @@ func TestCodeOwner(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// Create the repo.
-		repo, _, f := CreateDeclarativeRepo(t, user2, "",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypePullRequests}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go
index b0c6633cc5..c65335c469 100644
--- a/tests/integration/compare_test.go
+++ b/tests/integration/compare_test.go
@@ -224,7 +224,7 @@ func TestCompareCodeExpand(t *testing.T) {
 		owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
 
 		// Create a new repository, with a file that has many lines
-		repo, _, f := CreateDeclarativeRepoWithOptions(t, owner, DeclarativeRepoOptions{
+		repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, owner, tests.DeclarativeRepoOptions{
 			Files: optional.Some([]*files_service.ChangeRepoFile{
 				{
 					Operation:     "create",
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index 301a9e9540..e43200f4cb 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -28,15 +28,11 @@ import (
 	"code.gitea.io/gitea/cmd"
 	"code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
-	repo_model "code.gitea.io/gitea/models/repo"
-	unit_model "code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/git"
 	"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"
@@ -44,14 +40,10 @@ import (
 	"code.gitea.io/gitea/routers"
 	"code.gitea.io/gitea/services/auth/source/remote"
 	gitea_context "code.gitea.io/gitea/services/context"
-	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"
-	gouuid "github.com/google/uuid"
 	"github.com/markbates/goth"
 	"github.com/markbates/goth/gothic"
 	goth_github "github.com/markbates/goth/providers/github"
@@ -693,140 +685,3 @@ func GetHTMLTitle(t testing.TB, session *TestSession, urlStr string) string {
 	doc := NewHTMLParser(t, resp.Body)
 	return doc.Find("head title").Text()
 }
-
-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]
-	IsTemplate    optional.Option[bool]
-}
-
-func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts DeclarativeRepoOptions) (*repo_model.Repository, string, func()) {
-	t.Helper()
-
-	// 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()
-	}
-
-	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:      autoInit,
-		Gitignores:    "",
-		License:       "WTFPL",
-		Readme:        "Default",
-		DefaultBranch: "main",
-		IsTemplate:    opts.IsTemplate.Value(),
-	})
-	require.NoError(t, err)
-	assert.NotEmpty(t, repo)
-
-	// 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,
-			}
-		}
-	}
-
-	// 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))
-		require.NoError(t, err)
-	}
-
-	// Add files, if any.
-	var sha string
-	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",
-			OldBranch: "main",
-			NewBranch: "main",
-			Author: &files_service.IdentityOptions{
-				Name:  owner.Name,
-				Email: owner.Email,
-			},
-			Committer: &files_service.IdentityOptions{
-				Name:  owner.Name,
-				Email: owner.Email,
-			},
-			Dates: &files_service.CommitDateOptions{
-				Author:    time.Now(),
-				Committer: time.Now(),
-			},
-		})
-		require.NoError(t, err)
-		assert.NotEmpty(t, resp)
-
-		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")
-		require.NoError(t, err)
-
-		// Initialize the wiki
-		err = wiki_service.InitWiki(db.DefaultContext, repo)
-		require.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")
-		require.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 8a0505add2..4d9d99fb5e 100644
--- a/tests/integration/issue_test.go
+++ b/tests/integration/issue_test.go
@@ -338,7 +338,7 @@ func TestIssueDependencies(t *testing.T) {
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
 
-	repo, _, f := CreateDeclarativeRepoWithOptions(t, owner, DeclarativeRepoOptions{})
+	repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, owner, tests.DeclarativeRepoOptions{})
 	defer f()
 
 	createIssue := func(t *testing.T, title string) api.Issue {
@@ -1200,7 +1200,7 @@ func TestIssueForm(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 		session := loginUser(t, user2.Name)
-		repo, _, f := CreateDeclarativeRepo(t, user2, "",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypeCode, unit_model.TypeIssues}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
@@ -1250,7 +1250,7 @@ func TestIssueUnsubscription(t *testing.T) {
 		defer tests.PrepareTestEnv(t)()
 
 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-		repo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{
+		repo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{
 			AutoInit: optional.Some(false),
 		})
 		defer f()
diff --git a/tests/integration/linguist_test.go b/tests/integration/linguist_test.go
index b3d7331947..73423ee6a4 100644
--- a/tests/integration/linguist_test.go
+++ b/tests/integration/linguist_test.go
@@ -34,7 +34,7 @@ func TestLinguistSupport(t *testing.T) {
 
 			user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
-			repo, sha, f := CreateDeclarativeRepo(t, user2, "", nil, nil,
+			repo, sha, f := tests.CreateDeclarativeRepo(t, user2, "", nil, nil,
 				[]*files_service.ChangeRepoFile{
 					{
 						Operation:     "create",
diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go
index 45bca1c7e4..c2a0d5a45b 100644
--- a/tests/integration/mirror_push_test.go
+++ b/tests/integration/mirror_push_test.go
@@ -167,7 +167,7 @@ func TestSSHPushMirror(t *testing.T) {
 		srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
 		assert.False(t, srcRepo.HasWiki())
 		sess := loginUser(t, user.Name)
-		pushToRepo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{
+		pushToRepo, _, f := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{
 			Name:         optional.Some("push-mirror-test"),
 			AutoInit:     optional.Some(false),
 			EnabledUnits: optional.Some([]unit.Type{unit.TypeCode}),
diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go
index c042a75753..8bf48f4f6e 100644
--- a/tests/integration/pull_create_test.go
+++ b/tests/integration/pull_create_test.go
@@ -126,7 +126,7 @@ func TestPullCreateWithPullTemplate(t *testing.T) {
 				}
 			}
 
-			repo, _, deferrer := CreateDeclarativeRepo(t, baseUser, "", nil, nil, changeOps)
+			repo, _, deferrer := tests.CreateDeclarativeRepo(t, baseUser, "", nil, nil, changeOps)
 
 			return repo, deferrer
 		}
diff --git a/tests/integration/pull_icon_test.go b/tests/integration/pull_icon_test.go
index 0602f49179..8fde547ce9 100644
--- a/tests/integration/pull_icon_test.go
+++ b/tests/integration/pull_icon_test.go
@@ -32,7 +32,7 @@ import (
 func TestPullRequestIcons(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
-		repo, _, f := CreateDeclarativeRepo(t, user, "pr-icons", []unit_model.Type{unit_model.TypeCode, unit_model.TypePullRequests}, nil, nil)
+		repo, _, f := tests.CreateDeclarativeRepo(t, user, "pr-icons", []unit_model.Type{unit_model.TypeCode, unit_model.TypePullRequests}, nil, nil)
 		defer f()
 
 		session := loginUser(t, user.LoginName)
diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go
index b13ae60c45..9c6f33961e 100644
--- a/tests/integration/pull_merge_test.go
+++ b/tests/integration/pull_merge_test.go
@@ -42,6 +42,7 @@ import (
 	commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus"
 	files_service "code.gitea.io/gitea/services/repository/files"
 	webhook_service "code.gitea.io/gitea/services/webhook"
+	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -496,7 +497,7 @@ func TestConflictChecking(t *testing.T) {
 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// Create new clean repo to test conflict checking.
-		baseRepo, _, f := CreateDeclarativeRepo(t, user, "conflict-checking", nil, nil, nil)
+		baseRepo, _, f := tests.CreateDeclarativeRepo(t, user, "conflict-checking", nil, nil, nil)
 		defer f()
 
 		// create a commit on new branch.
diff --git a/tests/integration/pull_reopen_test.go b/tests/integration/pull_reopen_test.go
index 2b7c0c2bf6..e510d59626 100644
--- a/tests/integration/pull_reopen_test.go
+++ b/tests/integration/pull_reopen_test.go
@@ -37,7 +37,7 @@ func TestPullrequestReopen(t *testing.T) {
 		org26 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26})
 
 		// Create an base repository.
-		baseRepo, _, f := CreateDeclarativeRepo(t, user2, "reopen-base",
+		baseRepo, _, f := tests.CreateDeclarativeRepo(t, user2, "reopen-base",
 			[]unit_model.Type{unit_model.TypePullRequests}, nil, nil,
 		)
 		defer f()
diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go
index 003e92c0cf..08041f0717 100644
--- a/tests/integration/pull_update_test.go
+++ b/tests/integration/pull_update_test.go
@@ -19,6 +19,7 @@ import (
 	pull_service "code.gitea.io/gitea/services/pull"
 	repo_service "code.gitea.io/gitea/services/repository"
 	files_service "code.gitea.io/gitea/services/repository/files"
+	"code.gitea.io/gitea/tests"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -83,7 +84,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) {
 }
 
 func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest {
-	baseRepo, _, _ := CreateDeclarativeRepo(t, actor, "repo-pr-update", nil, nil, nil)
+	baseRepo, _, _ := tests.CreateDeclarativeRepo(t, actor, "repo-pr-update", nil, nil, nil)
 
 	headRepo, err := repo_service.ForkRepositoryAndUpdates(git.DefaultContext, actor, forkOrg, repo_service.ForkRepoOptions{
 		BaseRepo:    baseRepo,
diff --git a/tests/integration/quota_use_test.go b/tests/integration/quota_use_test.go
index b1fa15045a..06f23c9c23 100644
--- a/tests/integration/quota_use_test.go
+++ b/tests/integration/quota_use_test.go
@@ -1018,7 +1018,7 @@ func createQuotaWebEnv(t *testing.T) *quotaWebEnv {
 		user.Session = loginUser(t, userName)
 
 		// Create a repository for the user
-		repo, _, _ := CreateDeclarativeRepoWithOptions(t, user.User, DeclarativeRepoOptions{})
+		repo, _, _ := tests.CreateDeclarativeRepoWithOptions(t, user.User, tests.DeclarativeRepoOptions{})
 		user.Repo = repo
 
 		// Create a quota group for them
@@ -1058,7 +1058,7 @@ func createQuotaWebEnv(t *testing.T) *quotaWebEnv {
 
 		// Create a repository for the org
 		orgUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: org.Org.ID})
-		repo, _, _ := CreateDeclarativeRepoWithOptions(t, orgUser, DeclarativeRepoOptions{})
+		repo, _, _ := tests.CreateDeclarativeRepoWithOptions(t, orgUser, tests.DeclarativeRepoOptions{})
 		org.Repo = repo
 
 		// Create a quota group for them
diff --git a/tests/integration/repo_activity_test.go b/tests/integration/repo_activity_test.go
index dea7bcc415..c1177fac11 100644
--- a/tests/integration/repo_activity_test.go
+++ b/tests/integration/repo_activity_test.go
@@ -167,7 +167,7 @@ func TestRepoActivityOnlyCodeUnitWithNonEmptyRepo(t *testing.T) {
 	unit_model.LoadUnitConfig()
 
 	// Create a repo, with only code unit enabled.
-	repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{unit_model.TypeCode}, nil, nil)
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit_model.Type{unit_model.TypeCode}, nil, nil)
 	defer f()
 
 	req := NewRequest(t, "GET", fmt.Sprintf("%s/activity", repo.Link()))
diff --git a/tests/integration/repo_archive_text_test.go b/tests/integration/repo_archive_text_test.go
index 3f44562ad6..e759246aa4 100644
--- a/tests/integration/repo_archive_text_test.go
+++ b/tests/integration/repo_archive_text_test.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/translation"
+	"code.gitea.io/gitea/tests"
 
 	"github.com/PuerkitoBio/goquery"
 	"github.com/stretchr/testify/assert"
@@ -28,7 +29,7 @@ func TestArchiveText(t *testing.T) {
 		link := path.Join(testUser, testRepoName, "settings")
 
 		// Create test repo
-		_, _, f := CreateDeclarativeRepo(t, user2, testRepoName, nil, nil, nil)
+		_, _, f := tests.CreateDeclarativeRepo(t, user2, testRepoName, nil, nil, nil)
 		defer f()
 
 		// Test settings page
diff --git a/tests/integration/repo_badges_test.go b/tests/integration/repo_badges_test.go
index f1a61bd414..a74d3979c2 100644
--- a/tests/integration/repo_badges_test.go
+++ b/tests/integration/repo_badges_test.go
@@ -35,7 +35,7 @@ func TestBadges(t *testing.T) {
 		prep := func(t *testing.T) (*repo_model.Repository, func()) {
 			owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
-			repo, _, f := CreateDeclarativeRepo(t, owner, "",
+			repo, _, f := tests.CreateDeclarativeRepo(t, owner, "",
 				[]unit_model.Type{unit_model.TypeActions},
 				[]unit_model.Type{unit_model.TypeIssues, unit_model.TypePullRequests, unit_model.TypeReleases},
 				[]*files_service.ChangeRepoFile{
diff --git a/tests/integration/repo_citation_test.go b/tests/integration/repo_citation_test.go
index c5477eadaa..4f7e24ee51 100644
--- a/tests/integration/repo_citation_test.go
+++ b/tests/integration/repo_citation_test.go
@@ -28,7 +28,7 @@ func TestCitation(t *testing.T) {
 		t.Run("No citation", func(t *testing.T) {
 			defer tests.PrintCurrentTest(t)()
 
-			repo, _, f := CreateDeclarativeRepo(t, user, "citation-no-citation", []unit_model.Type{unit_model.TypeCode}, nil, nil)
+			repo, _, f := tests.CreateDeclarativeRepo(t, user, "citation-no-citation", []unit_model.Type{unit_model.TypeCode}, nil, nil)
 			defer f()
 
 			testCitationButtonExists(t, session, repo, "", false)
@@ -70,7 +70,7 @@ func testCitationButtonExists(t *testing.T, session *TestSession, repo *repo_mod
 }
 
 func createRepoWithEmptyFile(t *testing.T, user *user_model.User, repoName, fileName string) (*repo_model.Repository, func()) {
-	repo, _, f := CreateDeclarativeRepo(t, user, repoName, []unit_model.Type{unit_model.TypeCode}, nil, []*files_service.ChangeRepoFile{
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, repoName, []unit_model.Type{unit_model.TypeCode}, nil, []*files_service.ChangeRepoFile{
 		{
 			Operation: "create",
 			TreePath:  fileName,
diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go
index 6ea7ca6b3a..ff138532f9 100644
--- a/tests/integration/repo_settings_test.go
+++ b/tests/integration/repo_settings_test.go
@@ -51,7 +51,7 @@ func TestRepoAddMoreUnitsHighlighting(t *testing.T) {
 	unit_model.LoadUnitConfig()
 
 	// Create a known-good repo, with some units disabled.
-	repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit_model.Type{
 		unit_model.TypeCode,
 		unit_model.TypePullRequests,
 		unit_model.TypeProjects,
@@ -142,7 +142,7 @@ func TestRepoAddMoreUnits(t *testing.T) {
 	unit_model.LoadUnitConfig()
 
 	// Create a known-good repo, with all units enabled.
-	repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit_model.Type{
 		unit_model.TypeCode,
 		unit_model.TypePullRequests,
 		unit_model.TypeProjects,
diff --git a/tests/integration/repo_signed_tag_test.go b/tests/integration/repo_signed_tag_test.go
index 9b6188ef95..41e663ce52 100644
--- a/tests/integration/repo_signed_tag_test.go
+++ b/tests/integration/repo_signed_tag_test.go
@@ -29,7 +29,7 @@ func TestRepoSSHSignedTags(t *testing.T) {
 
 	// Preparations
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	repo, _, f := CreateDeclarativeRepo(t, user, "", nil, nil, nil)
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "", nil, nil, nil)
 	defer f()
 
 	// Set up an SSH key for the tagger
diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go
index bf8a7eb694..2d12df700b 100644
--- a/tests/integration/repo_test.go
+++ b/tests/integration/repo_test.go
@@ -834,7 +834,7 @@ func TestRepoFilesList(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, _, f := CreateDeclarativeRepo(t, user2, "",
+		repo, _, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypeCode}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
diff --git a/tests/integration/repo_view_test.go b/tests/integration/repo_view_test.go
index cc4084e21c..290686d554 100644
--- a/tests/integration/repo_view_test.go
+++ b/tests/integration/repo_view_test.go
@@ -47,7 +47,7 @@ func createRepoAndGetContext(t *testing.T, files []string, deleteMdReadme bool)
 	}
 
 	// README.md is already added by auto init
-	repo, _, f := CreateDeclarativeRepo(t, user, "readmetest", []unit_model.Type{unit_model.TypeCode}, nil, changeFiles)
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "readmetest", []unit_model.Type{unit_model.TypeCode}, nil, changeFiles)
 
 	ctx, _ := contexttest.MockContext(t, "user1/readmetest")
 	ctx.SetParams(":id", fmt.Sprint(repo.ID))
@@ -158,7 +158,7 @@ func TestRepoView_FindReadme(t *testing.T) {
 func TestRepoViewFileLines(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, _ *url.URL) {
 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-		repo, _, f := CreateDeclarativeRepo(t, user, "file-lines", []unit_model.Type{unit_model.TypeCode}, nil, []*files_service.ChangeRepoFile{
+		repo, _, f := tests.CreateDeclarativeRepo(t, user, "file-lines", []unit_model.Type{unit_model.TypeCode}, nil, []*files_service.ChangeRepoFile{
 			{
 				Operation:     "create",
 				TreePath:      "test-1",
diff --git a/tests/integration/size_translations_test.go b/tests/integration/size_translations_test.go
index ee03f5faa8..a0b88291f4 100644
--- a/tests/integration/size_translations_test.go
+++ b/tests/integration/size_translations_test.go
@@ -14,6 +14,7 @@ import (
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	files_service "code.gitea.io/gitea/services/repository/files"
+	"code.gitea.io/gitea/tests"
 
 	"github.com/PuerkitoBio/goquery"
 	"github.com/stretchr/testify/assert"
@@ -31,7 +32,7 @@ func TestDataSizeTranslation(t *testing.T) {
 		session := loginUser(t, testUser)
 
 		// Create test repo
-		testRepo, _, f := CreateDeclarativeRepo(t, user2, testRepoName, nil, nil,
+		testRepo, _, f := tests.CreateDeclarativeRepo(t, user2, testRepoName, nil, nil,
 			[]*files_service.ChangeRepoFile{
 				{
 					Operation:     "create",
diff --git a/tests/integration/user_profile_test.go b/tests/integration/user_profile_test.go
index 29758f3757..5532403510 100644
--- a/tests/integration/user_profile_test.go
+++ b/tests/integration/user_profile_test.go
@@ -44,7 +44,7 @@ func TestUserProfile(t *testing.T) {
 					})
 				}
 
-				_, _, f := CreateDeclarativeRepo(t, user2, ".profile", nil, nil, ops)
+				_, _, f := tests.CreateDeclarativeRepo(t, user2, ".profile", nil, nil, ops)
 				defer f()
 
 				// Perform the test
diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go
index e53f7f1e8d..035d0956f5 100644
--- a/tests/integration/user_test.go
+++ b/tests/integration/user_test.go
@@ -323,7 +323,7 @@ func TestUserHints(t *testing.T) {
 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)
 
 	// Create a known-good repo, with only one unit enabled
-	repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{
+	repo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit_model.Type{
 		unit_model.TypeCode,
 	}, []unit_model.Type{
 		unit_model.TypePullRequests,
diff --git a/tests/integration/view_test.go b/tests/integration/view_test.go
index 4e7c1b8cd2..ff2f2bdfd0 100644
--- a/tests/integration/view_test.go
+++ b/tests/integration/view_test.go
@@ -41,7 +41,7 @@ func TestAmbiguousCharacterDetection(t *testing.T) {
 		session := loginUser(t, user2.Name)
 
 		// Prepare the environments. File view, commit view (diff), wiki page.
-		repo, commitID, f := CreateDeclarativeRepo(t, user2, "",
+		repo, commitID, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypeCode, unit_model.TypeWiki}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
@@ -135,7 +135,7 @@ func TestInHistoryButton(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 		session := loginUser(t, user2.Name)
-		repo, commitID, f := CreateDeclarativeRepo(t, user2, "",
+		repo, commitID, f := tests.CreateDeclarativeRepo(t, user2, "",
 			[]unit_model.Type{unit_model.TypeCode, unit_model.TypeWiki}, nil,
 			[]*files_service.ChangeRepoFile{
 				{
diff --git a/tests/test_utils.go b/tests/test_utils.go
index 3b8ff7d310..ee1068961c 100644
--- a/tests/test_utils.go
+++ b/tests/test_utils.go
@@ -18,11 +18,15 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	packages_model "code.gitea.io/gitea/models/packages"
+	repo_model "code.gitea.io/gitea/models/repo"
+	unit_model "code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/optional"
 	"code.gitea.io/gitea/modules/process"
 	repo_module "code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
@@ -30,7 +34,12 @@ import (
 	"code.gitea.io/gitea/modules/testlogger"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/routers"
+	repo_service "code.gitea.io/gitea/services/repository"
+	files_service "code.gitea.io/gitea/services/repository/files"
+	wiki_service "code.gitea.io/gitea/services/wiki"
 
+	"github.com/google/uuid"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
@@ -304,3 +313,140 @@ func AddFixtures(dirs ...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]
+	IsTemplate    optional.Option[bool]
+}
+
+func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts DeclarativeRepoOptions) (*repo_model.Repository, string, func()) {
+	t.Helper()
+
+	// 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 = uuid.NewString()
+	}
+
+	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:      autoInit,
+		Gitignores:    "",
+		License:       "WTFPL",
+		Readme:        "Default",
+		DefaultBranch: "main",
+		IsTemplate:    opts.IsTemplate.Value(),
+	})
+	require.NoError(t, err)
+	assert.NotEmpty(t, repo)
+
+	// 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,
+			}
+		}
+	}
+
+	// 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))
+		require.NoError(t, err)
+	}
+
+	// Add files, if any.
+	var sha string
+	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",
+			OldBranch: "main",
+			NewBranch: "main",
+			Author: &files_service.IdentityOptions{
+				Name:  owner.Name,
+				Email: owner.Email,
+			},
+			Committer: &files_service.IdentityOptions{
+				Name:  owner.Name,
+				Email: owner.Email,
+			},
+			Dates: &files_service.CommitDateOptions{
+				Author:    time.Now(),
+				Committer: time.Now(),
+			},
+		})
+		require.NoError(t, err)
+		assert.NotEmpty(t, resp)
+
+		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")
+		require.NoError(t, err)
+
+		// Initialize the wiki
+		err = wiki_service.InitWiki(db.DefaultContext, repo)
+		require.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")
+		require.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)
+}