From 48d752c6301714bf699ded0bd5c236442bf79bb1 Mon Sep 17 00:00:00 2001
From: Gergely Nagy <forgejo@gergo.csillger.hu>
Date: Fri, 12 Jan 2024 11:50:29 +0100
Subject: [PATCH] [TESTS] Lift out CreateDeclarativeRepo()

There are a number of tests which require creating a repository on the
fly, and they each do it their own way. To reduce code duplication, lift
out this common pattern into a helper called `CreateDeclarativeRepo()`,
which lets us create a repository, set up enabled and disabled repo
units, and even add, delete, or update files.

Also convert a number of users of this pattern to the new helper - those
users that I introduced, and are in code introduced by Forgejo in the
first place.

Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
(cherry picked from commit 342b7bae3c85ebf36a625ba36d0a8c1ad822f794)
(cherry picked from commit 2ece8764e9929ed8d9ace7f7ba56d1c914369913)
---
 tests/integration/actions_route_test.go   |  51 +---
 tests/integration/integration_test.go     |  72 +++++
 tests/integration/repo_badges_test.go     | 331 ++++++++++------------
 tests/integration/repo_lang_stats_test.go | 133 +++------
 tests/integration/user_profile_test.go    | 103 ++-----
 5 files changed, 284 insertions(+), 406 deletions(-)

diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go
index 41bd4116c2..df22fc8647 100644
--- a/tests/integration/actions_route_test.go
+++ b/tests/integration/actions_route_test.go
@@ -9,16 +9,11 @@ import (
 	"net/url"
 	"strings"
 	"testing"
-	"time"
 
 	actions_model "code.gitea.io/gitea/models/actions"
-	"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"
-	repo_service "code.gitea.io/gitea/services/repository"
 	files_service "code.gitea.io/gitea/services/repository/files"
 
 	"github.com/stretchr/testify/assert"
@@ -29,53 +24,17 @@ func TestActionsWebRouteLatestRun(t *testing.T) {
 		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
 		// create the repo
-		repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
-			Name:          "actions-latest",
-			Description:   "test /actions/runs/latest",
-			AutoInit:      true,
-			Gitignores:    "Go",
-			License:       "MIT",
-			Readme:        "Default",
-			DefaultBranch: "main",
-			IsPrivate:     false,
-		})
-		assert.NoError(t, err)
-		assert.NotEmpty(t, repo)
-
-		// enable actions
-		err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
-			RepoID: repo.ID,
-			Type:   unit_model.TypeActions,
-		}}, nil)
-		assert.NoError(t, err)
-
-		// add workflow file to the repo
-		addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
-			Files: []*files_service.ChangeRepoFile{
+		repo, _, f := CreateDeclarativeRepo(t, user2, "",
+			[]unit_model.Type{unit_model.TypeActions}, nil,
+			[]*files_service.ChangeRepoFile{
 				{
 					Operation:     "create",
 					TreePath:      ".gitea/workflows/pr.yml",
 					ContentReader: strings.NewReader("name: test\non:\n  push:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo helloworld\n"),
 				},
 			},
-			Message:   "add workflow",
-			OldBranch: "main",
-			NewBranch: "main",
-			Author: &files_service.IdentityOptions{
-				Name:  user2.Name,
-				Email: user2.Email,
-			},
-			Committer: &files_service.IdentityOptions{
-				Name:  user2.Name,
-				Email: user2.Email,
-			},
-			Dates: &files_service.CommitDateOptions{
-				Author:    time.Now(),
-				Committer: time.Now(),
-			},
-		})
-		assert.NoError(t, err)
-		assert.NotEmpty(t, addWorkflowToBaseResp)
+		)
+		defer f()
 
 		// a run has been created
 		assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go
index 2dd8c8e7eb..44f5c122ce 100644
--- a/tests/integration/integration_test.go
+++ b/tests/integration/integration_test.go
@@ -1,4 +1,5 @@
 // Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
 // SPDX-License-Identifier: MIT
 
 //nolint:forbidigo
@@ -25,9 +26,12 @@ import (
 
 	"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"
 	gitea_context "code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -36,10 +40,13 @@ import (
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/web"
 	"code.gitea.io/gitea/routers"
+	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"
 	"code.gitea.io/gitea/tests"
 
 	"github.com/PuerkitoBio/goquery"
+	gouuid "github.com/google/uuid"
 	"github.com/markbates/goth"
 	"github.com/markbates/goth/gothic"
 	goth_gitlab "github.com/markbates/goth/providers/gitlab"
@@ -576,3 +583,68 @@ func GetHTMLTitle(t testing.TB, session *TestSession, urlStr string) string {
 	doc := NewHTMLParser(t, resp.Body)
 	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()) {
+	t.Helper()
+
+	repoName := name
+	if repoName == "" {
+		repoName = gouuid.NewString()
+	}
+
+	// Create a new repository
+	repo, err := repo_service.CreateRepository(db.DefaultContext, owner, owner, repo_service.CreateRepoOptions{
+		Name:          repoName,
+		Description:   "Temporary Repo",
+		AutoInit:      true,
+		Gitignores:    "",
+		License:       "WTFPL",
+		Readme:        "Default",
+		DefaultBranch: "main",
+	})
+	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{
+				RepoID: repo.ID,
+				Type:   unitType,
+			}
+		}
+
+		err := repo_model.UpdateRepositoryUnits(db.DefaultContext, repo, units, disabledUnits)
+		assert.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(),
+			},
+		})
+		assert.NoError(t, err)
+		assert.NotEmpty(t, resp)
+
+		sha = resp.Commit.SHA
+	}
+
+	return repo, sha, func() {
+		repo_service.DeleteRepository(db.DefaultContext, owner, repo, false)
+	}
+}
diff --git a/tests/integration/repo_badges_test.go b/tests/integration/repo_badges_test.go
index e4b634d1a8..18d04930f9 100644
--- a/tests/integration/repo_badges_test.go
+++ b/tests/integration/repo_badges_test.go
@@ -10,228 +10,181 @@ import (
 	"net/url"
 	"strings"
 	"testing"
-	"time"
 
 	actions_model "code.gitea.io/gitea/models/actions"
-	"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/test"
-	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"
 )
 
-func assertBadge(t *testing.T, resp *httptest.ResponseRecorder, badge string) {
-	assert.Equal(t, fmt.Sprintf("https://img.shields.io/badge/%s", badge), test.RedirectURL(resp))
-}
-
-func createMinimalRepo(t *testing.T) func() {
-	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-
-	// Create a new repository
-	repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
-		Name:          "minimal",
-		Description:   "minimal repo for badge testing",
-		AutoInit:      true,
-		Gitignores:    "Go",
-		License:       "MIT",
-		Readme:        "Default",
-		DefaultBranch: "main",
-		IsPrivate:     false,
-	})
-	assert.NoError(t, err)
-	assert.NotEmpty(t, repo)
-
-	// Enable Actions, and disable Issues, PRs and Releases
-	err = repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{
-		RepoID: repo.ID,
-		Type:   unit_model.TypeActions,
-	}}, []unit_model.Type{unit_model.TypeIssues, unit_model.TypePullRequests, unit_model.TypeReleases})
-	assert.NoError(t, err)
-
-	return func() {
-		repo_service.DeleteRepository(db.DefaultContext, user2, repo, false)
-	}
-}
-
-func addWorkflow(t *testing.T) {
-	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-	repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "minimal")
-	assert.NoError(t, err)
-
-	// Add a workflow file to the repo
-	addWorkflowToBaseResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2, &files_service.ChangeRepoFilesOptions{
-		Files: []*files_service.ChangeRepoFile{
-			{
-				Operation:     "create",
-				TreePath:      ".gitea/workflows/pr.yml",
-				ContentReader: strings.NewReader("name: test\non:\n  push:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo helloworld\n"),
-			},
-		},
-		Message:   "add workflow",
-		OldBranch: "main",
-		NewBranch: "main",
-		Author: &files_service.IdentityOptions{
-			Name:  user2.Name,
-			Email: user2.Email,
-		},
-		Committer: &files_service.IdentityOptions{
-			Name:  user2.Name,
-			Email: user2.Email,
-		},
-		Dates: &files_service.CommitDateOptions{
-			Author:    time.Now(),
-			Committer: time.Now(),
-		},
-	})
-	assert.NoError(t, err)
-	assert.NotEmpty(t, addWorkflowToBaseResp)
-
-	assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
-}
-
-func TestWorkflowBadges(t *testing.T) {
-	onGiteaRun(t, func(t *testing.T, u *url.URL) {
-		defer tests.PrintCurrentTest(t)()
-		defer createMinimalRepo(t)()
-
-		addWorkflow(t)
-
-		// Actions disabled
-		req := NewRequest(t, "GET", "/user2/repo1/badges/workflows/test.yaml/badge.svg")
-		resp := MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "test.yaml-Not%20found-crimson")
-
-		req = NewRequest(t, "GET", "/user2/repo1/badges/workflows/test.yaml/badge.svg?branch=no-such-branch")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "test.yaml-Not%20found-crimson")
-
-		// Actions enabled
-		req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-waiting-lightgrey")
-
-		req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg?branch=main")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-waiting-lightgrey")
-
-		req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg?branch=no-such-branch")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-Not%20found-crimson")
-
-		req = NewRequest(t, "GET", "/user2/minimal/badges/workflows/pr.yml/badge.svg?event=cron")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-Not%20found-crimson")
-
-		// GitHub compatibility
-		req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-waiting-lightgrey")
-
-		req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg?branch=main")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-waiting-lightgrey")
-
-		req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg?branch=no-such-branch")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-Not%20found-crimson")
-
-		req = NewRequest(t, "GET", "/user2/minimal/actions/workflows/pr.yml/badge.svg?event=cron")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pr.yml-Not%20found-crimson")
-	})
-}
-
 func TestBadges(t *testing.T) {
-	defer tests.PrepareTestEnv(t)()
+	onGiteaRun(t, func(t *testing.T, u *url.URL) {
+		prep := func(t *testing.T) (*repo_model.Repository, func()) {
+			owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 
-	t.Run("Stars", func(t *testing.T) {
-		defer tests.PrintCurrentTest(t)()
+			repo, _, f := CreateDeclarativeRepo(t, owner, "",
+				[]unit_model.Type{unit_model.TypeActions},
+				[]unit_model.Type{unit_model.TypeIssues, unit_model.TypePullRequests, unit_model.TypeReleases},
+				[]*files_service.ChangeRepoFile{
+					{
+						Operation:     "create",
+						TreePath:      ".gitea/workflows/pr.yml",
+						ContentReader: strings.NewReader("name: test\non:\n  push:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo helloworld\n"),
+					},
+				},
+			)
 
-		req := NewRequest(t, "GET", "/user2/repo1/badges/stars.svg")
-		resp := MakeRequest(t, req, http.StatusSeeOther)
+			assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID}))
 
-		assertBadge(t, resp, "stars-0-blue")
-	})
+			return repo, f
+		}
 
-	t.Run("Issues", func(t *testing.T) {
-		defer tests.PrintCurrentTest(t)()
-		defer createMinimalRepo(t)()
+		assertBadge := func(t *testing.T, resp *httptest.ResponseRecorder, badge string) {
+			t.Helper()
 
-		// Issues enabled
-		req := NewRequest(t, "GET", "/user2/repo1/badges/issues.svg")
-		resp := MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "issues-2-blue")
+			assert.Equal(t, fmt.Sprintf("https://img.shields.io/badge/%s", badge), test.RedirectURL(resp))
+		}
 
-		req = NewRequest(t, "GET", "/user2/repo1/badges/issues/open.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "issues-1%20open-blue")
+		t.Run("Workflows", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+			repo, f := prep(t)
+			defer f()
 
-		req = NewRequest(t, "GET", "/user2/repo1/badges/issues/closed.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "issues-1%20closed-blue")
+			// Actions disabled
+			req := NewRequest(t, "GET", "/user2/repo1/badges/workflows/test.yaml/badge.svg")
+			resp := MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "test.yaml-Not%20found-crimson")
 
-		// Issues disabled
-		req = NewRequest(t, "GET", "/user2/minimal/badges/issues.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "issues-Not%20found-crimson")
+			req = NewRequest(t, "GET", "/user2/repo1/badges/workflows/test.yaml/badge.svg?branch=no-such-branch")
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "test.yaml-Not%20found-crimson")
 
-		req = NewRequest(t, "GET", "/user2/minimal/badges/issues/open.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "issues-Not%20found-crimson")
+			// Actions enabled
+			req = NewRequestf(t, "GET", "/user2/%s/badges/workflows/pr.yml/badge.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-waiting-lightgrey")
 
-		req = NewRequest(t, "GET", "/user2/minimal/badges/issues/closed.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "issues-Not%20found-crimson")
-	})
+			req = NewRequestf(t, "GET", "/user2/%s/badges/workflows/pr.yml/badge.svg?branch=main", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-waiting-lightgrey")
 
-	t.Run("Pulls", func(t *testing.T) {
-		defer tests.PrintCurrentTest(t)()
-		defer createMinimalRepo(t)()
+			req = NewRequestf(t, "GET", "/user2/%s/badges/workflows/pr.yml/badge.svg?branch=no-such-branch", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-Not%20found-crimson")
 
-		// Pull requests enabled
-		req := NewRequest(t, "GET", "/user2/repo1/badges/pulls.svg")
-		resp := MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pulls-3-blue")
+			req = NewRequestf(t, "GET", "/user2/%s/badges/workflows/pr.yml/badge.svg?event=cron", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-Not%20found-crimson")
 
-		req = NewRequest(t, "GET", "/user2/repo1/badges/pulls/open.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pulls-3%20open-blue")
+			// GitHub compatibility
+			req = NewRequestf(t, "GET", "/user2/%s/actions/workflows/pr.yml/badge.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-waiting-lightgrey")
 
-		req = NewRequest(t, "GET", "/user2/repo1/badges/pulls/closed.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pulls-0%20closed-blue")
+			req = NewRequestf(t, "GET", "/user2/%s/actions/workflows/pr.yml/badge.svg?branch=main", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-waiting-lightgrey")
 
-		// Pull requests disabled
-		req = NewRequest(t, "GET", "/user2/minimal/badges/pulls.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pulls-Not%20found-crimson")
+			req = NewRequestf(t, "GET", "/user2/%s/actions/workflows/pr.yml/badge.svg?branch=no-such-branch", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-Not%20found-crimson")
 
-		req = NewRequest(t, "GET", "/user2/minimal/badges/pulls/open.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pulls-Not%20found-crimson")
+			req = NewRequestf(t, "GET", "/user2/%s/actions/workflows/pr.yml/badge.svg?event=cron", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pr.yml-Not%20found-crimson")
+		})
 
-		req = NewRequest(t, "GET", "/user2/minimal/badges/pulls/closed.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "pulls-Not%20found-crimson")
-	})
+		t.Run("Stars", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
 
-	t.Run("Release", func(t *testing.T) {
-		defer tests.PrintCurrentTest(t)()
-		defer createMinimalRepo(t)()
+			req := NewRequest(t, "GET", "/user2/repo1/badges/stars.svg")
+			resp := MakeRequest(t, req, http.StatusSeeOther)
 
-		req := NewRequest(t, "GET", "/user2/repo1/badges/release.svg")
-		resp := MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "release-v1.1-blue")
+			assertBadge(t, resp, "stars-0-blue")
+		})
 
-		req = NewRequest(t, "GET", "/user2/minimal/badges/release.svg")
-		resp = MakeRequest(t, req, http.StatusSeeOther)
-		assertBadge(t, resp, "release-Not%20found-crimson")
+		t.Run("Issues", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+			repo, f := prep(t)
+			defer f()
+
+			// Issues enabled
+			req := NewRequest(t, "GET", "/user2/repo1/badges/issues.svg")
+			resp := MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "issues-2-blue")
+
+			req = NewRequest(t, "GET", "/user2/repo1/badges/issues/open.svg")
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "issues-1%20open-blue")
+
+			req = NewRequest(t, "GET", "/user2/repo1/badges/issues/closed.svg")
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "issues-1%20closed-blue")
+
+			// Issues disabled
+			req = NewRequestf(t, "GET", "/user2/%s/badges/issues.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "issues-Not%20found-crimson")
+
+			req = NewRequestf(t, "GET", "/user2/%s/badges/issues/open.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "issues-Not%20found-crimson")
+
+			req = NewRequestf(t, "GET", "/user2/%s/badges/issues/closed.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "issues-Not%20found-crimson")
+		})
+
+		t.Run("Pulls", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+			repo, f := prep(t)
+			defer f()
+
+			// Pull requests enabled
+			req := NewRequest(t, "GET", "/user2/repo1/badges/pulls.svg")
+			resp := MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pulls-3-blue")
+
+			req = NewRequest(t, "GET", "/user2/repo1/badges/pulls/open.svg")
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pulls-3%20open-blue")
+
+			req = NewRequest(t, "GET", "/user2/repo1/badges/pulls/closed.svg")
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pulls-0%20closed-blue")
+
+			// Pull requests disabled
+			req = NewRequestf(t, "GET", "/user2/%s/badges/pulls.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pulls-Not%20found-crimson")
+
+			req = NewRequestf(t, "GET", "/user2/%s/badges/pulls/open.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pulls-Not%20found-crimson")
+
+			req = NewRequestf(t, "GET", "/user2/%s/badges/pulls/closed.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "pulls-Not%20found-crimson")
+		})
+
+		t.Run("Release", func(t *testing.T) {
+			defer tests.PrintCurrentTest(t)()
+			repo, f := prep(t)
+			defer f()
+
+			req := NewRequest(t, "GET", "/user2/repo1/badges/release.svg")
+			resp := MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "release-v1.1-blue")
+
+			req = NewRequestf(t, "GET", "/user2/%s/badges/release.svg", repo.Name)
+			resp = MakeRequest(t, req, http.StatusSeeOther)
+			assertBadge(t, resp, "release-Not%20found-crimson")
+		})
 	})
 }
diff --git a/tests/integration/repo_lang_stats_test.go b/tests/integration/repo_lang_stats_test.go
index f3a7e4bc6d..3cc924c81b 100644
--- a/tests/integration/repo_lang_stats_test.go
+++ b/tests/integration/repo_lang_stats_test.go
@@ -14,105 +14,14 @@ import (
 	repo_model "code.gitea.io/gitea/models/repo"
 	"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/indexer/stats"
 	"code.gitea.io/gitea/modules/queue"
-	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"
 )
 
-func createLangStatTestRepo(t *testing.T) (*repo_model.Repository, func()) {
-	t.Helper()
-
-	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-
-	// Create a new repository
-	repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
-		Name:          "lang-stat-test",
-		Description:   "minimal repo for language stats testing",
-		AutoInit:      true,
-		Gitignores:    "Go",
-		License:       "MIT",
-		Readme:        "Default",
-		DefaultBranch: "main",
-		IsPrivate:     false,
-	})
-	assert.NoError(t, err)
-	assert.NotEmpty(t, repo)
-
-	return repo, func() {
-		repo_service.DeleteRepository(db.DefaultContext, user2, repo, false)
-	}
-}
-
-func addLangStatTestFiles(t *testing.T, repo *repo_model.Repository, contents string) string {
-	t.Helper()
-
-	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
-
-	addFilesResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, owner, &files_service.ChangeRepoFilesOptions{
-		Files: []*files_service.ChangeRepoFile{
-			{
-				Operation:     "create",
-				TreePath:      ".gitattributes",
-				ContentReader: strings.NewReader(contents),
-			},
-			{
-				Operation:     "create",
-				TreePath:      "docs.md",
-				ContentReader: strings.NewReader("This **is** a `markdown` file.\n"),
-			},
-			{
-				Operation:     "create",
-				TreePath:      "foo.c",
-				ContentReader: strings.NewReader(`#include <stdio.h>\nint main() {\n  printf("Hello world!\n");\n  return 0;\n}\n`),
-			},
-			{
-				Operation:     "create",
-				TreePath:      "foo.nib",
-				ContentReader: strings.NewReader("Pinky promise, this is not a generated file!\n"),
-			},
-			{
-				Operation:     "create",
-				TreePath:      ".dot.pas",
-				ContentReader: strings.NewReader("program Hello;\nbegin\n  writeln('Hello, world.');\nend.\n"),
-			},
-			{
-				Operation:     "create",
-				TreePath:      "cpplint.py",
-				ContentReader: strings.NewReader(`#! /usr/bin/env python\n\nprint("Hello world!")\n`),
-			},
-			{
-				Operation:     "create",
-				TreePath:      "some-file.xml",
-				ContentReader: strings.NewReader(`<?xml version="1.0"?>\n<foo>\n <bar>Hello</bar>\n</foo>\n`),
-			},
-		},
-		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(),
-		},
-	})
-	assert.NoError(t, err)
-	assert.NotEmpty(t, addFilesResp)
-
-	return addFilesResp.Commit.SHA
-}
-
 func TestRepoLangStats(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		/******************
@@ -121,8 +30,46 @@ func TestRepoLangStats(t *testing.T) {
 		prep := func(t *testing.T, attribs string) (*repo_model.Repository, string, func()) {
 			t.Helper()
 
-			repo, f := createLangStatTestRepo(t)
-			sha := addLangStatTestFiles(t, repo, attribs)
+			user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+			repo, sha, f := CreateDeclarativeRepo(t, user2, "", nil, nil,
+				[]*files_service.ChangeRepoFile{
+					{
+						Operation:     "create",
+						TreePath:      ".gitattributes",
+						ContentReader: strings.NewReader(attribs),
+					},
+					{
+						Operation:     "create",
+						TreePath:      "docs.md",
+						ContentReader: strings.NewReader("This **is** a `markdown` file.\n"),
+					},
+					{
+						Operation:     "create",
+						TreePath:      "foo.c",
+						ContentReader: strings.NewReader(`#include <stdio.h>\nint main() {\n  printf("Hello world!\n");\n  return 0;\n}\n`),
+					},
+					{
+						Operation:     "create",
+						TreePath:      "foo.nib",
+						ContentReader: strings.NewReader("Pinky promise, this is not a generated file!\n"),
+					},
+					{
+						Operation:     "create",
+						TreePath:      ".dot.pas",
+						ContentReader: strings.NewReader("program Hello;\nbegin\n  writeln('Hello, world.');\nend.\n"),
+					},
+					{
+						Operation:     "create",
+						TreePath:      "cpplint.py",
+						ContentReader: strings.NewReader(`#! /usr/bin/env python\n\nprint("Hello world!")\n`),
+					},
+					{
+						Operation:     "create",
+						TreePath:      "some-file.xml",
+						ContentReader: strings.NewReader(`<?xml version="1.0"?>\n<foo>\n <bar>Hello</bar>\n</foo>\n`),
+					},
+				})
 
 			return repo, sha, f
 		}
diff --git a/tests/integration/user_profile_test.go b/tests/integration/user_profile_test.go
index 001e061674..29758f3757 100644
--- a/tests/integration/user_profile_test.go
+++ b/tests/integration/user_profile_test.go
@@ -8,99 +8,46 @@ import (
 	"net/url"
 	"strings"
 	"testing"
-	"time"
 
-	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
-	"code.gitea.io/gitea/modules/git"
-	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"
 )
 
-func createProfileRepo(t *testing.T, readmeName string) func() {
-	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
-
-	// Create a new repository
-	repo, err := repo_service.CreateRepository(db.DefaultContext, user2, user2, repo_service.CreateRepoOptions{
-		Name:          ".profile",
-		DefaultBranch: "main",
-		IsPrivate:     false,
-		AutoInit:      true,
-		Readme:        "Default",
-	})
-	assert.NoError(t, err)
-	assert.NotEmpty(t, repo)
-
-	deleteInitialReadmeResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2,
-		&files_service.ChangeRepoFilesOptions{
-			Files: []*files_service.ChangeRepoFile{
-				{
-					Operation: "delete",
-					TreePath:  "README.md",
-				},
-			},
-			Message: "Delete the initial readme",
-			Author: &files_service.IdentityOptions{
-				Name:  user2.Name,
-				Email: user2.Email,
-			},
-			Committer: &files_service.IdentityOptions{
-				Name:  user2.Name,
-				Email: user2.Email,
-			},
-			Dates: &files_service.CommitDateOptions{
-				Author:    time.Now(),
-				Committer: time.Now(),
-			},
-		})
-	assert.NoError(t, err)
-	assert.NotEmpty(t, deleteInitialReadmeResp)
-
-	if readmeName != "" {
-		addReadmeResp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user2,
-			&files_service.ChangeRepoFilesOptions{
-				Files: []*files_service.ChangeRepoFile{
-					{
-						Operation:     "create",
-						TreePath:      readmeName,
-						ContentReader: strings.NewReader("# Hi!\n"),
-					},
-				},
-				Message: "Add a readme",
-				Author: &files_service.IdentityOptions{
-					Name:  user2.Name,
-					Email: user2.Email,
-				},
-				Committer: &files_service.IdentityOptions{
-					Name:  user2.Name,
-					Email: user2.Email,
-				},
-				Dates: &files_service.CommitDateOptions{
-					Author:    time.Now(),
-					Committer: time.Now(),
-				},
-			})
-
-		assert.NoError(t, err)
-		assert.NotEmpty(t, addReadmeResp)
-	}
-
-	return func() {
-		repo_service.DeleteRepository(db.DefaultContext, user2, repo, false)
-	}
-}
-
 func TestUserProfile(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		checkReadme := func(t *testing.T, title, readmeFilename string, expectedCount int) {
 			t.Run(title, func(t *testing.T) {
 				defer tests.PrintCurrentTest(t)()
-				defer createProfileRepo(t, readmeFilename)()
 
+				// Prepare the test repository
+				user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+
+				var ops []*files_service.ChangeRepoFile
+				op := "create"
+				if readmeFilename != "README.md" {
+					ops = append(ops, &files_service.ChangeRepoFile{
+						Operation: "delete",
+						TreePath:  "README.md",
+					})
+				} else {
+					op = "update"
+				}
+				if readmeFilename != "" {
+					ops = append(ops, &files_service.ChangeRepoFile{
+						Operation:     op,
+						TreePath:      readmeFilename,
+						ContentReader: strings.NewReader("# Hi!\n"),
+					})
+				}
+
+				_, _, f := CreateDeclarativeRepo(t, user2, ".profile", nil, nil, ops)
+				defer f()
+
+				// Perform the test
 				req := NewRequest(t, "GET", "/user2")
 				resp := MakeRequest(t, req, http.StatusOK)