test: run integration tests on random ports and speed up slow tests (#39842)

Integration test processes now get their own work directory, config file
and random ports, so several runs can execute in parallel on one
machine. The slowest tests also lose fixed sleeps and needlessly
expensive setup, with their assertions kept.

- Batch PostgreSQL sequence resets, the main per-test fixture cost there
- Cheap hashing params in hash and OAuth2 fixture tests, full-cost
vectors stay
- Single fetch for negative Actions runner assertions instead of 1s
polls
- Queue flushes and backdated timestamps instead of sleeps
- In-memory SQLite test journal, tmpfs for PostgreSQL in CI
- E2E instance writes SSH keys into its work directory instead of
`~/.ssh/authorized_keys`
This commit is contained in:
silverwind
2026-10-18 12:56:04 +00:00
committed by GitHub
parent d0c35bcd58
commit 1031e34987
42 changed files with 219 additions and 179 deletions
+4
View File
@@ -25,6 +25,8 @@ jobs:
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
ports:
- "5432:5432"
ldap:
@@ -61,6 +63,8 @@ jobs:
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
ports:
- "5432:5432"
ldap:
+4
View File
@@ -48,6 +48,10 @@ Run a single integration test with the `#` selector:
make test-integration#TestName
```
Each run gets its own work directory under `tests/integration/` and random ports, so SQLite runs
can execute in parallel. Runs against other databases still share the database and object storage.
The work directory is removed after a passing run and kept after a failure.
If you hit errors such as a mismatched database version or SSH push failures, try a
clean rebuild first:
+6 -2
View File
@@ -105,7 +105,7 @@ func LoadTableSchemasMap(t *testing.T, x base.EngineMigration) map[string]*schem
func mainTest(m *testing.M) int {
testlogger.Init()
err := setting.PrepareIntegrationTestConfig()
workPath, err := setting.PrepareIntegrationTestConfig()
if err != nil {
return testlogger.MainErrorf("Unable to prepare integration test config: %v", err)
}
@@ -117,7 +117,11 @@ func mainTest(m *testing.M) int {
setting.Database.SlowQueryThreshold = 0
setting.LoadDBSetting()
setting.InitLoggersForTest()
return m.Run()
exitCode := m.Run()
if exitCode == 0 {
_ = os.RemoveAll(workPath)
}
return exitCode
}
func MainTest(m *testing.M) {
+2 -2
View File
@@ -3,7 +3,7 @@
uid: 1
name: "Test"
client_id: "da7da3ba-9a13-4167-856f-3899de0b0138"
client_secret: "$2a$10$UYRgUSgekzBp6hYe8pAdc.cgB4Gn06QRKsORUnIYTYQADs.YR/uvi" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
client_secret: "$2a$04$fk.C0LyaUcmxOFLcfcXUDO084yIA9o0nRwkSHSZb/tvZkCzLZjRea" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
redirect_uris: '["https://example.com"]'
created_unix: 1546869730
updated_unix: 1546869730
@@ -13,7 +13,7 @@
uid: 2
name: "Test native app"
client_id: "ce5a1322-42a7-11ed-b878-0242ac120002"
client_secret: "$2a$10$UYRgUSgekzBp6hYe8pAdc.cgB4Gn06QRKsORUnIYTYQADs.YR/uvi" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
client_secret: "$2a$04$VvPoNpflYJB94T8seLcZnOCTtQtB3Q2wXw7ZgrWIi2Oix3VWL8s7i" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
redirect_uris: '["http://127.0.0.1"]'
created_unix: 1546869730
updated_unix: 1546869730
+8 -5
View File
@@ -51,13 +51,16 @@ func loadFixtureResetSeqPgsql(e *xorm.Engine) error {
if err != nil {
return fmt.Errorf("failed to generate sequence update: %w", err)
}
for _, r := range results {
for _, value := range r {
_, err = e.Exec(value)
if err != nil {
return fmt.Errorf("failed to update sequence: %s, error: %w", value, err)
var statements []string
for _, result := range results {
for _, statement := range result {
statements = append(statements, statement)
}
}
if len(statements) > 0 {
if _, err := e.Exec(strings.Join(statements, "\n")); err != nil {
return fmt.Errorf("loadFixtureResetSeqPgsql: %w", err)
}
}
return nil
}
+1 -1
View File
@@ -206,7 +206,7 @@ type FixturesOptions struct {
// CreateTestEngine creates a test database and loads the fixture data from fixturesDir
func CreateTestEngine(testSQLiteFile string, opts FixturesOptions) error {
driver, connStr, err := db.ConnStr(db.ConnOptions{Type: setting.DatabaseTypeSQLite3, SQLitePath: testSQLiteFile, SQLiteBusyTimeout: setting.DefaultSQLiteBusyTimeout})
driver, connStr, err := db.ConnStr(db.ConnOptions{Type: setting.DatabaseTypeSQLite3, SQLitePath: testSQLiteFile, SQLiteBusyTimeout: setting.DefaultSQLiteBusyTimeout, SQLiteJournalMode: "MEMORY"})
if err != nil {
return err
}
+13 -2
View File
@@ -75,7 +75,18 @@ func TestHashing(t *testing.T) {
for _, algorithmName := range hashAlgorithmsToTest {
t.Run(algorithmName, func(t *testing.T) {
t.Parallel() // CPU-bound; these subtests only read the shared hasher registry
output, err := Parse(algorithmName).Hash(password, salt)
algorithm := *Parse(algorithmName)
switch algorithm.PasswordSaltHasher.(type) {
case *Argon2Hasher:
algorithm.PasswordSaltHasher = NewArgon2Hasher("2$64$8$50")
case *BcryptHasher:
algorithm.PasswordSaltHasher = NewBcryptHasher("4")
case *PBKDF2Hasher:
algorithm.PasswordSaltHasher = NewPBKDF2Hasher("1$50")
case *ScryptHasher:
algorithm.PasswordSaltHasher = NewScryptHasher("16$16$2$50")
}
output, err := algorithm.Hash(password, salt)
if shouldPass {
assert.NoError(t, err)
assert.NotEmpty(t, output, "output for %s was empty", algorithmName)
@@ -83,7 +94,7 @@ func TestHashing(t *testing.T) {
assert.Error(t, err)
}
assert.Equal(t, Parse(algorithmName).VerifyPassword(password, output, salt), shouldPass)
assert.Equal(t, algorithm.VerifyPassword(password, output, salt), shouldPass)
})
}
}
@@ -8,6 +8,7 @@
package tests
import (
"context"
"fmt"
"slices"
"testing"
@@ -744,6 +745,9 @@ func countIndexerData(data map[int64]*internal.IndexerData, f func(v *internal.I
// waitData waits for the indexer to index all data.
// Some engines like Elasticsearch index data asynchronously, so we need to wait for a while.
func waitData(t *testing.T, indexer internal.Indexer, total int64) {
if refresher, ok := indexer.(interface{ Refresh(context.Context) error }); ok {
require.NoError(t, refresher.Refresh(t.Context()))
}
assert.Eventually(t, func() bool {
result, err := indexer.Search(t.Context(), &internal.SearchOptions{Paginator: &db.ListOptions{}})
require.NoError(t, err)
@@ -0,0 +1,22 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package migration
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRetryDownloaderExhaustsAttempts(t *testing.T) {
failure := errors.New("permission denied")
attempts := 0
assert.ErrorIs(t, NewRetryDownloader(nil, 3, 0).retry(t.Context(), func(_ctx context.Context) error {
attempts++
return failure
}), failure)
assert.Equal(t, 3, attempts)
}
+34 -16
View File
@@ -6,6 +6,7 @@ package setting
import (
"errors"
"fmt"
"net"
"os"
"path/filepath"
"runtime"
@@ -129,40 +130,58 @@ func SetupGiteaTestEnv() {
_ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts
}
func PrepareIntegrationTestConfig() error {
// PrepareIntegrationTestConfig isolates each process for parallel runs
func PrepareIntegrationTestConfig() (workPath string, err error) {
giteaTestRoot := detectGiteaTestRoot()
isInCI := os.Getenv("CI") != ""
testDatabase := os.Getenv("GITEA_TEST_DATABASE")
if testDatabase == "" {
if isInCI {
return errors.New("GITEA_TEST_DATABASE environment variable not set")
return "", errors.New("GITEA_TEST_DATABASE environment variable not set")
}
// for local development, default to sqlite. CI needs to explicitly set a database to avoid unexpected results
testDatabase = "sqlite"
_, _ = fmt.Fprintf(os.Stderr, "Environment variable GITEA_TEST_DATABASE not set - defaulting to %s\n", testDatabase)
}
_ = os.Setenv("GITEA_TEST_ROOT", giteaTestRoot)
_ = os.Setenv("GITEA_TEST_CONF", filepath.Join("tests", testDatabase+".ini"))
workPath := filepath.Join(giteaTestRoot, "tests/integration/gitea-integration-"+testDatabase)
if err := os.MkdirAll(workPath, 0o755); err != nil {
return err
}
confFile := filepath.Join(giteaTestRoot, "tests", testDatabase+".ini")
tmplBuf, err := os.ReadFile(confFile + ".tmpl")
workPath, err = os.MkdirTemp(filepath.Join(giteaTestRoot, "tests/integration"), "gitea-integration-"+testDatabase+"-")
if err != nil {
return err
return "", err
}
confFile := filepath.Join(workPath, "app.ini")
giteaConf, err := filepath.Rel(giteaTestRoot, confFile)
if err != nil {
return "", err
}
_ = os.Setenv("GITEA_TEST_ROOT", giteaTestRoot)
_ = os.Setenv("GITEA_TEST_CONF", giteaConf)
tmplBuf, err := os.ReadFile(filepath.Join(giteaTestRoot, "tests", testDatabase+".ini.tmpl"))
if err != nil {
return "", err
}
tmpl := string(tmplBuf)
envVars, err := shellquote.Split(os.Getenv("MAKEFILE_VARS"))
if err != nil {
return err
return "", err
}
httpListener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return "", err
}
defer httpListener.Close()
sshListener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return "", err
}
defer sshListener.Close()
_, httpPort, _ := net.SplitHostPort(httpListener.Addr().String())
_, sshPort, _ := net.SplitHostPort(sshListener.Addr().String())
envVarMap := map[string]string{
"TEST_WORK_PATH": workPath,
"TEST_LOGGER": "test,file",
"TEST_HTTP_PORT": httpPort,
"TEST_SSH_PORT": sshPort,
}
for _, env := range append(os.Environ(), envVars...) {
k, v, _ := strings.Cut(env, "=")
@@ -173,6 +192,5 @@ func PrepareIntegrationTestConfig() error {
for k, v := range envVarMap {
tmpl = strings.ReplaceAll(tmpl, fmt.Sprintf("{{%s}}", k), v)
}
err = os.WriteFile(confFile, []byte(tmpl), 0o644)
return err
return workPath, os.WriteFile(confFile, []byte(tmpl), 0o644)
}
+1 -1
View File
@@ -223,7 +223,7 @@ func listDirRecursively(result *[]string, fsDir, recordParentPath string, opts *
}
defer dir.Close()
fis, err := dir.Readdir(0)
fis, err := dir.ReadDir(0)
if err != nil {
return err
}
+8 -8
View File
@@ -17,7 +17,7 @@ import (
)
func TestWriterReader(t *testing.T) {
testData := prepareTestData(t, 1_000_000)
testData := prepareTestData(t, 300000)
result := bytes.NewBuffer(nil)
@@ -73,13 +73,13 @@ func TestWriterReader(t *testing.T) {
}
func TestSeekableWriterReader(t *testing.T) {
testData := prepareTestData(t, 2_000_000)
testData := prepareTestData(t, 200000)
result := bytes.NewBuffer(nil)
t.Run("regular", func(t *testing.T) {
result.Reset()
blockSize := 100_000
blockSize := 10000
writer, err := NewSeekableWriter(result, blockSize)
require.NoError(t, err)
@@ -102,7 +102,7 @@ func TestSeekableWriterReader(t *testing.T) {
t.Run("seek read", func(t *testing.T) {
result.Reset()
blockSize := 100_000
blockSize := 10000
writer, err := NewSeekableWriter(result, blockSize)
require.NoError(t, err)
@@ -118,7 +118,7 @@ func TestSeekableWriterReader(t *testing.T) {
reader, err := NewSeekableReader(assertReader)
require.NoError(t, err)
_, err = reader.Seek(1_000_000, io.SeekStart)
_, err = reader.Seek(100000, io.SeekStart)
require.NoError(t, err)
data := make([]byte, 1000)
@@ -126,7 +126,7 @@ func TestSeekableWriterReader(t *testing.T) {
require.NoError(t, err)
require.NoError(t, reader.Close())
assert.Equal(t, testData[1_000_000:1_000_000+1000], data)
assert.Equal(t, testData[100000:100000+1000], data)
// Should seek 3 times,
// the first two times are for getting the index,
@@ -143,7 +143,7 @@ func TestSeekableWriterReader(t *testing.T) {
testData := prepareTestData(t, 1000) // data size is less than a block
result.Reset()
blockSize := 100_000
blockSize := 10000
writer, err := NewSeekableWriter(result, blockSize)
require.NoError(t, err)
@@ -192,7 +192,7 @@ func TestSeekableWriterReader(t *testing.T) {
t.Run("compatible reader", func(t *testing.T) {
result.Reset()
blockSize := 100_000
blockSize := 10000
writer, err := NewSeekableWriter(result, blockSize)
require.NoError(t, err)
+5 -5
View File
@@ -18,7 +18,7 @@ import (
)
func TestNewUserPost_MustChangePassword(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "admin/users/new")
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
@@ -55,7 +55,7 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
}
func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "admin/users/new")
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
@@ -92,7 +92,7 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
}
func TestNewUserPost_InvalidEmail(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "admin/users/new")
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
@@ -122,7 +122,7 @@ func TestNewUserPost_InvalidEmail(t *testing.T) {
}
func TestNewUserPost_VisibilityDefaultPublic(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "admin/users/new")
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
@@ -160,7 +160,7 @@ func TestNewUserPost_VisibilityDefaultPublic(t *testing.T) {
}
func TestNewUserPost_VisibilityPrivate(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "admin/users/new")
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
+2 -2
View File
@@ -23,7 +23,7 @@ import (
)
func TestIssueLabel(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("RetrieveLabels", testRetrieveLabels)
t.Run("NewLabel", testNewLabel)
t.Run("NewLabelInvalidColor", testNewLabelInvalidColor)
@@ -199,7 +199,7 @@ func testUpdateIssueLabelToggle(t *testing.T) {
{"toggle", []int64{1, 3}, 1, false},
{"toggle", []int64{1, 2}, 2, true},
} {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
+1 -1
View File
@@ -23,7 +23,7 @@ import (
)
func TestRetrieveRepoIssueMetaDataWithPrefill(t *testing.T) {
unittest.PrepareTestEnv(t)
require.NoError(t, unittest.PrepareTestDatabase())
require.NoError(t, db.Insert(t.Context(),
[]*issues_model.Label{
{ID: 100, RepoID: 1, Name: "Bug"},
+1 -1
View File
@@ -14,7 +14,7 @@ import (
)
func TestDiffViewStyle(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("AnonymousUser", func(t *testing.T) {
ctx, _ := contexttest.MockContext(t, "/any")
+7 -7
View File
@@ -30,7 +30,7 @@ import (
)
func TestAddDeployKey(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
t.Run("ReadOnly", func(t *testing.T) {
const testKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf\n"
ctx, _ := contexttest.MockContext(t, "POST /user2/repo1/settings/keys")
@@ -52,7 +52,7 @@ func TestAddDeployKey(t *testing.T) {
}
func TestCollaborationPost(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadUser(t, ctx, 4)
@@ -87,7 +87,7 @@ func TestCollaborationPost(t *testing.T) {
}
func TestCollaborationPost_InactiveUser(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadUser(t, ctx, 9)
@@ -110,7 +110,7 @@ func TestCollaborationPost_InactiveUser(t *testing.T) {
}
func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadUser(t, ctx, 4)
@@ -151,7 +151,7 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
}
func TestCollaborationPost_NonExistentUser(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "user2/repo1/issues/labels")
contexttest.LoadUser(t, ctx, 2)
contexttest.LoadRepo(t, ctx, 1)
@@ -173,7 +173,7 @@ func TestCollaborationPost_NonExistentUser(t *testing.T) {
}
func TestAddTeamPost(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26})
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 43})
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 11})
@@ -219,7 +219,7 @@ func TestAddTeamPost(t *testing.T) {
}
func TestDeleteTeam(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "org3/team1/repo3")
ctx.Req.Form.Set("id", "2")
+1 -1
View File
@@ -15,7 +15,7 @@ import (
)
func TestViewHomeSubmoduleRedirect(t *testing.T) {
unittest.PrepareTestEnv(t)
assert.NoError(t, unittest.PrepareTestDatabase())
ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
submodule := git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id")
-42
View File
@@ -154,48 +154,6 @@ func TestDeleteTeam(t *testing.T) {
assert.Less(t, accessMode, perm.AccessModeWrite)
}
func TestAddTeamMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(team *organization.Team, user *user_model.User) {
assert.NoError(t, AddTeamMember(t.Context(), team, user))
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: user.ID, TeamID: team.ID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID}, &user_model.User{ID: team.OrgID})
}
team1 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
team3 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 3})
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
test(team1, user2)
test(team1, user4)
test(team3, user2)
}
func TestRemoveTeamMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(team *organization.Team, user *user_model.User) {
assert.NoError(t, RemoveTeamMember(t.Context(), team, user))
unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: user.ID, TeamID: team.ID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID})
}
team1 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
team2 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
team3 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 3})
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
testSuccess(team1, user4)
testSuccess(team2, user2)
testSuccess(team3, user2)
err := RemoveTeamMember(t.Context(), team1, user2)
assert.True(t, organization.IsErrLastOrgOwner(err))
}
func TestIncludesAllRepositoriesTeams(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -5,7 +5,6 @@ package archiver
import (
"testing"
"time"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unittest"
@@ -106,9 +105,6 @@ func TestArchive_Basic(t *testing.T) {
// count.
doArchive(t.Context(), zipReq)
// Sleep two seconds to make sure the queue doesn't change.
time.Sleep(2 * time.Second)
zipReq2, err := NewRequest(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, firstCommit+".zip", nil)
assert.NoError(t, err)
// This zipReq should match what's sitting in the queue, as we haven't
+3 -6
View File
@@ -52,19 +52,16 @@ func TestTeam_RemoveRepository(t *testing.T) {
func TestDeleteOwnerRepositoriesDirectly(t *testing.T) {
unittest.PrepareTestEnv(t)
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, repo_service.DeleteOwnerRepositoriesDirectly(t.Context(), user))
t.Run("RejectedInTransaction", func(t *testing.T) {
unittest.PrepareTestEnv(t)
err := db.WithTx(t.Context(), func(ctx context.Context) error {
return repo_service.DeleteRepositoryDirectly(ctx, 1)
})
assert.ErrorContains(t, err, "must not be called within a transaction")
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, repo_service.DeleteOwnerRepositoriesDirectly(t.Context(), user))
}
func TestDeleteRepositoryDirectlyPurgesRepoScopedRows(t *testing.T) {
-2
View File
@@ -300,8 +300,6 @@ func TestPrepareWikiFileName(t *testing.T) {
}
func TestPrepareWikiFileName_FirstPage(t *testing.T) {
unittest.PrepareTestEnv(t)
// Now create a temporaryDirectory
tmpDir := t.TempDir()
@@ -1555,9 +1555,11 @@ jobs:
// wf1-job2 is waiting but no runner will run it
assert.Equal(t, actions_model.StatusWaiting, w1j2Job.Status)
time.Sleep(time.Second)
now := time.Now()
time.Sleep(time.Second)
_, err := db.GetEngine(t.Context()).NoAutoTime().ID(w1j2Job.ID).Cols("updated").Update(&actions_model.ActionRunJob{
Updated: timeutil.TimeStamp(now.Unix()).Add(-1),
})
require.NoError(t, err)
// push workflow2
opts2 := getWorkflowCreateFileOptions(user2, repo.DefaultBranch, "create %s"+wf2TreePath, wf2FileContent)
@@ -119,7 +119,7 @@ jobs:
}
runner.execTask(t, task, outcome)
}
runner.fetchNoTask(t, 300*time.Millisecond)
runner.fetchNoTask(t)
return names
}
+1 -2
View File
@@ -370,7 +370,7 @@ func TestRunnerDisableEnable(t *testing.T) {
MakeRequest(t, req, http.StatusOK)
testData.runner.execTask(t, task1, &mockTaskOutcome{result: runnerv1.Result_RESULT_SUCCESS})
testData.runner.fetchNoTask(t, 2*time.Second)
testData.runner.fetchNoTask(t)
req = newRunnerUpdateRequest(t, fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/%d", user2.Name, testData.repo.Name, testData.runnerID), false).AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
@@ -398,7 +398,6 @@ func TestRunnerDisableEnable(t *testing.T) {
// Trigger a second run so there is a pending job after we re-enable the runner
triggerRunnerDisableEnableRun(t, user2, token, testData.repo, "second-push.txt")
time.Sleep(500 * time.Millisecond) // allow workflow run to be created
req := newRunnerUpdateRequest(t, fmt.Sprintf("/api/v1/repos/%s/%s/actions/runners/%d", user2.Name, testData.repo.Name, testData.runnerID), true).AddTokenAuth(token)
MakeRequest(t, req, http.StatusOK)
+2 -2
View File
@@ -98,8 +98,8 @@ func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1
return task
}
func (r *mockRunner) fetchNoTask(t *testing.T, timeout ...time.Duration) {
task := r.tryFetchTask(t, timeout...)
func (r *mockRunner) fetchNoTask(t *testing.T) {
task, _ := r.fetchTaskOnce(t, 0)
require.Nil(t, task, "a task is fetched")
}
+3 -2
View File
@@ -9,9 +9,11 @@ import (
"testing"
"time"
"gitea.dev/modules/queue"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepoLanguages(t *testing.T) {
@@ -36,8 +38,7 @@ func TestRepoLanguages(t *testing.T) {
resp = session.MakeRequest(t, req, http.StatusOK)
assert.NotEmpty(t, test.RedirectURL(resp))
// let gitea calculate language stats
time.Sleep(time.Second)
require.NoError(t, queue.GetManager().FlushAll(t.Context(), 10*time.Second))
// Save new file to master branch
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/languages")
+4 -4
View File
@@ -11,10 +11,12 @@ import (
"time"
auth_model "gitea.dev/models/auth"
"gitea.dev/modules/queue"
api "gitea.dev/modules/structs"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var testLicenseContent = `
@@ -51,8 +53,7 @@ func TestAPIRepoLicense(t *testing.T) {
resp = session.MakeRequest(t, req, http.StatusOK)
assert.NotEmpty(t, test.RedirectURL(resp))
// let gitea update repo license
time.Sleep(time.Second)
require.NoError(t, queue.GetManager().FlushAll(t.Context(), 10*time.Second))
checkRepoLicense(t, "user2", "repo1", []string{"BSD-2-Clause"})
// Change default branch
@@ -63,8 +64,7 @@ func TestAPIRepoLicense(t *testing.T) {
}).AddTokenAuth(token)
session.MakeRequest(t, req, http.StatusOK)
// let gitea update repo license
time.Sleep(time.Second)
require.NoError(t, queue.GetManager().FlushAll(t.Context(), 10*time.Second))
checkRepoLicense(t, "user2", "repo1", []string{"MIT"})
})
}
+2 -2
View File
@@ -33,6 +33,7 @@ import (
"gitea.dev/modules/setting"
api "gitea.dev/modules/structs"
"gitea.dev/modules/test"
"gitea.dev/services/automergequeue"
"gitea.dev/tests"
"github.com/kballard/go-shellquote"
@@ -817,8 +818,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
// Call API to add Success status for commit
t.Run("CreateStatus", doAPICreateCommitStatusTest(ctx, commitID, commitstatus.CommitStatusSuccess, "testci"))
// wait to let gitea merge stuff
time.Sleep(time.Second)
require.NoError(t, automergequeue.AutoMergeQueue.FlushWithContext(t.Context(), 5*time.Second))
// test pr status
pr, err = doAPIGetPullRequest(ctx, baseCtx.Username, baseCtx.Reponame, pr.Index)(t)
+8 -10
View File
@@ -31,7 +31,7 @@ func TestGitPush(t *testing.T) {
func testGitPush(t *testing.T, u *url.URL) {
t.Run("Push branches at once", func(t *testing.T) {
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
for i := range 10 {
for i := range 4 {
branchName := fmt.Sprintf("branch-%d", i)
pushed = append(pushed, branchName)
doGitCreateBranch(gitPath, branchName)(t)
@@ -44,21 +44,19 @@ func testGitPush(t *testing.T, u *url.URL) {
t.Run("Push branches exists", func(t *testing.T) {
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
for i := range 10 {
for i := range 4 {
branchName := fmt.Sprintf("branch-%d", i)
if i < 5 {
if i < 2 {
pushed = append(pushed, branchName)
}
doGitCreateBranch(gitPath, branchName)(t)
}
// only push master and the first 5 branches
pushed = append(pushed, "master")
args := append([]string{"origin"}, pushed...)
doGitPushTestRepository(gitPath, args...)(t)
pushed = pushed[:0]
// do some changes for the first 5 branches created above
for i := range 5 {
for i := range 2 {
branchName := fmt.Sprintf("branch-%d", i)
pushed = append(pushed, branchName)
doGitCheckoutWriteFileCommit(localGitAddCommitOptions{
@@ -69,7 +67,7 @@ func testGitPush(t *testing.T, u *url.URL) {
})(t)
}
for i := 5; i < 10; i++ {
for i := 2; i < 4; i++ {
pushed = append(pushed, fmt.Sprintf("branch-%d", i))
}
pushed = append(pushed, "master")
@@ -83,7 +81,7 @@ func testGitPush(t *testing.T, u *url.URL) {
t.Run("Push branches one by one", func(t *testing.T) {
runTestGitPush(t, u, func(t *testing.T, gitPath string) (pushed, deleted []string) {
for i := range 10 {
for i := range 4 {
branchName := fmt.Sprintf("branch-%d", i)
doGitCreateBranch(gitPath, branchName)(t)
doGitPushTestRepository(gitPath, "origin", branchName)(t)
@@ -109,14 +107,14 @@ func testGitPush(t *testing.T, u *url.URL) {
doGitPushTestRepository(gitPath, "origin", "master")(t) // make sure master is the default branch instead of a branch we are going to delete
pushed = append(pushed, "master")
for i := range 10 {
for i := range 4 {
branchName := fmt.Sprintf("branch-%d", i)
pushed = append(pushed, branchName)
doGitCreateBranch(gitPath, branchName)(t)
}
doGitPushTestRepository(gitPath, "origin", "--all")(t)
for i := range 5 {
for i := range 2 {
branchName := fmt.Sprintf("branch-%d", i)
doGitPushTestRepository(gitPath, "origin", "--delete", branchName)(t)
deleted = append(deleted, branchName)
+6 -5
View File
@@ -48,7 +48,7 @@ func testMain(m *testing.M) int {
graceful.InitManager(managerCtx)
defer cancel()
err := tests.InitIntegrationTest()
workPath, err := tests.InitIntegrationTest()
if err != nil {
return testlogger.MainErrorf("InitTest error: %v", err)
}
@@ -67,11 +67,12 @@ func testMain(m *testing.M) int {
// Instead, "No tests were found", last nonsense log is "According to the configuration, subsequent logs will not be printed to the console"
exitCode := m.Run()
if err = os.RemoveAll(setting.Indexer.IssuePath); err != nil {
log.Error("Failed to remove indexer path: %v", err)
if exitCode == 0 {
graceful.GetManager().DoGracefulShutdown() // let background tasks finish writing
<-graceful.GetManager().Done()
if err = os.RemoveAll(workPath); err != nil {
log.Error("Failed to remove work path: %v", err)
}
if err = os.RemoveAll(setting.Indexer.RepoPath); err != nil {
log.Error("Failed to remove indexer path: %v", err)
}
return exitCode
}
+6 -1
View File
@@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"path"
"slices"
"strconv"
"strings"
"testing"
@@ -29,6 +30,7 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection {
@@ -131,8 +133,11 @@ func TestViewIssuesKeyword(t *testing.T) {
Index: 1,
})
issues.UpdateIssueIndexer(t.Context(), issue.ID)
time.Sleep(time.Second * 1)
const keyword = "first"
require.Eventually(t, func() bool {
issueIDs, _, err := issues.SearchIssues(t.Context(), &issues.SearchOptions{Keyword: keyword, RepoIDs: []int64{repo.ID}})
return err == nil && slices.Contains(issueIDs, issue.ID)
}, 10*time.Second, 10*time.Millisecond)
req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.Link(), keyword)
resp := MakeRequest(t, req, http.StatusOK)
+2 -2
View File
@@ -67,7 +67,7 @@ func TestMigrateGiteaForm(t *testing.T) {
repoName := "repo1"
repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName})
session := loginUser(t, ownerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadMisc)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeReadMisc, auth_model.AccessTokenScopeReadIssue)
// Step 0: verify the repo is available
req := NewRequestf(t, "GET", "/%s/%s", ownerName, repoName)
@@ -99,7 +99,7 @@ func TestMigrateGiteaForm(t *testing.T) {
loc := *test.ParseJSONRedirect(resp.Body.Bytes()).Redirect
assert.Equal(t, fmt.Sprintf("/%s/%s", ownerName, migratedRepoName), loc)
// Step 6: check the repo was created
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName})
assert.Equal(t, repo_model.RepositoryReady, unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: migratedRepoName}).Status)
})
}
@@ -34,7 +34,13 @@ var currentEngine db.EngineMigration
func initMigrationTest(t *testing.T) func() {
testlogger.Init()
require.NoError(t, setting.PrepareIntegrationTestConfig())
workPath, err := setting.PrepareIntegrationTestConfig()
require.NoError(t, err)
t.Cleanup(func() {
if !t.Failed() {
_ = os.RemoveAll(workPath)
}
})
setting.SetupGiteaTestEnv()
assert.NotEmpty(t, setting.RepoRootPath)
+12 -8
View File
@@ -11,6 +11,7 @@ import (
"net/url"
"os"
"path"
"slices"
"strconv"
"strings"
"testing"
@@ -29,8 +30,9 @@ import (
"gitea.dev/modules/commitstatus"
"gitea.dev/modules/git"
"gitea.dev/modules/git/gitcmd"
issue_indexer "gitea.dev/modules/indexer/issues"
"gitea.dev/modules/json"
"gitea.dev/modules/queue"
"gitea.dev/modules/optional"
"gitea.dev/modules/setting"
api "gitea.dev/modules/structs"
"gitea.dev/modules/test"
@@ -746,9 +748,6 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
createPullResp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "Indexer notifier test pull")
assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 0))
time.Sleep(time.Second)
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
OwnerName: "user2",
Name: "repo1",
@@ -759,6 +758,13 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
IsPull: true,
IsClosed: false,
})
waitIndexed := func(isClosed bool) { // elasticsearch makes documents searchable asynchronously
require.Eventually(t, func() bool {
ids, _, err := issue_indexer.SearchIssues(t.Context(), &issue_indexer.SearchOptions{Keyword: "notifier", RepoIDs: []int64{repo1.ID}, IsClosed: optional.Some(isClosed)})
return err == nil && slices.Contains(ids, issue.ID)
}, 10*time.Second, 10*time.Millisecond)
}
waitIndexed(false)
// build the request for searching issues
link, _ := url.Parse("/api/v1/repos/issues/search")
@@ -786,9 +792,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) {
ID: issue.ID,
})
assert.True(t, issue.IsClosed)
assert.NoError(t, queue.GetManager().FlushAll(t.Context(), 0))
time.Sleep(time.Second)
waitIndexed(true)
// search issues again
searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
@@ -937,7 +941,7 @@ func TestPullAutoMergeAfterCommitStatusSucceedAndApproval(t *testing.T) {
})
assert.NoError(t, err)
time.Sleep(2 * time.Second)
require.NoError(t, automergequeue.AutoMergeQueue.FlushWithContext(t.Context(), 5*time.Second))
// reload pr again
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
+6 -3
View File
@@ -1600,8 +1600,9 @@ jobs:
}
}
// Add this sleep to ensure the func can find the tasks by timestamp.
time.Sleep(time.Second)
_, err = db_model.GetEngine(ctx).Where("repo_id = ?", testRepo.ID).NoAutoTime().
SetExpr("updated", "updated - 2").Update(new(actions_model.ActionRunJob))
require.NoError(t, err)
err = actions.CancelAbandonedJobs(ctx)
assert.NoError(t, err)
@@ -1685,7 +1686,9 @@ jobs:
initialRunEventsLen := len(webhookData.payloads)
time.Sleep(2 * time.Second)
_, err := db_model.GetEngine(t.Context()).In("id", task1.Id, task2.Id).
SetExpr("started", "started - 3").Update(new(actions_model.ActionTask))
require.NoError(t, err)
require.NoError(t, actions.StopEndlessTasks(t.Context()))
+4 -4
View File
@@ -33,12 +33,12 @@ SIGNING_KEY = none
[server]
SSH_DOMAIN = localhost
HTTP_PORT = 3003
ROOT_URL = http://localhost:3003/
LOCAL_ROOT_URL = http://127.0.0.1:3003/
HTTP_PORT = {{TEST_HTTP_PORT}}
ROOT_URL = http://localhost:{{TEST_HTTP_PORT}}/
LOCAL_ROOT_URL = http://127.0.0.1:{{TEST_HTTP_PORT}}/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2201
SSH_PORT = {{TEST_SSH_PORT}}
START_SSH_SERVER = true
LFS_START_SERVER = true
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
+4 -4
View File
@@ -35,12 +35,12 @@ SIGNING_KEY = none
[server]
SSH_DOMAIN = localhost
HTTP_PORT = 3001
ROOT_URL = http://localhost:3001/
LOCAL_ROOT_URL = http://127.0.0.1:3001/
HTTP_PORT = {{TEST_HTTP_PORT}}
ROOT_URL = http://localhost:{{TEST_HTTP_PORT}}/
LOCAL_ROOT_URL = http://127.0.0.1:{{TEST_HTTP_PORT}}/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2201
SSH_PORT = {{TEST_SSH_PORT}}
BUILTIN_SSH_SERVER_USER = git
START_SSH_SERVER = true
+4 -4
View File
@@ -34,12 +34,12 @@ SIGNING_KEY = none
[server]
SSH_DOMAIN = localhost
HTTP_PORT = 3002
ROOT_URL = http://localhost:3002/
LOCAL_ROOT_URL = http://127.0.0.1:3002/
HTTP_PORT = {{TEST_HTTP_PORT}}
ROOT_URL = http://localhost:{{TEST_HTTP_PORT}}/
LOCAL_ROOT_URL = http://127.0.0.1:{{TEST_HTTP_PORT}}/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2202
SSH_PORT = {{TEST_SSH_PORT}}
START_SSH_SERVER = true
LFS_START_SERVER = true
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
+6 -5
View File
@@ -4,7 +4,8 @@ RUN_MODE = prod
[database]
DB_TYPE = sqlite3
PATH = gitea-test.db
PATH = {{TEST_WORK_PATH}}/gitea-test.db
SQLITE_JOURNAL_MODE = MEMORY
[indexer]
REPO_INDEXER_ENABLED = true
@@ -29,12 +30,12 @@ SIGNING_KEY = none
[server]
SSH_DOMAIN = localhost
HTTP_PORT = 3003
ROOT_URL = http://localhost:3003/
LOCAL_ROOT_URL = http://127.0.0.1:3003/
HTTP_PORT = {{TEST_HTTP_PORT}}
ROOT_URL = http://localhost:{{TEST_HTTP_PORT}}/
LOCAL_ROOT_URL = http://127.0.0.1:{{TEST_HTTP_PORT}}/
DISABLE_SSH = false
SSH_LISTEN_HOST = localhost
SSH_PORT = 2203
SSH_PORT = {{TEST_SSH_PORT}}
START_SSH_SERVER = true
LFS_START_SERVER = true
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
+7 -7
View File
@@ -21,35 +21,35 @@ import (
"github.com/stretchr/testify/assert"
)
func InitIntegrationTest() error {
func InitIntegrationTest() (workPath string, err error) {
testlogger.Init()
err := setting.PrepareIntegrationTestConfig()
workPath, err = setting.PrepareIntegrationTestConfig()
if err != nil {
return err
return "", err
}
setting.SetupGiteaTestEnv()
setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master"
if err := git.InitFull(); err != nil {
return err
return "", err
}
setting.Database.SlowQueryThreshold = 0
setting.LoadDBSetting()
cleanupDb, err := unittest.ResetTestDatabase()
if err != nil {
return err
return "", err
}
_ = cleanupDb // no clean up yet (not really needed at the moment)
if err := storage.Init(); err != nil {
return err
return "", err
}
routers.InitWebInstalled(graceful.GetManager().HammerContext())
return nil
return workPath, nil
}
func PrepareAttachmentsStorage(t testing.TB) {
+1
View File
@@ -103,6 +103,7 @@ PATH = $WORK_DIR/data/gitea.db
HTTP_PORT = $FREE_PORT
ROOT_URL = http://localhost:$FREE_PORT
STATIC_ROOT_PATH = $(pwd)
SSH_ROOT_PATH = $WORK_DIR/ssh
[security]
INSTALL_LOCK = true