From 66e0e1e2c94c5922028c115e5403f01b20047e37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 04:18:19 +0000 Subject: [PATCH 01/33] models/fixtures: remove test data from webhook.yml, use code to insert in tests Agent-Logs-Url: https://github.com/go-gitea/gitea/sessions/52d595da-6991-440e-9cae-a4e070410229 Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com> --- models/fixtures/webhook.yml | 53 +---- models/webhook/webhook_system_test.go | 28 ++- models/webhook/webhook_test.go | 272 ++++++++++++++++++++++---- routers/api/v1/repo/hook_test.go | 14 +- services/webhook/webhook_test.go | 58 +++--- 5 files changed, 301 insertions(+), 124 deletions(-) diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml index f372aaaecb41d..830bb411ae182 100644 --- a/models/fixtures/webhook.yml +++ b/models/fixtures/webhook.yml @@ -1,54 +1,3 @@ -- - id: 1 - repo_id: 1 - url: https://www.example.com/url1 - content_type: 1 # json - events: '{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}' - is_active: true - -- - id: 2 - repo_id: 1 - url: https://www.example.com/url2 - content_type: 1 # json - events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' - is_active: false - -- - id: 3 - owner_id: 3 - repo_id: 3 - url: https://www.example.com/url3 - content_type: 1 # json - events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' - is_active: true - -- - id: 4 - repo_id: 2 - url: https://www.example.com/url4 - content_type: 1 # json - events: '{"push_only":true,"branch_filter":"{master,feature*}"}' - is_active: true - -- - id: 5 - repo_id: 0 - owner_id: 0 - url: https://www.example.com/url5 - content_type: 1 # json - events: '{"push_only":true,"branch_filter":"{master,feature*}"}' - is_active: true - is_system_webhook: true - -- - id: 6 - repo_id: 0 - owner_id: 0 - url: https://www.example.com/url6 - content_type: 1 # json - events: '{"push_only":true,"branch_filter":"{master,feature*}"}' - is_active: true - is_system_webhook: false +[] # DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly diff --git a/models/webhook/webhook_system_test.go b/models/webhook/webhook_system_test.go index d0013c6873f00..04915d2a67db7 100644 --- a/models/webhook/webhook_system_test.go +++ b/models/webhook/webhook_system_test.go @@ -14,24 +14,44 @@ import ( func TestListSystemWebhookOptions(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + + systemHook := &Webhook{ + URL: "https://www.example.com/system", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + IsSystemWebhook: true, + } + assert.NoError(t, CreateWebhook(t.Context(), systemHook)) + + defaultHook := &Webhook{ + URL: "https://www.example.com/default", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + IsSystemWebhook: false, + } + assert.NoError(t, CreateWebhook(t.Context(), defaultHook)) + opts := ListSystemWebhookOptions{IsSystem: optional.None[bool]()} hooks, _, err := GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 2) { - assert.Equal(t, int64(5), hooks[0].ID) - assert.Equal(t, int64(6), hooks[1].ID) + assert.Equal(t, systemHook.ID, hooks[0].ID) + assert.Equal(t, defaultHook.ID, hooks[1].ID) } + opts.IsSystem = optional.Some(true) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, int64(5), hooks[0].ID) + assert.Equal(t, systemHook.ID, hooks[0].ID) } opts.IsSystem = optional.Some(false) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, int64(6), hooks[0].ID) + assert.Equal(t, defaultHook.ID, hooks[0].ID) } } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 71f50017c51fc..cae08ffd7b4b4 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -30,24 +30,52 @@ func TestIsValidHookContentType(t *testing.T) { func TestWebhook_History(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1}) - tasks, err := webhook.History(t.Context(), 0) + + hook1 := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/history1", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, CreateWebhook(t.Context(), hook1)) + task1, err := CreateHookTask(t.Context(), &HookTask{HookID: hook1.ID, IsDelivered: true, PayloadVersion: 2}) + assert.NoError(t, err) + task2, err := CreateHookTask(t.Context(), &HookTask{HookID: hook1.ID, IsDelivered: true, PayloadVersion: 2}) + assert.NoError(t, err) + task3, err := CreateHookTask(t.Context(), &HookTask{HookID: hook1.ID, IsDelivered: true, PayloadVersion: 2}) + assert.NoError(t, err) + + tasks, err := hook1.History(t.Context(), 0) assert.NoError(t, err) if assert.Len(t, tasks, 3) { - assert.Equal(t, int64(3), tasks[0].ID) - assert.Equal(t, int64(2), tasks[1].ID) - assert.Equal(t, int64(1), tasks[2].ID) + assert.Equal(t, task3.ID, tasks[0].ID) + assert.Equal(t, task2.ID, tasks[1].ID) + assert.Equal(t, task1.ID, tasks[2].ID) } - webhook = unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}) - tasks, err = webhook.History(t.Context(), 0) + hook2 := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/history2", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: false, + } + assert.NoError(t, CreateWebhook(t.Context(), hook2)) + tasks, err = hook2.History(t.Context(), 0) assert.NoError(t, err) assert.Empty(t, tasks) } func TestWebhook_UpdateEvent(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1}) + webhook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/update_event", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), webhook)) hookEvent := &webhook_module.HookEvent{ PushOnly: true, SendEverything: false, @@ -101,9 +129,17 @@ func TestCreateWebhook(t *testing.T) { func TestGetWebhookByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook, err := GetWebhookByRepoID(t.Context(), 1, 1) + hook := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/by_repo_id", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + + loaded, err := GetWebhookByRepoID(t.Context(), 1, hook.ID) assert.NoError(t, err) - assert.Equal(t, int64(1), hook.ID) + assert.Equal(t, hook.ID, loaded.ID) _, err = GetWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -112,9 +148,18 @@ func TestGetWebhookByRepoID(t *testing.T) { func TestGetWebhookByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook, err := GetWebhookByOwnerID(t.Context(), 3, 3) + hook := &Webhook{ + OwnerID: 3, + URL: "https://www.example.com/by_owner_id", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + + loaded, err := GetWebhookByOwnerID(t.Context(), 3, hook.ID) assert.NoError(t, err) - assert.Equal(t, int64(3), hook.ID) + assert.Equal(t, hook.ID, loaded.ID) _, err = GetWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -123,47 +168,106 @@ func TestGetWebhookByOwnerID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook1 := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/active1", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, CreateWebhook(t.Context(), hook1)) + hook2 := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/inactive1", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: false, + } + assert.NoError(t, CreateWebhook(t.Context(), hook2)) + hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, int64(1), hooks[0].ID) + assert.Equal(t, hook1.ID, hooks[0].ID) assert.True(t, hooks[0].IsActive) } } func TestGetWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook1 := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/repo1_hook1", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, CreateWebhook(t.Context(), hook1)) + hook2 := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/repo1_hook2", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: false, + } + assert.NoError(t, CreateWebhook(t.Context(), hook2)) + hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1}) assert.NoError(t, err) if assert.Len(t, hooks, 2) { - assert.Equal(t, int64(1), hooks[0].ID) - assert.Equal(t, int64(2), hooks[1].ID) + assert.Equal(t, hook1.ID, hooks[0].ID) + assert.Equal(t, hook2.ID, hooks[1].ID) } } func TestGetActiveWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + OwnerID: 3, + URL: "https://www.example.com/owner_active", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, int64(3), hooks[0].ID) + assert.Equal(t, hook.ID, hooks[0].ID) assert.True(t, hooks[0].IsActive) } } func TestGetWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + OwnerID: 3, + URL: "https://www.example.com/owner_hook", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, int64(3), hooks[0].ID) + assert.Equal(t, hook.ID, hooks[0].ID) assert.True(t, hooks[0].IsActive) } } func TestUpdateWebhook(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}) + hook := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/update", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: false, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hook.IsActive = true hook.ContentType = ContentTypeForm unittest.AssertNotExistsBean(t, hook) @@ -173,9 +277,16 @@ func TestUpdateWebhook(t *testing.T) { func TestDeleteWebhookByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2, RepoID: 1}) - assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, 2)) - unittest.AssertNotExistsBean(t, &Webhook{ID: 2, RepoID: 1}) + hook := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/delete_by_repo", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + unittest.AssertExistsAndLoadBean(t, &Webhook{ID: hook.ID, RepoID: 1}) + assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, hook.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, RepoID: 1}) err := DeleteWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -184,9 +295,16 @@ func TestDeleteWebhookByRepoID(t *testing.T) { func TestDeleteWebhookByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OwnerID: 3}) - assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, 3)) - unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OwnerID: 3}) + hook := &Webhook{ + OwnerID: 3, + URL: "https://www.example.com/delete_by_owner", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + unittest.AssertExistsAndLoadBean(t, &Webhook{ID: hook.ID, OwnerID: 3}) + assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, hook.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, OwnerID: 3}) err := DeleteWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -195,12 +313,26 @@ func TestDeleteWebhookByOwnerID(t *testing.T) { func TestHookTasks(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hookTasks, err := HookTasks(t.Context(), 1, 1) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/hook_tasks", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + task1, err := CreateHookTask(t.Context(), &HookTask{HookID: hook.ID, IsDelivered: true, PayloadVersion: 2}) + assert.NoError(t, err) + task2, err := CreateHookTask(t.Context(), &HookTask{HookID: hook.ID, IsDelivered: true, PayloadVersion: 2}) + assert.NoError(t, err) + task3, err := CreateHookTask(t.Context(), &HookTask{HookID: hook.ID, IsDelivered: true, PayloadVersion: 2}) + assert.NoError(t, err) + + hookTasks, err := HookTasks(t.Context(), hook.ID, 1) assert.NoError(t, err) if assert.Len(t, hookTasks, 3) { - assert.Equal(t, int64(3), hookTasks[0].ID) - assert.Equal(t, int64(2), hookTasks[1].ID) - assert.Equal(t, int64(1), hookTasks[2].ID) + assert.Equal(t, task3.ID, hookTasks[0].ID) + assert.Equal(t, task2.ID, hookTasks[1].ID) + assert.Equal(t, task1.ID, hookTasks[2].ID) } hookTasks, err = HookTasks(t.Context(), unittest.NonexistentID, 1) @@ -210,8 +342,15 @@ func TestHookTasks(t *testing.T) { func TestCreateHookTask(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/create_task", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 3, + HookID: hook.ID, PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) @@ -222,19 +361,35 @@ func TestCreateHookTask(t *testing.T) { func TestUpdateHookTask(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/update_task", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) + hookTask := &HookTask{HookID: hook.ID, PayloadVersion: 2} + _, err := CreateHookTask(t.Context(), hookTask) + assert.NoError(t, err) - hook := unittest.AssertExistsAndLoadBean(t, &HookTask{ID: 1}) - hook.PayloadContent = "new payload content" - hook.IsDelivered = true - unittest.AssertNotExistsBean(t, hook) - assert.NoError(t, UpdateHookTask(t.Context(), hook)) - unittest.AssertExistsAndLoadBean(t, hook) + hookTask.PayloadContent = "new payload content" + hookTask.IsDelivered = true + unittest.AssertNotExistsBean(t, hookTask) + assert.NoError(t, UpdateHookTask(t.Context(), hookTask)) + unittest.AssertExistsAndLoadBean(t, hookTask) } func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/cleanup1", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 3, + HookID: hook.ID, IsDelivered: true, Delivered: timeutil.TimeStampNanoNow(), PayloadVersion: 2, @@ -250,8 +405,15 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/cleanup2", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 4, + HookID: hook.ID, IsDelivered: false, PayloadVersion: 2, } @@ -266,8 +428,15 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/cleanup3", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 4, + HookID: hook.ID, IsDelivered: true, Delivered: timeutil.TimeStampNanoNow(), PayloadVersion: 2, @@ -283,8 +452,15 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/cleanup4", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 3, + HookID: hook.ID, IsDelivered: true, Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -8).UnixNano()), PayloadVersion: 2, @@ -300,8 +476,15 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/cleanup5", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 4, + HookID: hook.ID, IsDelivered: false, PayloadVersion: 2, } @@ -316,8 +499,15 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + hook := &Webhook{ + RepoID: 3, + URL: "https://www.example.com/cleanup6", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + } + assert.NoError(t, CreateWebhook(t.Context(), hook)) hookTask := &HookTask{ - HookID: 4, + HookID: hook.ID, IsDelivered: true, Delivered: timeutil.TimeStampNano(time.Now().AddDate(0, 0, -6).UnixNano()), PayloadVersion: 2, diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go index f8d61ccf00067..b9505d9dc98a5 100644 --- a/routers/api/v1/repo/hook_test.go +++ b/routers/api/v1/repo/hook_test.go @@ -4,6 +4,7 @@ package repo import ( + "fmt" "net/http" "testing" @@ -17,8 +18,17 @@ import ( func TestTestHook(t *testing.T) { unittest.PrepareTestEnv(t) + hook := &webhook.Webhook{ + RepoID: 1, + URL: "https://www.example.com/test_hook", + ContentType: webhook.ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + assert.NoError(t, webhook.CreateWebhook(t.Context(), hook)) + ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/wiki/_pages") - ctx.SetPathParam("id", "1") + ctx.SetPathParam("id", fmt.Sprintf("%d", hook.ID)) contexttest.LoadRepo(t, ctx, 1) contexttest.LoadRepoCommit(t, ctx) contexttest.LoadUser(t, ctx, 2) @@ -26,6 +36,6 @@ func TestTestHook(t *testing.T) { assert.Equal(t, http.StatusNoContent, ctx.Resp.WrittenStatus()) unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{ - HookID: 1, + HookID: hook.ID, }, unittest.Cond("is_delivered=?", false)) } diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index f4432cc3f1198..74240bb675c0c 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -37,50 +37,58 @@ func TestPrepareWebhooks(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) - hookTasks := []*webhook_model.HookTask{ - {HookID: 1, EventType: webhook_module.HookEventPush}, - } - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) + hook := &webhook_model.Webhook{ + RepoID: repo.ID, + URL: "https://www.example.com/prepare_webhooks", + ContentType: webhook_model.ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, } + assert.NoError(t, webhook_model.CreateWebhook(t.Context(), hook)) + + hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} + unittest.AssertNotExistsBean(t, hookTask) assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}})) - for _, hookTask := range hookTasks { - unittest.AssertExistsAndLoadBean(t, hookTask) - } + unittest.AssertExistsAndLoadBean(t, hookTask) } func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - hookTasks := []*webhook_model.HookTask{ - {HookID: 4, EventType: webhook_module.HookEventPush}, - } - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) + hook := &webhook_model.Webhook{ + RepoID: repo.ID, + URL: "https://www.example.com/branch_filter_match", + ContentType: webhook_model.ContentTypeJSON, + Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, + IsActive: true, } + assert.NoError(t, webhook_model.CreateWebhook(t.Context(), hook)) + + hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} + unittest.AssertNotExistsBean(t, hookTask) // this test also ensures that * doesn't handle / in any special way (like shell would) assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}})) - for _, hookTask := range hookTasks { - unittest.AssertExistsAndLoadBean(t, hookTask) - } + unittest.AssertExistsAndLoadBean(t, hookTask) } func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - hookTasks := []*webhook_model.HookTask{ - {HookID: 4, EventType: webhook_module.HookEventPush}, - } - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) + hook := &webhook_model.Webhook{ + RepoID: repo.ID, + URL: "https://www.example.com/branch_filter_no_match", + ContentType: webhook_model.ContentTypeJSON, + Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, + IsActive: true, } - assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"})) + assert.NoError(t, webhook_model.CreateWebhook(t.Context(), hook)) - for _, hookTask := range hookTasks { - unittest.AssertNotExistsBean(t, hookTask) - } + hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} + unittest.AssertNotExistsBean(t, hookTask) + assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"})) + unittest.AssertNotExistsBean(t, hookTask) } func TestWebhookUserMail(t *testing.T) { From 8bb79e76fa7af10bd6468a8a18f14fcdd33453d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 04:38:57 +0000 Subject: [PATCH 02/33] models/webhook: use db.Insert in tests, merge fixture setup into prepareWebhookTestData Agent-Logs-Url: https://github.com/go-gitea/gitea/sessions/d187e3a5-2c68-4115-a4d1-d2bda8f54004 Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com> --- models/webhook/webhook_system_test.go | 29 +- models/webhook/webhook_test.go | 372 ++++++++++---------------- routers/api/v1/repo/hook_test.go | 3 +- services/webhook/webhook_test.go | 7 +- 4 files changed, 159 insertions(+), 252 deletions(-) diff --git a/models/webhook/webhook_system_test.go b/models/webhook/webhook_system_test.go index 04915d2a67db7..32bd4878cbfd8 100644 --- a/models/webhook/webhook_system_test.go +++ b/models/webhook/webhook_system_test.go @@ -6,52 +6,33 @@ package webhook import ( "testing" - "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" ) func TestListSystemWebhookOptions(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - systemHook := &Webhook{ - URL: "https://www.example.com/system", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - IsSystemWebhook: true, - } - assert.NoError(t, CreateWebhook(t.Context(), systemHook)) - - defaultHook := &Webhook{ - URL: "https://www.example.com/default", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - IsSystemWebhook: false, - } - assert.NoError(t, CreateWebhook(t.Context(), defaultHook)) + fixture := prepareWebhookTestData(t) opts := ListSystemWebhookOptions{IsSystem: optional.None[bool]()} hooks, _, err := GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 2) { - assert.Equal(t, systemHook.ID, hooks[0].ID) - assert.Equal(t, defaultHook.ID, hooks[1].ID) + assert.Equal(t, fixture.HookSystem.ID, hooks[0].ID) + assert.Equal(t, fixture.HookDefault.ID, hooks[1].ID) } opts.IsSystem = optional.Some(true) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, systemHook.ID, hooks[0].ID) + assert.Equal(t, fixture.HookSystem.ID, hooks[0].ID) } opts.IsSystem = optional.Some(false) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, defaultHook.ID, hooks[0].ID) + assert.Equal(t, fixture.HookDefault.ID, hooks[0].ID) } } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index cae08ffd7b4b4..732e164e9a349 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -15,8 +15,102 @@ import ( webhook_module "code.gitea.io/gitea/modules/webhook" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) +// webhookTestFixtures holds standard webhook test data created by prepareWebhookTestData. +type webhookTestFixtures struct { + HookRepo1 *Webhook + HookRepo1Inactive *Webhook + HookOwner3 *Webhook + HookRepo2Branch *Webhook + HookSystem *Webhook + HookDefault *Webhook + HookTask1 *HookTask + HookTask2 *HookTask + HookTask3 *HookTask +} + +// prepareWebhookTestData resets the test database and inserts a standard set of +// webhooks and hook tasks used across multiple tests in this package. +func prepareWebhookTestData(t *testing.T) *webhookTestFixtures { + t.Helper() + require.NoError(t, unittest.PrepareTestDatabase()) + + hookRepo1 := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/url1", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + } + require.NoError(t, db.Insert(t.Context(), hookRepo1)) + + hookRepo1Inactive := &Webhook{ + RepoID: 1, + URL: "https://www.example.com/url2", + ContentType: ContentTypeJSON, + Events: `{"push_only":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`, + IsActive: false, + } + require.NoError(t, db.Insert(t.Context(), hookRepo1Inactive)) + + hookOwner3 := &Webhook{ + OwnerID: 3, + URL: "https://www.example.com/url3", + ContentType: ContentTypeJSON, + Events: `{"push_only":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`, + IsActive: true, + } + require.NoError(t, db.Insert(t.Context(), hookOwner3)) + + hookRepo2Branch := &Webhook{ + RepoID: 2, + URL: "https://www.example.com/url4", + ContentType: ContentTypeJSON, + Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, + IsActive: true, + } + require.NoError(t, db.Insert(t.Context(), hookRepo2Branch)) + + hookSystem := &Webhook{ + URL: "https://www.example.com/url5", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + IsSystemWebhook: true, + } + require.NoError(t, db.Insert(t.Context(), hookSystem)) + + hookDefault := &Webhook{ + URL: "https://www.example.com/url6", + ContentType: ContentTypeJSON, + Events: `{"push_only":true}`, + IsActive: true, + IsSystemWebhook: false, + } + require.NoError(t, db.Insert(t.Context(), hookDefault)) + + task1, err := CreateHookTask(t.Context(), &HookTask{HookID: hookRepo1.ID, IsDelivered: true, PayloadVersion: 2}) + require.NoError(t, err) + task2, err := CreateHookTask(t.Context(), &HookTask{HookID: hookRepo1.ID, IsDelivered: true, PayloadVersion: 2}) + require.NoError(t, err) + task3, err := CreateHookTask(t.Context(), &HookTask{HookID: hookRepo1.ID, IsDelivered: true, PayloadVersion: 2}) + require.NoError(t, err) + + return &webhookTestFixtures{ + HookRepo1: hookRepo1, + HookRepo1Inactive: hookRepo1Inactive, + HookOwner3: hookOwner3, + HookRepo2Branch: hookRepo2Branch, + HookSystem: hookSystem, + HookDefault: hookDefault, + HookTask1: task1, + HookTask2: task2, + HookTask3: task3, + } +} + func TestHookContentType_Name(t *testing.T) { assert.Equal(t, "json", ContentTypeJSON.Name()) assert.Equal(t, "form", ContentTypeForm.Name()) @@ -29,53 +123,24 @@ func TestIsValidHookContentType(t *testing.T) { } func TestWebhook_History(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - hook1 := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/history1", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - } - assert.NoError(t, CreateWebhook(t.Context(), hook1)) - task1, err := CreateHookTask(t.Context(), &HookTask{HookID: hook1.ID, IsDelivered: true, PayloadVersion: 2}) - assert.NoError(t, err) - task2, err := CreateHookTask(t.Context(), &HookTask{HookID: hook1.ID, IsDelivered: true, PayloadVersion: 2}) - assert.NoError(t, err) - task3, err := CreateHookTask(t.Context(), &HookTask{HookID: hook1.ID, IsDelivered: true, PayloadVersion: 2}) - assert.NoError(t, err) + fixture := prepareWebhookTestData(t) - tasks, err := hook1.History(t.Context(), 0) + tasks, err := fixture.HookRepo1.History(t.Context(), 0) assert.NoError(t, err) if assert.Len(t, tasks, 3) { - assert.Equal(t, task3.ID, tasks[0].ID) - assert.Equal(t, task2.ID, tasks[1].ID) - assert.Equal(t, task1.ID, tasks[2].ID) + assert.Equal(t, fixture.HookTask3.ID, tasks[0].ID) + assert.Equal(t, fixture.HookTask2.ID, tasks[1].ID) + assert.Equal(t, fixture.HookTask1.ID, tasks[2].ID) } - hook2 := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/history2", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: false, - } - assert.NoError(t, CreateWebhook(t.Context(), hook2)) - tasks, err = hook2.History(t.Context(), 0) + tasks, err = fixture.HookRepo1Inactive.History(t.Context(), 0) assert.NoError(t, err) assert.Empty(t, tasks) } func TestWebhook_UpdateEvent(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - webhook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/update_event", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), webhook)) + fixture := prepareWebhookTestData(t) + webhook := fixture.HookRepo1 hookEvent := &webhook_module.HookEvent{ PushOnly: true, SendEverything: false, @@ -128,18 +193,11 @@ func TestCreateWebhook(t *testing.T) { } func TestGetWebhookByRepoID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/by_repo_id", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + fixture := prepareWebhookTestData(t) - loaded, err := GetWebhookByRepoID(t.Context(), 1, hook.ID) + loaded, err := GetWebhookByRepoID(t.Context(), 1, fixture.HookRepo1.ID) assert.NoError(t, err) - assert.Equal(t, hook.ID, loaded.ID) + assert.Equal(t, fixture.HookRepo1.ID, loaded.ID) _, err = GetWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -147,19 +205,11 @@ func TestGetWebhookByRepoID(t *testing.T) { } func TestGetWebhookByOwnerID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - OwnerID: 3, - URL: "https://www.example.com/by_owner_id", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + fixture := prepareWebhookTestData(t) - loaded, err := GetWebhookByOwnerID(t.Context(), 3, hook.ID) + loaded, err := GetWebhookByOwnerID(t.Context(), 3, fixture.HookOwner3.ID) assert.NoError(t, err) - assert.Equal(t, hook.ID, loaded.ID) + assert.Equal(t, fixture.HookOwner3.ID, loaded.ID) _, err = GetWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -167,107 +217,52 @@ func TestGetWebhookByOwnerID(t *testing.T) { } func TestGetActiveWebhooksByRepoID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook1 := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/active1", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - } - assert.NoError(t, CreateWebhook(t.Context(), hook1)) - hook2 := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/inactive1", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: false, - } - assert.NoError(t, CreateWebhook(t.Context(), hook2)) + fixture := prepareWebhookTestData(t) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, hook1.ID, hooks[0].ID) + assert.Equal(t, fixture.HookRepo1.ID, hooks[0].ID) assert.True(t, hooks[0].IsActive) } } func TestGetWebhooksByRepoID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook1 := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/repo1_hook1", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - } - assert.NoError(t, CreateWebhook(t.Context(), hook1)) - hook2 := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/repo1_hook2", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: false, - } - assert.NoError(t, CreateWebhook(t.Context(), hook2)) + fixture := prepareWebhookTestData(t) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1}) assert.NoError(t, err) if assert.Len(t, hooks, 2) { - assert.Equal(t, hook1.ID, hooks[0].ID) - assert.Equal(t, hook2.ID, hooks[1].ID) + assert.Equal(t, fixture.HookRepo1.ID, hooks[0].ID) + assert.Equal(t, fixture.HookRepo1Inactive.ID, hooks[1].ID) } } func TestGetActiveWebhooksByOwnerID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - OwnerID: 3, - URL: "https://www.example.com/owner_active", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + fixture := prepareWebhookTestData(t) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, hook.ID, hooks[0].ID) + assert.Equal(t, fixture.HookOwner3.ID, hooks[0].ID) assert.True(t, hooks[0].IsActive) } } func TestGetWebhooksByOwnerID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - OwnerID: 3, - URL: "https://www.example.com/owner_hook", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + fixture := prepareWebhookTestData(t) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { - assert.Equal(t, hook.ID, hooks[0].ID) + assert.Equal(t, fixture.HookOwner3.ID, hooks[0].ID) assert.True(t, hooks[0].IsActive) } } func TestUpdateWebhook(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/update", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: false, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + fixture := prepareWebhookTestData(t) + hook := fixture.HookRepo1Inactive hook.IsActive = true hook.ContentType = ContentTypeForm unittest.AssertNotExistsBean(t, hook) @@ -276,17 +271,10 @@ func TestUpdateWebhook(t *testing.T) { } func TestDeleteWebhookByRepoID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 1, - URL: "https://www.example.com/delete_by_repo", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: hook.ID, RepoID: 1}) - assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, hook.ID)) - unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, RepoID: 1}) + fixture := prepareWebhookTestData(t) + unittest.AssertExistsAndLoadBean(t, &Webhook{ID: fixture.HookRepo1.ID, RepoID: 1}) + assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, fixture.HookRepo1.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: fixture.HookRepo1.ID, RepoID: 1}) err := DeleteWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -294,17 +282,10 @@ func TestDeleteWebhookByRepoID(t *testing.T) { } func TestDeleteWebhookByOwnerID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - OwnerID: 3, - URL: "https://www.example.com/delete_by_owner", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: hook.ID, OwnerID: 3}) - assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, hook.ID)) - unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, OwnerID: 3}) + fixture := prepareWebhookTestData(t) + unittest.AssertExistsAndLoadBean(t, &Webhook{ID: fixture.HookOwner3.ID, OwnerID: 3}) + assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, fixture.HookOwner3.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: fixture.HookOwner3.ID, OwnerID: 3}) err := DeleteWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -312,27 +293,14 @@ func TestDeleteWebhookByOwnerID(t *testing.T) { } func TestHookTasks(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/hook_tasks", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) - task1, err := CreateHookTask(t.Context(), &HookTask{HookID: hook.ID, IsDelivered: true, PayloadVersion: 2}) - assert.NoError(t, err) - task2, err := CreateHookTask(t.Context(), &HookTask{HookID: hook.ID, IsDelivered: true, PayloadVersion: 2}) - assert.NoError(t, err) - task3, err := CreateHookTask(t.Context(), &HookTask{HookID: hook.ID, IsDelivered: true, PayloadVersion: 2}) - assert.NoError(t, err) + fixture := prepareWebhookTestData(t) - hookTasks, err := HookTasks(t.Context(), hook.ID, 1) + hookTasks, err := HookTasks(t.Context(), fixture.HookRepo1.ID, 1) assert.NoError(t, err) if assert.Len(t, hookTasks, 3) { - assert.Equal(t, task3.ID, hookTasks[0].ID) - assert.Equal(t, task2.ID, hookTasks[1].ID) - assert.Equal(t, task1.ID, hookTasks[2].ID) + assert.Equal(t, fixture.HookTask3.ID, hookTasks[0].ID) + assert.Equal(t, fixture.HookTask2.ID, hookTasks[1].ID) + assert.Equal(t, fixture.HookTask1.ID, hookTasks[2].ID) } hookTasks, err = HookTasks(t.Context(), unittest.NonexistentID, 1) @@ -341,16 +309,9 @@ func TestHookTasks(t *testing.T) { } func TestCreateHookTask(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/create_task", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + fixture := prepareWebhookTestData(t) hookTask := &HookTask{ - HookID: hook.ID, + HookID: fixture.HookOwner3.ID, PayloadVersion: 2, } unittest.AssertNotExistsBean(t, hookTask) @@ -360,15 +321,8 @@ func TestCreateHookTask(t *testing.T) { } func TestUpdateHookTask(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/update_task", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) - hookTask := &HookTask{HookID: hook.ID, PayloadVersion: 2} + fixture := prepareWebhookTestData(t) + hookTask := &HookTask{HookID: fixture.HookOwner3.ID, PayloadVersion: 2} _, err := CreateHookTask(t.Context(), hookTask) assert.NoError(t, err) @@ -381,13 +335,8 @@ func TestUpdateHookTask(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/cleanup1", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup1", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ HookID: hook.ID, IsDelivered: true, @@ -405,13 +354,8 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/cleanup2", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup2", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ HookID: hook.ID, IsDelivered: false, @@ -428,13 +372,8 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/cleanup3", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup3", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ HookID: hook.ID, IsDelivered: true, @@ -452,13 +391,8 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/cleanup4", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup4", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ HookID: hook.ID, IsDelivered: true, @@ -476,13 +410,8 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/cleanup5", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup5", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ HookID: hook.ID, IsDelivered: false, @@ -499,13 +428,8 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hook := &Webhook{ - RepoID: 3, - URL: "https://www.example.com/cleanup6", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - } - assert.NoError(t, CreateWebhook(t.Context(), hook)) + hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup6", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ HookID: hook.ID, IsDelivered: true, diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go index b9505d9dc98a5..cca83bf5683cd 100644 --- a/routers/api/v1/repo/hook_test.go +++ b/routers/api/v1/repo/hook_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/services/contexttest" @@ -25,7 +26,7 @@ func TestTestHook(t *testing.T) { Events: `{"push_only":true}`, IsActive: true, } - assert.NoError(t, webhook.CreateWebhook(t.Context(), hook)) + assert.NoError(t, db.Insert(t.Context(), hook)) ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/wiki/_pages") ctx.SetPathParam("id", fmt.Sprintf("%d", hook.ID)) diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index 74240bb675c0c..adef62a2b05ad 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -6,6 +6,7 @@ package webhook import ( "testing" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -44,7 +45,7 @@ func TestPrepareWebhooks(t *testing.T) { Events: `{"push_only":true}`, IsActive: true, } - assert.NoError(t, webhook_model.CreateWebhook(t.Context(), hook)) + assert.NoError(t, db.Insert(t.Context(), hook)) hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} unittest.AssertNotExistsBean(t, hookTask) @@ -63,7 +64,7 @@ func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, } - assert.NoError(t, webhook_model.CreateWebhook(t.Context(), hook)) + assert.NoError(t, db.Insert(t.Context(), hook)) hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} unittest.AssertNotExistsBean(t, hookTask) @@ -83,7 +84,7 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, } - assert.NoError(t, webhook_model.CreateWebhook(t.Context(), hook)) + assert.NoError(t, db.Insert(t.Context(), hook)) hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} unittest.AssertNotExistsBean(t, hookTask) From d3341cfb078c8be2f85b370499baeaf9c2896961 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 04:40:21 +0000 Subject: [PATCH 03/33] models/webhook: address review feedback - add RepoID to hookOwner3, use HookRepo1Inactive in delete test Agent-Logs-Url: https://github.com/go-gitea/gitea/sessions/d187e3a5-2c68-4115-a4d1-d2bda8f54004 Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com> --- models/webhook/webhook_test.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 732e164e9a349..a5c6c87b3f0e5 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -57,6 +57,7 @@ func prepareWebhookTestData(t *testing.T) *webhookTestFixtures { hookOwner3 := &Webhook{ OwnerID: 3, + RepoID: 3, URL: "https://www.example.com/url3", ContentType: ContentTypeJSON, Events: `{"push_only":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`, @@ -272,9 +273,9 @@ func TestUpdateWebhook(t *testing.T) { func TestDeleteWebhookByRepoID(t *testing.T) { fixture := prepareWebhookTestData(t) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: fixture.HookRepo1.ID, RepoID: 1}) - assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, fixture.HookRepo1.ID)) - unittest.AssertNotExistsBean(t, &Webhook{ID: fixture.HookRepo1.ID, RepoID: 1}) + unittest.AssertExistsAndLoadBean(t, &Webhook{ID: fixture.HookRepo1Inactive.ID, RepoID: 1}) + assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, fixture.HookRepo1Inactive.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: fixture.HookRepo1Inactive.ID, RepoID: 1}) err := DeleteWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) From 502c76e0f0079e458abaa68fe51755790f628b89 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 13:48:19 +0800 Subject: [PATCH 04/33] fix --- models/fixtures/hook_task.yml | 39 +--- models/webhook/main_test.go | 1 + models/webhook/webhook_system_test.go | 29 ++- models/webhook/webhook_test.go | 254 ++++++++++---------------- services/webhook/webhook_test.go | 45 +++-- 5 files changed, 140 insertions(+), 228 deletions(-) diff --git a/models/fixtures/hook_task.yml b/models/fixtures/hook_task.yml index e19eeb0368743..01918b35eeb2e 100644 --- a/models/fixtures/hook_task.yml +++ b/models/fixtures/hook_task.yml @@ -1,39 +1,2 @@ -- - id: 1 - hook_id: 1 - uuid: uuid1 - is_delivered: true - is_succeed: false - request_content: > - { - "url": "/matrix-delivered", - "http_method":"PUT", - "headers": { - "X-Head": "42" - }, - "body": "{}" - } - -- - id: 2 - hook_id: 1 - uuid: uuid2 - is_delivered: true - -- - id: 3 - hook_id: 1 - uuid: uuid3 - is_delivered: true - is_succeed: true - payload_content: '{"key":"value"}' # legacy task, payload saved in payload_content (and not in request_content) - request_content: > - { - "url": "/matrix-success", - "http_method":"PUT", - "headers": { - "X-Head": "42" - } - } - +[] # DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly diff --git a/models/webhook/main_test.go b/models/webhook/main_test.go index f19465d50530e..5f2d5081a1a33 100644 --- a/models/webhook/main_test.go +++ b/models/webhook/main_test.go @@ -15,5 +15,6 @@ func TestMain(m *testing.M) { "webhook.yml", "hook_task.yml", }, + SetUp: prepareWebhookTestData, }) } diff --git a/models/webhook/webhook_system_test.go b/models/webhook/webhook_system_test.go index 32bd4878cbfd8..9e954d1e37756 100644 --- a/models/webhook/webhook_system_test.go +++ b/models/webhook/webhook_system_test.go @@ -6,33 +6,32 @@ package webhook import ( "testing" + "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestListSystemWebhookOptions(t *testing.T) { - fixture := prepareWebhookTestData(t) - + hookSystem := unittest.AssertExistsAndLoadBean(t, &Webhook{URL: "https://www.example.com/system"}) + hookDefault := unittest.AssertExistsAndLoadBean(t, &Webhook{URL: "https://www.example.com/default"}) opts := ListSystemWebhookOptions{IsSystem: optional.None[bool]()} hooks, _, err := GetGlobalWebhooks(t.Context(), &opts) - assert.NoError(t, err) - if assert.Len(t, hooks, 2) { - assert.Equal(t, fixture.HookSystem.ID, hooks[0].ID) - assert.Equal(t, fixture.HookDefault.ID, hooks[1].ID) - } + require.NoError(t, err) + require.Len(t, hooks, 2) + assert.Equal(t, hookSystem.ID, hooks[0].ID) + assert.Equal(t, hookDefault.ID, hooks[1].ID) opts.IsSystem = optional.Some(true) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) - assert.NoError(t, err) - if assert.Len(t, hooks, 1) { - assert.Equal(t, fixture.HookSystem.ID, hooks[0].ID) - } + require.NoError(t, err) + require.Len(t, hooks, 1) + assert.Equal(t, hookSystem.ID, hooks[0].ID) opts.IsSystem = optional.Some(false) hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) - assert.NoError(t, err) - if assert.Len(t, hooks, 1) { - assert.Equal(t, fixture.HookDefault.ID, hooks[0].ID) - } + require.NoError(t, err) + require.Len(t, hooks, 1) + assert.Equal(t, hookDefault.ID, hooks[0].ID) } diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index a5c6c87b3f0e5..073af91de217f 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -4,6 +4,7 @@ package webhook import ( + "context" "testing" "time" @@ -14,134 +15,105 @@ import ( "code.gitea.io/gitea/modules/timeutil" webhook_module "code.gitea.io/gitea/modules/webhook" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "xorm.io/builder" ) -// webhookTestFixtures holds standard webhook test data created by prepareWebhookTestData. -type webhookTestFixtures struct { - HookRepo1 *Webhook - HookRepo1Inactive *Webhook - HookOwner3 *Webhook - HookRepo2Branch *Webhook - HookSystem *Webhook - HookDefault *Webhook - HookTask1 *HookTask - HookTask2 *HookTask - HookTask3 *HookTask -} - -// prepareWebhookTestData resets the test database and inserts a standard set of -// webhooks and hook tasks used across multiple tests in this package. -func prepareWebhookTestData(t *testing.T) *webhookTestFixtures { - t.Helper() - require.NoError(t, unittest.PrepareTestDatabase()) - - hookRepo1 := &Webhook{ +func prepareWebhookTestData() error { + if err := unittest.PrepareTestDatabase(); err != nil { + return err + } + var hooks []*Webhook + hooks = append(hooks, &Webhook{ RepoID: 1, URL: "https://www.example.com/url1", ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, + Events: `{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}`, IsActive: true, - } - require.NoError(t, db.Insert(t.Context(), hookRepo1)) - - hookRepo1Inactive := &Webhook{ + }) + hooks = append(hooks, &Webhook{ RepoID: 1, URL: "https://www.example.com/url2", ContentType: ContentTypeJSON, - Events: `{"push_only":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`, + Events: `{}`, IsActive: false, - } - require.NoError(t, db.Insert(t.Context(), hookRepo1Inactive)) - - hookOwner3 := &Webhook{ + }) + hooks = append(hooks, &Webhook{ OwnerID: 3, RepoID: 3, URL: "https://www.example.com/url3", ContentType: ContentTypeJSON, - Events: `{"push_only":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`, + Events: `{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}`, IsActive: true, - } - require.NoError(t, db.Insert(t.Context(), hookOwner3)) - - hookRepo2Branch := &Webhook{ + }) + hooks = append(hooks, &Webhook{ + OwnerID: 3, + RepoID: 3, + URL: "https://www.example.com/url3", + ContentType: ContentTypeJSON, + Events: `{}`, + }) + hooks = append(hooks, &Webhook{ RepoID: 2, URL: "https://www.example.com/url4", ContentType: ContentTypeJSON, Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, - } - require.NoError(t, db.Insert(t.Context(), hookRepo2Branch)) - - hookSystem := &Webhook{ - URL: "https://www.example.com/url5", + }) + hooks = append(hooks, &Webhook{ + URL: "https://www.example.com/system", ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, + Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsSystemWebhook: true, + }) + hooks = append(hooks, &Webhook{ + URL: "https://www.example.com/default", + ContentType: ContentTypeJSON, + Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, + }) + ctx := context.Background() + if err := db.TruncateBeans(ctx, &Webhook{}); err != nil { + return err } - require.NoError(t, db.Insert(t.Context(), hookSystem)) - - hookDefault := &Webhook{ - URL: "https://www.example.com/url6", - ContentType: ContentTypeJSON, - Events: `{"push_only":true}`, - IsActive: true, - IsSystemWebhook: false, + if err := db.Insert(ctx, hooks); err != nil { + return err } - require.NoError(t, db.Insert(t.Context(), hookDefault)) - task1, err := CreateHookTask(t.Context(), &HookTask{HookID: hookRepo1.ID, IsDelivered: true, PayloadVersion: 2}) - require.NoError(t, err) - task2, err := CreateHookTask(t.Context(), &HookTask{HookID: hookRepo1.ID, IsDelivered: true, PayloadVersion: 2}) - require.NoError(t, err) - task3, err := CreateHookTask(t.Context(), &HookTask{HookID: hookRepo1.ID, IsDelivered: true, PayloadVersion: 2}) - require.NoError(t, err) - - return &webhookTestFixtures{ - HookRepo1: hookRepo1, - HookRepo1Inactive: hookRepo1Inactive, - HookOwner3: hookOwner3, - HookRepo2Branch: hookRepo2Branch, - HookSystem: hookSystem, - HookDefault: hookDefault, - HookTask1: task1, - HookTask2: task2, - HookTask3: task3, + hook, _, _ := db.Get[Webhook](ctx, builder.Eq{"repo_id": 1, "is_active": true}) + var tasks []*HookTask + tasks = append(tasks, &HookTask{HookID: hook.ID, UUID: uuid.New().String()}) + tasks = append(tasks, &HookTask{HookID: hook.ID, UUID: uuid.New().String()}) + tasks = append(tasks, &HookTask{HookID: hook.ID, UUID: uuid.New().String()}) + if err := db.TruncateBeans(ctx, &HookTask{}); err != nil { + return err } + return db.Insert(ctx, tasks) } -func TestHookContentType_Name(t *testing.T) { +func TestWebHookContentType(t *testing.T) { assert.Equal(t, "json", ContentTypeJSON.Name()) assert.Equal(t, "form", ContentTypeForm.Name()) -} - -func TestIsValidHookContentType(t *testing.T) { assert.True(t, IsValidHookContentType("json")) assert.True(t, IsValidHookContentType("form")) assert.False(t, IsValidHookContentType("invalid")) } func TestWebhook_History(t *testing.T) { - fixture := prepareWebhookTestData(t) - - tasks, err := fixture.HookRepo1.History(t.Context(), 0) - assert.NoError(t, err) - if assert.Len(t, tasks, 3) { - assert.Equal(t, fixture.HookTask3.ID, tasks[0].ID) - assert.Equal(t, fixture.HookTask2.ID, tasks[1].ID) - assert.Equal(t, fixture.HookTask1.ID, tasks[2].ID) - } + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true}) + tasks, err := hook.History(t.Context(), 0) + require.NoError(t, err) + require.Len(t, tasks, 3) - tasks, err = fixture.HookRepo1Inactive.History(t.Context(), 0) + hook = unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, Events: "{}"}) + tasks, err = hook.History(t.Context(), 0) assert.NoError(t, err) assert.Empty(t, tasks) } func TestWebhook_UpdateEvent(t *testing.T) { - fixture := prepareWebhookTestData(t) - webhook := fixture.HookRepo1 + webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true}) hookEvent := &webhook_module.HookEvent{ PushOnly: true, SendEverything: false, @@ -194,11 +166,10 @@ func TestCreateWebhook(t *testing.T) { } func TestGetWebhookByRepoID(t *testing.T) { - fixture := prepareWebhookTestData(t) - - loaded, err := GetWebhookByRepoID(t.Context(), 1, fixture.HookRepo1.ID) + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true}) + loaded, err := GetWebhookByRepoID(t.Context(), 1, hook.ID) assert.NoError(t, err) - assert.Equal(t, fixture.HookRepo1.ID, loaded.ID) + assert.Equal(t, hook.ID, loaded.ID) _, err = GetWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -206,11 +177,10 @@ func TestGetWebhookByRepoID(t *testing.T) { } func TestGetWebhookByOwnerID(t *testing.T) { - fixture := prepareWebhookTestData(t) - - loaded, err := GetWebhookByOwnerID(t.Context(), 3, fixture.HookOwner3.ID) - assert.NoError(t, err) - assert.Equal(t, fixture.HookOwner3.ID, loaded.ID) + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3}) + loaded, err := GetWebhookByOwnerID(t.Context(), 3, hook.ID) + require.NoError(t, err) + require.Equal(t, hook.ID, loaded.ID) _, err = GetWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) assert.Error(t, err) @@ -218,52 +188,45 @@ func TestGetWebhookByOwnerID(t *testing.T) { } func TestGetActiveWebhooksByRepoID(t *testing.T) { - fixture := prepareWebhookTestData(t) - + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true}) hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)}) - assert.NoError(t, err) - if assert.Len(t, hooks, 1) { - assert.Equal(t, fixture.HookRepo1.ID, hooks[0].ID) - assert.True(t, hooks[0].IsActive) - } + require.NoError(t, err) + require.Len(t, hooks, 1) + assert.Equal(t, hook.ID, hooks[0].ID) + assert.True(t, hooks[0].IsActive) } func TestGetWebhooksByRepoID(t *testing.T) { - fixture := prepareWebhookTestData(t) - hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{RepoID: 1}) - assert.NoError(t, err) - if assert.Len(t, hooks, 2) { - assert.Equal(t, fixture.HookRepo1.ID, hooks[0].ID) - assert.Equal(t, fixture.HookRepo1Inactive.ID, hooks[1].ID) - } + require.NoError(t, err) + require.Len(t, hooks, 2) + assert.Equal(t, int64(1), hooks[0].RepoID) + assert.True(t, hooks[0].IsActive) + assert.Equal(t, int64(1), hooks[1].RepoID) + assert.False(t, hooks[1].IsActive) } func TestGetActiveWebhooksByOwnerID(t *testing.T) { - fixture := prepareWebhookTestData(t) - hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)}) - assert.NoError(t, err) - if assert.Len(t, hooks, 1) { - assert.Equal(t, fixture.HookOwner3.ID, hooks[0].ID) - assert.True(t, hooks[0].IsActive) - } + require.NoError(t, err) + require.Len(t, hooks, 1) + assert.Equal(t, int64(3), hooks[0].OwnerID) + assert.True(t, hooks[0].IsActive) } func TestGetWebhooksByOwnerID(t *testing.T) { - fixture := prepareWebhookTestData(t) - hooks, err := db.Find[Webhook](t.Context(), ListWebhookOptions{OwnerID: 3}) - assert.NoError(t, err) - if assert.Len(t, hooks, 1) { - assert.Equal(t, fixture.HookOwner3.ID, hooks[0].ID) - assert.True(t, hooks[0].IsActive) - } + require.NoError(t, err) + require.Len(t, hooks, 2) + assert.Equal(t, int64(3), hooks[0].OwnerID) + assert.True(t, hooks[0].IsActive) + assert.Equal(t, int64(3), hooks[1].OwnerID) + assert.False(t, hooks[1].IsActive) } func TestUpdateWebhook(t *testing.T) { - fixture := prepareWebhookTestData(t) - hook := fixture.HookRepo1Inactive + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, Events: `{}`}) + require.False(t, hook.IsActive) hook.IsActive = true hook.ContentType = ContentTypeForm unittest.AssertNotExistsBean(t, hook) @@ -272,49 +235,36 @@ func TestUpdateWebhook(t *testing.T) { } func TestDeleteWebhookByRepoID(t *testing.T) { - fixture := prepareWebhookTestData(t) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: fixture.HookRepo1Inactive.ID, RepoID: 1}) - assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, fixture.HookRepo1Inactive.ID)) - unittest.AssertNotExistsBean(t, &Webhook{ID: fixture.HookRepo1Inactive.ID, RepoID: 1}) + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, Events: `{}`}) + assert.NoError(t, DeleteWebhookByRepoID(t.Context(), 1, hook.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, RepoID: 1}) err := DeleteWebhookByRepoID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) - assert.Error(t, err) assert.True(t, IsErrWebhookNotExist(err)) } func TestDeleteWebhookByOwnerID(t *testing.T) { - fixture := prepareWebhookTestData(t) - unittest.AssertExistsAndLoadBean(t, &Webhook{ID: fixture.HookOwner3.ID, OwnerID: 3}) - assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, fixture.HookOwner3.ID)) - unittest.AssertNotExistsBean(t, &Webhook{ID: fixture.HookOwner3.ID, OwnerID: 3}) + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3, Events: `{}`}) + assert.NoError(t, DeleteWebhookByOwnerID(t.Context(), 3, hook.ID)) + unittest.AssertNotExistsBean(t, &Webhook{ID: hook.ID, OwnerID: 3}) err := DeleteWebhookByOwnerID(t.Context(), unittest.NonexistentID, unittest.NonexistentID) - assert.Error(t, err) assert.True(t, IsErrWebhookNotExist(err)) } func TestHookTasks(t *testing.T) { - fixture := prepareWebhookTestData(t) - - hookTasks, err := HookTasks(t.Context(), fixture.HookRepo1.ID, 1) + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{RepoID: 1, IsActive: true}) + hookTasks, err := HookTasks(t.Context(), hook.ID, 1) assert.NoError(t, err) - if assert.Len(t, hookTasks, 3) { - assert.Equal(t, fixture.HookTask3.ID, hookTasks[0].ID) - assert.Equal(t, fixture.HookTask2.ID, hookTasks[1].ID) - assert.Equal(t, fixture.HookTask1.ID, hookTasks[2].ID) - } - + assert.Len(t, hookTasks, 3) hookTasks, err = HookTasks(t.Context(), unittest.NonexistentID, 1) assert.NoError(t, err) assert.Empty(t, hookTasks) } func TestCreateHookTask(t *testing.T) { - fixture := prepareWebhookTestData(t) - hookTask := &HookTask{ - HookID: fixture.HookOwner3.ID, - PayloadVersion: 2, - } + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3, IsActive: true}) + hookTask := &HookTask{HookID: hook.ID, PayloadVersion: 2} unittest.AssertNotExistsBean(t, hookTask) _, err := CreateHookTask(t.Context(), hookTask) assert.NoError(t, err) @@ -322,8 +272,8 @@ func TestCreateHookTask(t *testing.T) { } func TestUpdateHookTask(t *testing.T) { - fixture := prepareWebhookTestData(t) - hookTask := &HookTask{HookID: fixture.HookOwner3.ID, PayloadVersion: 2} + hook := unittest.AssertExistsAndLoadBean(t, &Webhook{OwnerID: 3, IsActive: true}) + hookTask := &HookTask{HookID: hook.ID, PayloadVersion: 2} _, err := CreateHookTask(t.Context(), hookTask) assert.NoError(t, err) @@ -335,7 +285,6 @@ func TestUpdateHookTask(t *testing.T) { } func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup1", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ @@ -354,7 +303,6 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { } func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup2", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ @@ -372,7 +320,6 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { } func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup3", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ @@ -391,7 +338,6 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { } func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup4", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ @@ -410,7 +356,6 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { } func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup5", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ @@ -428,7 +373,6 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { } func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) hook := &Webhook{RepoID: 3, URL: "https://www.example.com/cleanup6", ContentType: ContentTypeJSON, Events: `{"push_only":true}`} require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &HookTask{ diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index adef62a2b05ad..63ef698399054 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -22,7 +22,17 @@ import ( "github.com/stretchr/testify/require" ) -func TestWebhook_GetSlackHook(t *testing.T) { +func TestWebhookService(t *testing.T) { + unittest.PrepareTestEnv(t) + t.Run("GetSlackHook", testWebhookGetSlackHook) + t.Run("PrepareWebhooks", testWebhookPrepare) + t.Run("PrepareBranchFilterMatch", testWebhookPrepareBranchFilterMatch) + t.Run("PrepareBranchFilterNoMatch", testWebhookPrepareBranchFilterNoMatch) + t.Run("WebhookUserMail", testWebhookUserMail) + t.Run("CheckBranchFilter", testWebhookCheckBranchFilter) +} + +func testWebhookGetSlackHook(t *testing.T) { w := &webhook_model.Webhook{ Meta: `{"channel": "foo", "username": "username", "color": "blue"}`, } @@ -34,9 +44,7 @@ func TestWebhook_GetSlackHook(t *testing.T) { }, *slackHook) } -func TestPrepareWebhooks(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - +func testWebhookPrepare(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) hook := &webhook_model.Webhook{ RepoID: repo.ID, @@ -45,17 +53,16 @@ func TestPrepareWebhooks(t *testing.T) { Events: `{"push_only":true}`, IsActive: true, } - assert.NoError(t, db.Insert(t.Context(), hook)) + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} unittest.AssertNotExistsBean(t, hookTask) - assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}})) + err := PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Commits: []*api.PayloadCommit{{}}}) + require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) } -func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - +func testWebhookPrepareBranchFilterMatch(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) hook := &webhook_model.Webhook{ RepoID: repo.ID, @@ -64,18 +71,17 @@ func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, } - assert.NoError(t, db.Insert(t.Context(), hook)) + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} unittest.AssertNotExistsBean(t, hookTask) // this test also ensures that * doesn't handle / in any special way (like shell would) - assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}})) + err := PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/feature/7791", Commits: []*api.PayloadCommit{{}}}) + require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) } -func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - +func testWebhookPrepareBranchFilterNoMatch(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) hook := &webhook_model.Webhook{ RepoID: repo.ID, @@ -84,24 +90,23 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) { Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, } - assert.NoError(t, db.Insert(t.Context(), hook)) + require.NoError(t, db.Insert(t.Context(), hook)) hookTask := &webhook_model.HookTask{HookID: hook.ID, EventType: webhook_module.HookEventPush} unittest.AssertNotExistsBean(t, hookTask) - assert.NoError(t, PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"})) + err := PrepareWebhooks(t.Context(), EventSource{Repository: repo}, webhook_module.HookEventPush, &api.PushPayload{Ref: "refs/heads/fix_weird_bug"}) + require.NoError(t, err) unittest.AssertNotExistsBean(t, hookTask) } -func TestWebhookUserMail(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) +func testWebhookUserMail(t *testing.T) { defer test.MockVariableValue(&setting.Service.NoReplyAddress, "no-reply.com")() - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(t.Context(), user, nil).Email) assert.Equal(t, user.Email, convert.ToUser(t.Context(), user, user).Email) } -func TestCheckBranchFilter(t *testing.T) { +func testWebhookCheckBranchFilter(t *testing.T) { cases := []struct { filter string ref git.RefName From e07cf5b0c9bd2a8f706f497d8e5ab39eb24071fa Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 13:56:20 +0800 Subject: [PATCH 05/33] fix lint --- routers/api/v1/repo/hook_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/repo/hook_test.go b/routers/api/v1/repo/hook_test.go index cca83bf5683cd..6b2c7627d09ae 100644 --- a/routers/api/v1/repo/hook_test.go +++ b/routers/api/v1/repo/hook_test.go @@ -4,8 +4,8 @@ package repo import ( - "fmt" "net/http" + "strconv" "testing" "code.gitea.io/gitea/models/db" @@ -29,7 +29,7 @@ func TestTestHook(t *testing.T) { assert.NoError(t, db.Insert(t.Context(), hook)) ctx, _ := contexttest.MockAPIContext(t, "user2/repo1/wiki/_pages") - ctx.SetPathParam("id", fmt.Sprintf("%d", hook.ID)) + ctx.SetPathParam("id", strconv.FormatInt(hook.ID, 10)) contexttest.LoadRepo(t, ctx, 1) contexttest.LoadRepoCommit(t, ctx) contexttest.LoadUser(t, ctx, 2) From 9147c35950663c91e0be491ec39724ab5f8a22c7 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 13:58:26 +0800 Subject: [PATCH 06/33] fine tune --- models/fixtures/webhook.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml index 830bb411ae182..01918b35eeb2e 100644 --- a/models/fixtures/webhook.yml +++ b/models/fixtures/webhook.yml @@ -1,3 +1,2 @@ [] - # DO NOT add more test data in the fixtures, test case should prepare their own test data separately and clearly From a6bfda011a63104d8532a36897e8dfcd29eb354a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 14:36:55 +0800 Subject: [PATCH 07/33] fix test --- tests/integration/pull_merge_test.go | 61 ++++++++++++---------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index bf237a4fc6fc9..62ff20ea60195 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -17,6 +17,7 @@ import ( "time" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" pull_model "code.gitea.io/gitea/models/pull" @@ -92,12 +93,24 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str return resp } +func preparePullMergeWebhook(t *testing.T, repoID int64) { + require.NoError(t, db.TruncateBeans(t.Context(), &webhook.Webhook{}, &webhook.HookTask{})) + require.NoError(t, db.Insert(t.Context(), &webhook.Webhook{ + RepoID: 1, + URL: "https://www.example.com/url1", + ContentType: webhook.ContentTypeJSON, + Events: `{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}`, + IsActive: true, + })) +} + +func assertPullMergeWebhookTask(t *testing.T) { + unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{HookID: 1}) +} + func TestPullMerge(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number - assert.NoError(t, err) - hookTasksLenBefore := len(hookTasks) - + preparePullMergeWebhook(t, 1) session := loginUser(t, "user1") // FIXME: don't use admin user for testing testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") @@ -123,18 +136,13 @@ func TestPullMerge(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) - assert.NoError(t, err) - assert.Len(t, hookTasks, hookTasksLenBefore+1) + assertPullMergeWebhookTask(t) }) } func TestPullRebase(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number - assert.NoError(t, err) - hookTasksLenBefore := len(hookTasks) - + preparePullMergeWebhook(t, 1) session := loginUser(t, "user1") // FIXME: don't use admin user for testing testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") @@ -160,18 +168,13 @@ func TestPullRebase(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) - assert.NoError(t, err) - assert.Len(t, hookTasks, hookTasksLenBefore+1) + assertPullMergeWebhookTask(t) }) } func TestPullRebaseMerge(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number - assert.NoError(t, err) - hookTasksLenBefore := len(hookTasks) - + preparePullMergeWebhook(t, 1) session := loginUser(t, "user1") // FIXME: don't use admin user for testing testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") @@ -197,18 +200,13 @@ func TestPullRebaseMerge(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) - assert.NoError(t, err) - assert.Len(t, hookTasks, hookTasksLenBefore+1) + assertPullMergeWebhookTask(t) }) } func TestPullSquash(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number - assert.NoError(t, err) - hookTasksLenBefore := len(hookTasks) - + preparePullMergeWebhook(t, 1) session := loginUser(t, "user1") // FIXME: don't use admin user for testing testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") @@ -223,18 +221,13 @@ func TestPullSquash(t *testing.T) { DeleteBranch: false, }) - hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) - assert.NoError(t, err) - assert.Len(t, hookTasks, hookTasksLenBefore+1) + assertPullMergeWebhookTask(t) }) } func TestPullSquashWithHeadCommitID(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(t.Context(), 1, 1) // Retrieve previous hook number - assert.NoError(t, err) - hookTasksLenBefore := len(hookTasks) - + preparePullMergeWebhook(t, 1) session := loginUser(t, "user1") // FIXME: don't use admin user for testing testRepoFork(t, session, "user2", "repo1", "user1", "repo1", "") testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") @@ -267,9 +260,7 @@ func TestPullSquashWithHeadCommitID(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - hookTasks, err = webhook.HookTasks(t.Context(), 1, 1) - assert.NoError(t, err) - assert.Len(t, hookTasks, hookTasksLenBefore+1) + assertPullMergeWebhookTask(t) }) } From 9553faeb7f93b5f193da70ecc5126990d8700d07 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 14:39:30 +0800 Subject: [PATCH 08/33] clean up --- tests/integration/pull_merge_test.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 62ff20ea60195..6cadcb5922001 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -294,18 +294,14 @@ func TestPullCleanUpAfterMerge(t *testing.T) { // Check PR branch deletion resp = testPullCleanUp(t, session, elem[1], elem[2], elem[4]) - respJSON := struct { - Redirect string - }{} - DecodeJSON(t, resp, &respJSON) + respJSON := test.ParseJSONRedirect(resp.Body.Bytes()) + require.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") - assert.NotEmpty(t, respJSON.Redirect, "Redirected URL is not found") - - elem = strings.Split(respJSON.Redirect, "/") + elem = strings.Split(*respJSON.Redirect, "/") assert.Equal(t, "pulls", elem[3]) // Check branch deletion result - req := NewRequest(t, "GET", respJSON.Redirect) + req := NewRequest(t, "GET", *respJSON.Redirect) resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) @@ -758,8 +754,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) { // search issues searchIssuesResp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) - var apiIssuesBefore []*api.Issue - DecodeJSON(t, searchIssuesResp, &apiIssuesBefore) + apiIssuesBefore := DecodeJSON(t, searchIssuesResp, []*api.Issue{}) assert.Empty(t, apiIssuesBefore) // merge the pull request @@ -781,11 +776,9 @@ func TestPullMergeIndexerNotifier(t *testing.T) { // search issues again searchIssuesResp = session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) - var apiIssuesAfter []*api.Issue - DecodeJSON(t, searchIssuesResp, &apiIssuesAfter) - if assert.Len(t, apiIssuesAfter, 1) { - assert.Equal(t, issue.ID, apiIssuesAfter[0].ID) - } + apiIssuesAfter := DecodeJSON(t, searchIssuesResp, []*api.Issue{}) + require.Len(t, apiIssuesAfter, 1) + assert.Equal(t, issue.ID, apiIssuesAfter[0].ID) }) } From 17cca5ca21012e2171e0cd3b3145aa2e4b9f0587 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 14:51:28 +0800 Subject: [PATCH 09/33] fix --- services/webhook/deliver.go | 5 +---- tests/integration/pull_merge_test.go | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 58fba9f68df00..105d834b826da 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -36,10 +36,7 @@ import ( func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) { switch w.HTTPMethod { - case "": - log.Info("HTTP Method for %s webhook %s [ID: %d] is not set, defaulting to POST", w.Type, w.URL, w.ID) - fallthrough - case http.MethodPost: + case "", http.MethodPost: switch w.ContentType { case webhook_model.ContentTypeJSON: req, err = http.NewRequest(http.MethodPost, w.URL, strings.NewReader(t.PayloadContent)) diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 6cadcb5922001..3d24c4c32644c 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -96,16 +96,17 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str func preparePullMergeWebhook(t *testing.T, repoID int64) { require.NoError(t, db.TruncateBeans(t.Context(), &webhook.Webhook{}, &webhook.HookTask{})) require.NoError(t, db.Insert(t.Context(), &webhook.Webhook{ - RepoID: 1, - URL: "https://www.example.com/url1", + RepoID: repoID, + URL: "http://localhost/gitea-test-webhook-pull-merge", ContentType: webhook.ContentTypeJSON, Events: `{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}`, IsActive: true, })) } -func assertPullMergeWebhookTask(t *testing.T) { - unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{HookID: 1}) +func assertPullMergeWebhookTask(t *testing.T, repoID int64) { + hook := unittest.AssertExistsAndLoadBean(t, &webhook.Webhook{RepoID: repoID}) + unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{HookID: hook.ID}) } func TestPullMerge(t *testing.T) { @@ -136,7 +137,7 @@ func TestPullMerge(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - assertPullMergeWebhookTask(t) + assertPullMergeWebhookTask(t, 1) }) } @@ -168,7 +169,7 @@ func TestPullRebase(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - assertPullMergeWebhookTask(t) + assertPullMergeWebhookTask(t, 1) }) } @@ -200,7 +201,7 @@ func TestPullRebaseMerge(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - assertPullMergeWebhookTask(t) + assertPullMergeWebhookTask(t, 1) }) } @@ -221,7 +222,7 @@ func TestPullSquash(t *testing.T) { DeleteBranch: false, }) - assertPullMergeWebhookTask(t) + assertPullMergeWebhookTask(t, 1) }) } @@ -260,7 +261,7 @@ func TestPullSquashWithHeadCommitID(t *testing.T) { assert.Equal(t, 4, repo.NumPulls) assert.Equal(t, 3, repo.NumOpenPulls) - assertPullMergeWebhookTask(t) + assertPullMergeWebhookTask(t, 1) }) } From 7a1f463525cf7185daa13f9d6366dfb024d771b9 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 14:57:43 +0800 Subject: [PATCH 10/33] don't send request to example.com --- services/webhook/webhook_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index 63ef698399054..8522cce8eca28 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -48,7 +48,7 @@ func testWebhookPrepare(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) hook := &webhook_model.Webhook{ RepoID: repo.ID, - URL: "https://www.example.com/prepare_webhooks", + URL: "http://localhost/gitea-webhook-test-prepare_webhooks", ContentType: webhook_model.ContentTypeJSON, Events: `{"push_only":true}`, IsActive: true, @@ -66,7 +66,7 @@ func testWebhookPrepareBranchFilterMatch(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) hook := &webhook_model.Webhook{ RepoID: repo.ID, - URL: "https://www.example.com/branch_filter_match", + URL: "http://localhost/gitea-webhook-test-branch_filter_match", ContentType: webhook_model.ContentTypeJSON, Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, @@ -85,7 +85,7 @@ func testWebhookPrepareBranchFilterNoMatch(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) hook := &webhook_model.Webhook{ RepoID: repo.ID, - URL: "https://www.example.com/branch_filter_no_match", + URL: "http://localhost/gitea-webhook-test-branch_filter_no_match", ContentType: webhook_model.ContentTypeJSON, Events: `{"push_only":true,"branch_filter":"{master,feature*}"}`, IsActive: true, From 363daae9a9b3000e685f58b344d4513a1c28cad5 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 15:18:43 +0800 Subject: [PATCH 11/33] unify path init --- models/unittest/testdb.go | 46 +++++++++++++------------------------- modules/setting/testenv.go | 2 +- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 0c1458e2ce505..e5c91fdff3b9d 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -42,7 +42,22 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) { func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { testOpts := util.OptionalArg(testOptsArg, &TestOptions{}) + + appDataPath, appDataCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("appdata") + if err != nil { + testlogger.Panicf("TempDir: %v\n", err) + } + defer appDataCleanup() + _ = os.Setenv("GITEA_TEST_CONF_CONTENT", ` +[server] +APP_DATA_PATH = "`+appDataPath+`" +`) setting.SetupGiteaTestEnv() + if setting.RepoRootPath == "" || setting.AppDataPath == "" { + _, _ = fmt.Fprintln(os.Stderr, "SetupGiteaTestEnv failed, paths are not initialized") + os.Exit(1) + } + giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} if err := CreateTestEngine(fixturesOpts); err != nil { @@ -59,38 +74,7 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { setting.SSH.Domain = "try.gitea.io" setting.Database.Type = "sqlite3" setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" - repoRootPath, cleanup1, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("repos") - if err != nil { - testlogger.Panicf("TempDir: %v\n", err) - } - defer cleanup1() - - setting.RepoRootPath = repoRootPath - appDataPath, cleanup2, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("appdata") - if err != nil { - testlogger.Panicf("TempDir: %v\n", err) - } - defer cleanup2() - - setting.AppDataPath = appDataPath setting.GravatarSource = "https://secure.gravatar.com/avatar/" - - setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments") - - setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs") - - setting.Avatar.Storage.Path = filepath.Join(setting.AppDataPath, "avatars") - - setting.RepoAvatar.Storage.Path = filepath.Join(setting.AppDataPath, "repo-avatars") - - setting.RepoArchive.Storage.Path = filepath.Join(setting.AppDataPath, "repo-archive") - - setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages") - - setting.Actions.LogStorage.Path = filepath.Join(setting.AppDataPath, "actions_log") - - setting.Git.HomePath = filepath.Join(setting.AppDataPath, "home") - setting.IncomingEmail.ReplyToAddress = "incoming+%{token}@localhost" config.SetDynGetter(system.NewDatabaseDynKeyGetter()) diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 27bf72e860ae2..30ea1bb98abc4 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -65,7 +65,7 @@ func SetupGiteaTestEnv() { giteaConf = "custom/conf/app-test-tmp.ini" customConfBuiltin = filepath.Join(AppWorkPath, giteaConf) CustomConf = customConfBuiltin - _ = os.Remove(CustomConf) + _ = os.WriteFile(CustomConf, []byte(os.Getenv("GITEA_TEST_CONF_CONTENT")), os.ModePerm) } else { // CustomConf must be absolute path to make tests pass, CustomConf = filepath.Join(AppWorkPath, giteaConf) From 9abb18c8fac0f4927f5d8db91a84cb6b08b900cf Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 15:44:54 +0800 Subject: [PATCH 12/33] fix test --- cmd/cmd_test.go | 38 ------ cmd/cmdtest/cmd_test.go | 237 +++++++++++++++++++++++++++++++++++++ cmd/{cmd.go => helper.go} | 0 cmd/main.go | 4 +- cmd/main_test.go | 241 ++++---------------------------------- 5 files changed, 262 insertions(+), 258 deletions(-) delete mode 100644 cmd/cmd_test.go create mode 100644 cmd/cmdtest/cmd_test.go rename cmd/{cmd.go => helper.go} (100%) diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go deleted file mode 100644 index a36d05c76e07d..0000000000000 --- a/cmd/cmd_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2025 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package cmd - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/urfave/cli/v3" -) - -func TestDefaultCommand(t *testing.T) { - test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) { - called := false - cmd := &cli.Command{ - DefaultCommand: "test", - Commands: []*cli.Command{ - { - Name: "test", - Action: func(ctx context.Context, command *cli.Command) error { - retName, retValid := isValidDefaultSubCommand(command) - assert.Equal(t, expectedRetName, retName) - assert.Equal(t, expectedRetValid, retValid) - called = true - return nil - }, - }, - }, - } - assert.NoError(t, cmd.Run(t.Context(), args)) - assert.True(t, called) - } - test(t, []string{"./gitea"}, "", true) - test(t, []string{"./gitea", "test"}, "", true) - test(t, []string{"./gitea", "other"}, "other", false) -} diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go new file mode 100644 index 0000000000000..1de9390ec842a --- /dev/null +++ b/cmd/cmdtest/cmd_test.go @@ -0,0 +1,237 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +// Tests here reload the config system multiple times with uncontrollable details. +// So it must be in a separate package, to avoid affect other tests + +package cmdtest + +import ( + "context" + "errors" + "fmt" + "io" + "path/filepath" + "strings" + "testing" + + "code.gitea.io/gitea/cmd" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/util" + + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v3" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} + +func makePathOutput(workPath, customPath, customConf string) string { + return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) +} + +func newTestApp(testCmd cli.Command) *cli.Command { + app := cmd.NewMainApp(cmd.AppVersion{}) + testCmd.Name = util.IfZero(testCmd.Name, "test-cmd") + cmd.PrepareSubcommandWithGlobalFlags(&testCmd) + app.Commands = append(app.Commands, &testCmd) + app.DefaultCommand = testCmd.Name + return app +} + +type runResult struct { + Stdout string + Stderr string + ExitCode int +} + +func runTestApp(app *cli.Command, args ...string) (runResult, error) { + outBuf := new(strings.Builder) + errBuf := new(strings.Builder) + app.Writer = outBuf + app.ErrWriter = errBuf + exitCode := -1 + defer test.MockVariableValue(&cli.ErrWriter, app.ErrWriter)() + defer test.MockVariableValue(&cli.OsExiter, func(code int) { + if exitCode == -1 { + exitCode = code // save the exit code once and then reset the writer (to simulate the exit) + app.Writer, app.ErrWriter, cli.ErrWriter = io.Discard, io.Discard, io.Discard + } + })() + err := cmd.RunMainApp(app, args...) + return runResult{outBuf.String(), errBuf.String(), exitCode}, err +} + +func TestCliCmd(t *testing.T) { + defaultWorkPath := filepath.FromSlash("/tmp/mocked-work-path") + defaultCustomPath := filepath.Join(defaultWorkPath, "custom") + defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") + defer setting.MockBuiltinPaths(defaultWorkPath, "", "")() + + cli.CommandHelpTemplate = "(command help template)" + cli.RootCommandHelpTemplate = "(app help template)" + cli.SubcommandHelpTemplate = "(subcommand help template)" + + cases := []struct { + env map[string]string + cmd string + exp string + }{ + // help commands + { + cmd: "./gitea -h", + exp: "DEFAULT CONFIGURATION:", + }, + { + cmd: "./gitea help", + exp: "DEFAULT CONFIGURATION:", + }, + + { + cmd: "./gitea -c /dev/null -h", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea help -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null test-cmd -h", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -c /dev/null -h", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -h -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + { + cmd: "./gitea -c /dev/null test-cmd help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd -c /dev/null help", + exp: "ConfigFile: /dev/null", + }, + { + cmd: "./gitea test-cmd help -c /dev/null", + exp: "ConfigFile: /dev/null", + }, + + // parse paths + { + cmd: "./gitea test-cmd", + exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf), + }, + { + cmd: "./gitea -c /tmp/app.ini test-cmd", + exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"), + }, + { + cmd: "./gitea test-cmd -c /tmp/app.ini", + exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"), + }, + { + env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, + cmd: "./gitea test-cmd", + exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"), + }, + { + env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, + cmd: "./gitea test-cmd --work-path /tmp/other", + exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"), + }, + { + env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, + cmd: "./gitea test-cmd --config /tmp/app-other.ini", + exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"), + }, + } + + for _, c := range cases { + t.Run(c.cmd, func(t *testing.T) { + app := newTestApp(cli.Command{ + Action: func(ctx context.Context, cmd *cli.Command) error { + _, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) + return nil + }, + }) + for k, v := range c.env { + t.Setenv(k, v) + } + args := strings.Split(c.cmd, " ") // for test only, "split" is good enough + r, err := runTestApp(app, args...) + assert.NoError(t, err, c.cmd) + assert.NotEmpty(t, c.exp, c.cmd) + if !assert.Contains(t, r.Stdout, c.exp, c.cmd) { + t.Log("Full output:\n" + r.Stdout) + t.Log("Expected:\n" + c.exp) + } + }) + } +} + +func TestCliCmdError(t *testing.T) { + app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }}) + r, err := runTestApp(app, "./gitea", "test-cmd") + assert.Error(t, err) + assert.Equal(t, 1, r.ExitCode) + assert.Empty(t, r.Stdout) + assert.Equal(t, "Command error: normal error\n", r.Stderr) + + app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }}) + r, err = runTestApp(app, "./gitea", "test-cmd") + assert.Error(t, err) + assert.Equal(t, 2, r.ExitCode) + assert.Empty(t, r.Stdout) + assert.Equal(t, "exit error\n", r.Stderr) + + app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }}) + r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") + assert.Error(t, err) + assert.Equal(t, 1, r.ExitCode) + assert.Empty(t, r.Stdout) + assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr) + + app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }}) + r, err = runTestApp(app, "./gitea", "test-cmd") + assert.NoError(t, err) + assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called + assert.Empty(t, r.Stdout) + assert.Empty(t, r.Stderr) +} + +func TestCliCmdBefore(t *testing.T) { + ctxNew := context.WithValue(context.Background(), any("key"), "value") + configValues := map[string]string{} + setting.CustomConf = "/tmp/any.ini" + var actionCtx context.Context + app := newTestApp(cli.Command{ + Before: func(context.Context, *cli.Command) (context.Context, error) { + configValues["before"] = setting.CustomConf + return ctxNew, nil + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + configValues["action"] = setting.CustomConf + actionCtx = ctx + return nil + }, + }) + _, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd") + assert.NoError(t, err) + assert.Equal(t, ctxNew, actionCtx) + assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config") + assert.Equal(t, "/dev/null", configValues["action"]) +} diff --git a/cmd/cmd.go b/cmd/helper.go similarity index 100% rename from cmd/cmd.go rename to cmd/helper.go diff --git a/cmd/main.go b/cmd/main.go index a6b89a6fadab5..27d8cba2e9052 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -48,7 +48,7 @@ DEFAULT CONFIGURATION: } } -func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) { +func PrepareSubcommandWithGlobalFlags(originCmd *cli.Command) { originBefore := originCmd.Before originCmd.Before = func(ctxOrig context.Context, cmd *cli.Command) (ctx context.Context, err error) { ctx = ctxOrig @@ -145,7 +145,7 @@ func NewMainApp(appVer AppVersion) *cli.Command { app.Before = PrepareConsoleLoggerLevel(log.INFO) for i := range subCmdWithConfig { - prepareSubcommandWithGlobalFlags(subCmdWithConfig[i]) + PrepareSubcommandWithGlobalFlags(subCmdWithConfig[i]) } app.Commands = append(app.Commands, subCmdWithConfig...) app.Commands = append(app.Commands, subCmdStandalone...) diff --git a/cmd/main_test.go b/cmd/main_test.go index 3cd2c984e6481..a36d05c76e07d 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -1,233 +1,38 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd import ( "context" - "errors" - "fmt" - "io" - "path/filepath" - "strings" "testing" - "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" - "github.com/stretchr/testify/assert" "github.com/urfave/cli/v3" ) -func TestMain(m *testing.M) { - unittest.MainTest(m) -} - -func makePathOutput(workPath, customPath, customConf string) string { - return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) -} - -func newTestApp(testCmd cli.Command) *cli.Command { - app := NewMainApp(AppVersion{}) - testCmd.Name = util.IfZero(testCmd.Name, "test-cmd") - prepareSubcommandWithGlobalFlags(&testCmd) - app.Commands = append(app.Commands, &testCmd) - app.DefaultCommand = testCmd.Name - return app -} - -type runResult struct { - Stdout string - Stderr string - ExitCode int -} - -func runTestApp(app *cli.Command, args ...string) (runResult, error) { - outBuf := new(strings.Builder) - errBuf := new(strings.Builder) - app.Writer = outBuf - app.ErrWriter = errBuf - exitCode := -1 - defer test.MockVariableValue(&cli.ErrWriter, app.ErrWriter)() - defer test.MockVariableValue(&cli.OsExiter, func(code int) { - if exitCode == -1 { - exitCode = code // save the exit code once and then reset the writer (to simulate the exit) - app.Writer, app.ErrWriter, cli.ErrWriter = io.Discard, io.Discard, io.Discard - } - })() - err := RunMainApp(app, args...) - return runResult{outBuf.String(), errBuf.String(), exitCode}, err -} - -func TestCliCmd(t *testing.T) { - defaultWorkPath := filepath.FromSlash("/tmp/mocked-work-path") - defaultCustomPath := filepath.Join(defaultWorkPath, "custom") - defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") - defer setting.MockBuiltinPaths(defaultWorkPath, "", "")() - - cli.CommandHelpTemplate = "(command help template)" - cli.RootCommandHelpTemplate = "(app help template)" - cli.SubcommandHelpTemplate = "(subcommand help template)" - - cases := []struct { - env map[string]string - cmd string - exp string - }{ - // help commands - { - cmd: "./gitea -h", - exp: "DEFAULT CONFIGURATION:", - }, - { - cmd: "./gitea help", - exp: "DEFAULT CONFIGURATION:", - }, - - { - cmd: "./gitea -c /dev/null -h", - exp: "ConfigFile: /dev/null", - }, - - { - cmd: "./gitea -c /dev/null help", - exp: "ConfigFile: /dev/null", - }, - { - cmd: "./gitea help -c /dev/null", - exp: "ConfigFile: /dev/null", - }, - - { - cmd: "./gitea -c /dev/null test-cmd -h", - exp: "ConfigFile: /dev/null", - }, - { - cmd: "./gitea test-cmd -c /dev/null -h", - exp: "ConfigFile: /dev/null", - }, - { - cmd: "./gitea test-cmd -h -c /dev/null", - exp: "ConfigFile: /dev/null", - }, - - { - cmd: "./gitea -c /dev/null test-cmd help", - exp: "ConfigFile: /dev/null", - }, - { - cmd: "./gitea test-cmd -c /dev/null help", - exp: "ConfigFile: /dev/null", - }, - { - cmd: "./gitea test-cmd help -c /dev/null", - exp: "ConfigFile: /dev/null", - }, - - // parse paths - { - cmd: "./gitea test-cmd", - exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf), - }, - { - cmd: "./gitea -c /tmp/app.ini test-cmd", - exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"), - }, - { - cmd: "./gitea test-cmd -c /tmp/app.ini", - exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"), - }, - { - env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, - cmd: "./gitea test-cmd", - exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"), - }, - { - env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, - cmd: "./gitea test-cmd --work-path /tmp/other", - exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"), - }, - { - env: map[string]string{"GITEA_WORK_DIR": "/tmp"}, - cmd: "./gitea test-cmd --config /tmp/app-other.ini", - exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"), - }, - } - - for _, c := range cases { - t.Run(c.cmd, func(t *testing.T) { - app := newTestApp(cli.Command{ - Action: func(ctx context.Context, cmd *cli.Command) error { - _, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) - return nil +func TestDefaultCommand(t *testing.T) { + test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) { + called := false + cmd := &cli.Command{ + DefaultCommand: "test", + Commands: []*cli.Command{ + { + Name: "test", + Action: func(ctx context.Context, command *cli.Command) error { + retName, retValid := isValidDefaultSubCommand(command) + assert.Equal(t, expectedRetName, retName) + assert.Equal(t, expectedRetValid, retValid) + called = true + return nil + }, }, - }) - for k, v := range c.env { - t.Setenv(k, v) - } - args := strings.Split(c.cmd, " ") // for test only, "split" is good enough - r, err := runTestApp(app, args...) - assert.NoError(t, err, c.cmd) - assert.NotEmpty(t, c.exp, c.cmd) - if !assert.Contains(t, r.Stdout, c.exp, c.cmd) { - t.Log("Full output:\n" + r.Stdout) - t.Log("Expected:\n" + c.exp) - } - }) + }, + } + assert.NoError(t, cmd.Run(t.Context(), args)) + assert.True(t, called) } -} - -func TestCliCmdError(t *testing.T) { - app := newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }}) - r, err := runTestApp(app, "./gitea", "test-cmd") - assert.Error(t, err) - assert.Equal(t, 1, r.ExitCode) - assert.Empty(t, r.Stdout) - assert.Equal(t, "Command error: normal error\n", r.Stderr) - - app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }}) - r, err = runTestApp(app, "./gitea", "test-cmd") - assert.Error(t, err) - assert.Equal(t, 2, r.ExitCode) - assert.Empty(t, r.Stdout) - assert.Equal(t, "exit error\n", r.Stderr) - - app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }}) - r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") - assert.Error(t, err) - assert.Equal(t, 1, r.ExitCode) - assert.Empty(t, r.Stdout) - assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr) - - app = newTestApp(cli.Command{Action: func(ctx context.Context, cmd *cli.Command) error { return nil }}) - r, err = runTestApp(app, "./gitea", "test-cmd") - assert.NoError(t, err) - assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called - assert.Empty(t, r.Stdout) - assert.Empty(t, r.Stderr) -} - -func TestCliCmdBefore(t *testing.T) { - ctxNew := context.WithValue(context.Background(), any("key"), "value") - configValues := map[string]string{} - setting.CustomConf = "/tmp/any.ini" - var actionCtx context.Context - app := newTestApp(cli.Command{ - Before: func(context.Context, *cli.Command) (context.Context, error) { - configValues["before"] = setting.CustomConf - return ctxNew, nil - }, - Action: func(ctx context.Context, cmd *cli.Command) error { - configValues["action"] = setting.CustomConf - actionCtx = ctx - return nil - }, - }) - _, err := runTestApp(app, "./gitea", "--config", "/dev/null", "test-cmd") - assert.NoError(t, err) - assert.Equal(t, ctxNew, actionCtx) - assert.Equal(t, "/tmp/any.ini", configValues["before"], "BeforeFunc must be called before preparing config") - assert.Equal(t, "/dev/null", configValues["action"]) + test(t, []string{"./gitea"}, "", true) + test(t, []string{"./gitea", "test"}, "", true) + test(t, []string{"./gitea", "other"}, "other", false) } From 3359227aec5bf91392926b8f2fed40cbadd5af97 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 15:59:50 +0800 Subject: [PATCH 13/33] fix test --- cmd/main_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/main_test.go b/cmd/main_test.go index a36d05c76e07d..b2b4936d93008 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -7,10 +7,16 @@ import ( "context" "testing" + "code.gitea.io/gitea/models/unittest" + "github.com/stretchr/testify/assert" "github.com/urfave/cli/v3" ) +func TestMain(m *testing.M) { + unittest.MainTest(m) +} + func TestDefaultCommand(t *testing.T) { test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) { called := false From 5204ef97d3c5523c18707e11e65099d4e348a0b2 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 16:11:02 +0800 Subject: [PATCH 14/33] fine tune --- cmd/cmdtest/cmd_test.go | 2 +- models/unittest/testdb.go | 2 +- modules/setting/testenv.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go index 1de9390ec842a..3473013fd2747 100644 --- a/cmd/cmdtest/cmd_test.go +++ b/cmd/cmdtest/cmd_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT // Tests here reload the config system multiple times with uncontrollable details. -// So it must be in a separate package, to avoid affect other tests +// So it must be in a separate package, to avoid affecting other tests package cmdtest diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index e5c91fdff3b9d..c1286d1f34950 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -50,7 +50,7 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { defer appDataCleanup() _ = os.Setenv("GITEA_TEST_CONF_CONTENT", ` [server] -APP_DATA_PATH = "`+appDataPath+`" +APP_DATA_PATH = `+appDataPath+` `) setting.SetupGiteaTestEnv() if setting.RepoRootPath == "" || setting.AppDataPath == "" { diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 30ea1bb98abc4..c10dc06aa1f3c 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -65,7 +65,7 @@ func SetupGiteaTestEnv() { giteaConf = "custom/conf/app-test-tmp.ini" customConfBuiltin = filepath.Join(AppWorkPath, giteaConf) CustomConf = customConfBuiltin - _ = os.WriteFile(CustomConf, []byte(os.Getenv("GITEA_TEST_CONF_CONTENT")), os.ModePerm) + _ = os.WriteFile(CustomConf, []byte(os.Getenv("GITEA_TEST_CONF_CONTENT")), 0o644) } else { // CustomConf must be absolute path to make tests pass, CustomConf = filepath.Join(AppWorkPath, giteaConf) From 54d3e43dbdce6873ed2ac6314a3e3a67e629e4ba Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 16:49:51 +0800 Subject: [PATCH 15/33] fix test --- tests/integration/repo_webhook_test.go | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index 4b72962d4f54a..12a11d45bd1f1 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -10,12 +10,14 @@ import ( "net/http/httptest" "net/url" "path" + "strconv" "strings" "testing" "time" actions_model "code.gitea.io/gitea/models/actions" auth_model "code.gitea.io/gitea/models/auth" + db_model "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -40,26 +42,28 @@ import ( func TestNewWebHookLink(t *testing.T) { defer tests.PrepareTestEnv(t)() + require.NoError(t, db_model.Insert(t.Context(), &webhook.Webhook{ + RepoID: 1, + URL: "http://localhost/gitea-test-webhook-link", + ContentType: webhook.ContentTypeJSON, + Events: `{}`, + IsActive: true, + })) + hook := unittest.AssertExistsAndLoadBean(t, &webhook.Webhook{RepoID: 1}) session := loginUser(t, "user2") - - baseurl := "/user2/repo1/settings/hooks" - tests := []string{ - // webhook list page - baseurl, - // new webhook page - baseurl + "/gitea/new", - // edit webhook page - baseurl + "/1", + webhooksBaseHref := "/user2/repo1/settings/hooks" + cases := []string{ + webhooksBaseHref, + webhooksBaseHref + "/gitea/new", + webhooksBaseHref + "/" + strconv.FormatInt(hook.ID, 10), // edit webhook } - - for _, url := range tests { - resp := session.MakeRequest(t, NewRequest(t, "GET", url), http.StatusOK) + for _, reqHref := range cases { + resp := session.MakeRequest(t, NewRequest(t, "GET", reqHref), http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a") menus.Each(func(i int, menu *goquery.Selection) { - url, exist := menu.Attr("href") - assert.True(t, exist) - assert.True(t, strings.HasPrefix(url, baseurl)) + foundHref := menu.AttrOr("href", "") + assert.True(t, strings.HasPrefix(foundHref, webhooksBaseHref)) }) } } From 398b47fd8ddacff42b30dd142517b58b12f6b377 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 17:12:17 +0800 Subject: [PATCH 16/33] debug --- models/unittest/testdb.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index c1286d1f34950..aed561fc28d45 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/storage" @@ -58,6 +59,8 @@ APP_DATA_PATH = `+appDataPath+` os.Exit(1) } + _, _ = fmt.Fprintf(os.Stderr, "mainTest: %s\n%s\n", appDataPath, log.Stack(2)) + giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} if err := CreateTestEngine(fixturesOpts); err != nil { From a973269a37be7130d294eec887fb5359b5b63f82 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 17:12:17 +0800 Subject: [PATCH 17/33] debug # Conflicts: # models/unittest/testdb.go --- models/unittest/testdb.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index aed561fc28d45..1750525f46c55 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -59,7 +59,8 @@ APP_DATA_PATH = `+appDataPath+` os.Exit(1) } - _, _ = fmt.Fprintf(os.Stderr, "mainTest: %s\n%s\n", appDataPath, log.Stack(2)) + _, _ = fmt.Fprintf(os.Stderr, "mainTest: appDataPath=%s\n%s\n", appDataPath, log.Stack(2)) + log.Error("mainTest: appDataPath=%s\n%s\n", appDataPath, log.Stack(2)) giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} From 11f910c80c358eb976ac794f41e64d6e3fe3d68a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 18:01:34 +0800 Subject: [PATCH 18/33] debug --- models/migrations/base/tests.go | 2 +- models/unittest/testdb.go | 6 +----- modules/queue/manager_test.go | 5 ----- modules/setting/server.go | 3 +++ 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index 117975928ca46..b822b2d74e51c 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -204,7 +204,7 @@ func mainTest(m *testing.M) int { testlogger.Init() setting.SetupGiteaTestEnv() - tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("data") + tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-") if err != nil { testlogger.Panicf("Unable to create temporary data path %v\n", err) } diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 1750525f46c55..b179dfb6fe241 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/storage" @@ -44,7 +43,7 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) { func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { testOpts := util.OptionalArg(testOptsArg, &TestOptions{}) - appDataPath, appDataCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("appdata") + appDataPath, appDataCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-data-") if err != nil { testlogger.Panicf("TempDir: %v\n", err) } @@ -59,9 +58,6 @@ APP_DATA_PATH = `+appDataPath+` os.Exit(1) } - _, _ = fmt.Fprintf(os.Stderr, "mainTest: appDataPath=%s\n%s\n", appDataPath, log.Stack(2)) - log.Error("mainTest: appDataPath=%s\n%s\n", appDataPath, log.Stack(2)) - giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} if err := CreateTestEngine(fixturesOpts); err != nil { diff --git a/modules/queue/manager_test.go b/modules/queue/manager_test.go index fda498cc8411c..ee6ccc46ff3b9 100644 --- a/modules/queue/manager_test.go +++ b/modules/queue/manager_test.go @@ -8,16 +8,11 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" - "github.com/stretchr/testify/assert" ) func TestManager(t *testing.T) { - oldAppDataPath := setting.AppDataPath setting.AppDataPath = t.TempDir() - defer func() { - setting.AppDataPath = oldAppDataPath - }() newQueueFromConfig := func(name, cfg string) (*WorkerPoolQueue[int], error) { cfgProvider, err := setting.NewConfigProviderFromData(cfg) diff --git a/modules/setting/server.go b/modules/setting/server.go index dc58e43c435a2..56685a9165244 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -278,6 +278,9 @@ func loadServerFrom(rootCfg ConfigProvider) { StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) + + log.Error("Set AppDataPath=%s\n%s\n", AppDataPath, log.Stack(2)) + if !filepath.IsAbs(AppDataPath) { AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) } From 1591c063956ccdf727ba0963adc6df2e67458d5e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 18:13:14 +0800 Subject: [PATCH 19/33] debug --- modules/setting/server.go | 1 + modules/setting/testenv.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/modules/setting/server.go b/modules/setting/server.go index 56685a9165244..56af7a2b085e6 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -279,6 +279,7 @@ func loadServerFrom(rootCfg ConfigProvider) { StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) + // FIXME: debug log.Error("Set AppDataPath=%s\n%s\n", AppDataPath, log.Stack(2)) if !filepath.IsAbs(AppDataPath) { diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index c10dc06aa1f3c..688c43452f375 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -66,6 +66,9 @@ func SetupGiteaTestEnv() { customConfBuiltin = filepath.Join(AppWorkPath, giteaConf) CustomConf = customConfBuiltin _ = os.WriteFile(CustomConf, []byte(os.Getenv("GITEA_TEST_CONF_CONTENT")), 0o644) + + // FIXME: debug + log.Error("SetupGiteaTestEnv CustomConf=%s\n%s\n%s\n", CustomConf, os.Getenv("GITEA_TEST_CONF_CONTENT"), log.Stack(2)) } else { // CustomConf must be absolute path to make tests pass, CustomConf = filepath.Join(AppWorkPath, giteaConf) From 18795df69c2629c69bcdd9cdb421b6671e094182 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 18:25:44 +0800 Subject: [PATCH 20/33] debug --- Makefile | 2 +- modules/setting/server.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e621cc362fce4..3c0e2a3e731a6 100644 --- a/Makefile +++ b/Makefile @@ -411,7 +411,7 @@ coverage: .PHONY: unit-test-coverage unit-test-coverage: @echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 + @$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -v -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 .PHONY: tidy tidy: ## run go mod tidy diff --git a/modules/setting/server.go b/modules/setting/server.go index 56af7a2b085e6..f8f4038c0d17a 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -280,7 +280,8 @@ func loadServerFrom(rootCfg ConfigProvider) { AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) // FIXME: debug - log.Error("Set AppDataPath=%s\n%s\n", AppDataPath, log.Stack(2)) + bufCustomConf, _ := os.ReadFile(CustomConf) + log.Error("Set AppDataPath=%s (CustomConf=%s)\n%s\n%s\n", AppDataPath, CustomConf, string(bufCustomConf), log.Stack(2)) if !filepath.IsAbs(AppDataPath) { AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) From 888f4daa311665d0e7bf6e12d324280330cd2084 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 18:40:17 +0800 Subject: [PATCH 21/33] debug --- models/unittest/testdb.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index b179dfb6fe241..830968e858d12 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -47,7 +47,13 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { if err != nil { testlogger.Panicf("TempDir: %v\n", err) } - defer appDataCleanup() + + // FIXME: debug + _, _ = fmt.Fprintf(os.Stderr, "Prepare APP_DATA_PATH for SetupGiteaTestEnv: %s\n", appDataPath) + defer func() { + _, _ = fmt.Fprintf(os.Stderr, "Remove APP_DATA_PATH for SetupGiteaTestEnv: %s\n", appDataPath) + appDataCleanup() + }() _ = os.Setenv("GITEA_TEST_CONF_CONTENT", ` [server] APP_DATA_PATH = `+appDataPath+` From f310b1c3afd7240efcd781ae818b349f0c4d9ca6 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 19:16:29 +0800 Subject: [PATCH 22/33] debug --- cmd/migrate_storage_test.go | 2 ++ models/unittest/testdb.go | 29 +++++++++++++++++------------ modules/setting/server.go | 2 +- modules/setting/testenv.go | 14 ++++++++++++++ 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go index 3ea193eb1eae8..6c985146f85b9 100644 --- a/cmd/migrate_storage_test.go +++ b/cmd/migrate_storage_test.go @@ -22,6 +22,8 @@ import ( func TestMigratePackages(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + t.Log("DebugLogs", setting.DebugGetLogs()) + creator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) content := "package main\n\nfunc main() {\nfmt.Println(\"hi\")\n}\n" diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 830968e858d12..01191eb3d0f17 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/tempdir" - "code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -41,34 +40,40 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) { } func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { + reportError := func(msg string, a ...any) int { + _, _ = fmt.Fprintf(os.Stderr, msg+"\n", a...) + return 1 + } + testOpts := util.OptionalArg(testOptsArg, &TestOptions{}) appDataPath, appDataCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-data-") if err != nil { - testlogger.Panicf("TempDir: %v\n", err) + return reportError("Failed to create temp dir for app data path: %v", err) } // FIXME: debug _, _ = fmt.Fprintf(os.Stderr, "Prepare APP_DATA_PATH for SetupGiteaTestEnv: %s\n", appDataPath) + setting.DebugAppendLog("Prepare APP_DATA_PATH for SetupGiteaTestEnv: %s", appDataPath) defer func() { _, _ = fmt.Fprintf(os.Stderr, "Remove APP_DATA_PATH for SetupGiteaTestEnv: %s\n", appDataPath) + setting.DebugAppendLog("Remove APP_DATA_PATH for SetupGiteaTestEnv: %s", appDataPath) appDataCleanup() }() + _ = os.Setenv("GITEA_TEST_CONF_CONTENT", ` [server] APP_DATA_PATH = `+appDataPath+` `) setting.SetupGiteaTestEnv() if setting.RepoRootPath == "" || setting.AppDataPath == "" { - _, _ = fmt.Fprintln(os.Stderr, "SetupGiteaTestEnv failed, paths are not initialized") - os.Exit(1) + return reportError("SetupGiteaTestEnv failed, paths are not initialized") } giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} if err := CreateTestEngine(fixturesOpts); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "Error creating test database engine: %v\n", err) - os.Exit(1) + return reportError("Error creating test database engine: %v", err) } setting.AppURL = "https://try.gitea.io/" @@ -86,22 +91,22 @@ APP_DATA_PATH = `+appDataPath+` config.SetDynGetter(system.NewDatabaseDynKeyGetter()) if err = cache.Init(); err != nil { - testlogger.Panicf("cache.Init: %v\n", err) + return reportError("cache.Init: %v", err) } if err = storage.Init(); err != nil { - testlogger.Panicf("storage.Init: %v\n", err) + return reportError("storage.Init: %v", err) } if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil { - testlogger.Panicf("util.SyncDirs: %v\n", err) + return reportError("util.SyncDirs: %v", err) } if err = git.InitFull(); err != nil { - testlogger.Panicf("git.Init: %v\n", err) + return reportError("git.Init: %v", err) } if testOpts.SetUp != nil { if err := testOpts.SetUp(); err != nil { - testlogger.Panicf("set up failed: %v\n", err) + return reportError("set up failed: %v", err) } } @@ -109,7 +114,7 @@ APP_DATA_PATH = `+appDataPath+` if testOpts.TearDown != nil { if err := testOpts.TearDown(); err != nil { - testlogger.Panicf("tear down failed: %v\n", err) + return reportError("tear down failed: %v", err) } } return exitStatus diff --git a/modules/setting/server.go b/modules/setting/server.go index f8f4038c0d17a..7d9b8215be5d9 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -282,7 +282,7 @@ func loadServerFrom(rootCfg ConfigProvider) { // FIXME: debug bufCustomConf, _ := os.ReadFile(CustomConf) log.Error("Set AppDataPath=%s (CustomConf=%s)\n%s\n%s\n", AppDataPath, CustomConf, string(bufCustomConf), log.Stack(2)) - + DebugAppendLog("Set AppDataPath=%s (CustomConf=%s)\n%s\n%s\n", AppDataPath, CustomConf, string(bufCustomConf), log.Stack(2)) if !filepath.IsAbs(AppDataPath) { AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) } diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 688c43452f375..1d9efc9ed8b49 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -21,6 +21,19 @@ func GetGiteaTestSourceRoot() string { return *giteaTestSourceRoot } +func DebugAppendLog(msg string, a ...any) { + f, _ := os.OpenFile("/tmp/debug.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) + if f != nil { + defer f.Close() + fmt.Fprintf(f, msg+"\n", a...) + } +} + +func DebugGetLogs() string { + buf, _ := os.ReadFile("/tmp/debug.log") + return string(buf) +} + func SetupGiteaTestEnv() { if giteaTestSourceRoot != nil { return // already initialized @@ -69,6 +82,7 @@ func SetupGiteaTestEnv() { // FIXME: debug log.Error("SetupGiteaTestEnv CustomConf=%s\n%s\n%s\n", CustomConf, os.Getenv("GITEA_TEST_CONF_CONTENT"), log.Stack(2)) + DebugAppendLog("SetupGiteaTestEnv CustomConf=%s\n%s\n%s\n", CustomConf, os.Getenv("GITEA_TEST_CONF_CONTENT"), log.Stack(2)) } else { // CustomConf must be absolute path to make tests pass, CustomConf = filepath.Join(AppWorkPath, giteaConf) From 81fcfab0e35877262a84a14dfaffcd92ff42ece0 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 19:45:37 +0800 Subject: [PATCH 23/33] fix --- Makefile | 2 +- cmd/migrate_storage_test.go | 2 -- models/unittest/testdb.go | 19 ++++--------------- modules/queue/manager_test.go | 1 + modules/setting/server.go | 5 ----- modules/setting/testenv.go | 20 ++------------------ 6 files changed, 8 insertions(+), 41 deletions(-) diff --git a/Makefile b/Makefile index 3c0e2a3e731a6..e621cc362fce4 100644 --- a/Makefile +++ b/Makefile @@ -411,7 +411,7 @@ coverage: .PHONY: unit-test-coverage unit-test-coverage: @echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." - @$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -v -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 + @$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 .PHONY: tidy tidy: ## run go mod tidy diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go index 6c985146f85b9..3ea193eb1eae8 100644 --- a/cmd/migrate_storage_test.go +++ b/cmd/migrate_storage_test.go @@ -22,8 +22,6 @@ import ( func TestMigratePackages(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - t.Log("DebugLogs", setting.DebugGetLogs()) - creator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) content := "package main\n\nfunc main() {\nfmt.Println(\"hi\")\n}\n" diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 01191eb3d0f17..82c8d181ac268 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -47,24 +47,13 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { testOpts := util.OptionalArg(testOptsArg, &TestOptions{}) - appDataPath, appDataCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-data-") + tempWorkPath, tempCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-dir-") if err != nil { - return reportError("Failed to create temp dir for app data path: %v", err) + return reportError("Failed to create temp dir for unit test: %v", err) } + defer tempCleanup() - // FIXME: debug - _, _ = fmt.Fprintf(os.Stderr, "Prepare APP_DATA_PATH for SetupGiteaTestEnv: %s\n", appDataPath) - setting.DebugAppendLog("Prepare APP_DATA_PATH for SetupGiteaTestEnv: %s", appDataPath) - defer func() { - _, _ = fmt.Fprintf(os.Stderr, "Remove APP_DATA_PATH for SetupGiteaTestEnv: %s\n", appDataPath) - setting.DebugAppendLog("Remove APP_DATA_PATH for SetupGiteaTestEnv: %s", appDataPath) - appDataCleanup() - }() - - _ = os.Setenv("GITEA_TEST_CONF_CONTENT", ` -[server] -APP_DATA_PATH = `+appDataPath+` -`) + defer setting.MockBuiltinPaths(tempWorkPath, "", "")() setting.SetupGiteaTestEnv() if setting.RepoRootPath == "" || setting.AppDataPath == "" { return reportError("SetupGiteaTestEnv failed, paths are not initialized") diff --git a/modules/queue/manager_test.go b/modules/queue/manager_test.go index ee6ccc46ff3b9..f9f9b7310be26 100644 --- a/modules/queue/manager_test.go +++ b/modules/queue/manager_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" + "github.com/stretchr/testify/assert" ) diff --git a/modules/setting/server.go b/modules/setting/server.go index 7d9b8215be5d9..dc58e43c435a2 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -278,11 +278,6 @@ func loadServerFrom(rootCfg ConfigProvider) { StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath) StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour) AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data")) - - // FIXME: debug - bufCustomConf, _ := os.ReadFile(CustomConf) - log.Error("Set AppDataPath=%s (CustomConf=%s)\n%s\n%s\n", AppDataPath, CustomConf, string(bufCustomConf), log.Stack(2)) - DebugAppendLog("Set AppDataPath=%s (CustomConf=%s)\n%s\n%s\n", AppDataPath, CustomConf, string(bufCustomConf), log.Stack(2)) if !filepath.IsAbs(AppDataPath) { AppDataPath = filepath.ToSlash(filepath.Join(AppWorkPath, AppDataPath)) } diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 1d9efc9ed8b49..3ffb22109e583 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -21,19 +21,6 @@ func GetGiteaTestSourceRoot() string { return *giteaTestSourceRoot } -func DebugAppendLog(msg string, a ...any) { - f, _ := os.OpenFile("/tmp/debug.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) - if f != nil { - defer f.Close() - fmt.Fprintf(f, msg+"\n", a...) - } -} - -func DebugGetLogs() string { - buf, _ := os.ReadFile("/tmp/debug.log") - return string(buf) -} - func SetupGiteaTestEnv() { if giteaTestSourceRoot != nil { return // already initialized @@ -75,14 +62,11 @@ func SetupGiteaTestEnv() { giteaConf := os.Getenv("GITEA_TEST_CONF") if giteaConf == "" { // if no GITEA_TEST_CONF, then it is in unit test, use a temp (non-existing / empty) config file + // do not really use such config file, the test can run concurrently, using the same config file will cause data-race between tests giteaConf = "custom/conf/app-test-tmp.ini" customConfBuiltin = filepath.Join(AppWorkPath, giteaConf) CustomConf = customConfBuiltin - _ = os.WriteFile(CustomConf, []byte(os.Getenv("GITEA_TEST_CONF_CONTENT")), 0o644) - - // FIXME: debug - log.Error("SetupGiteaTestEnv CustomConf=%s\n%s\n%s\n", CustomConf, os.Getenv("GITEA_TEST_CONF_CONTENT"), log.Stack(2)) - DebugAppendLog("SetupGiteaTestEnv CustomConf=%s\n%s\n%s\n", CustomConf, os.Getenv("GITEA_TEST_CONF_CONTENT"), log.Stack(2)) + _ = os.Remove(CustomConf) } else { // CustomConf must be absolute path to make tests pass, CustomConf = filepath.Join(AppWorkPath, giteaConf) From 0b7aee8147902c132dd309cdf9f773da34286144 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 19:59:18 +0800 Subject: [PATCH 24/33] fix --- cmd/cmdtest/cmd_test.go | 2 +- modules/setting/testenv.go | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go index 3473013fd2747..c7c269e56489d 100644 --- a/cmd/cmdtest/cmd_test.go +++ b/cmd/cmdtest/cmd_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT // Tests here reload the config system multiple times with uncontrollable details. -// So it must be in a separate package, to avoid affecting other tests +// So they must be in a separate package, to avoid affecting other tests package cmdtest diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 3ffb22109e583..1c06fbb680938 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/util" ) var giteaTestSourceRoot *string @@ -51,10 +50,7 @@ func SetupGiteaTestEnv() { } initGiteaPaths := func() { - appWorkPathBuiltin = *giteaTestSourceRoot - AppWorkPath = appWorkPathBuiltin - AppPath = filepath.Join(AppWorkPath, "gitea") + util.Iif(IsWindows, ".exe", "") - StaticRootPath = AppWorkPath // need to load assets (options, public) from the source code directory for testing + StaticRootPath = *giteaTestSourceRoot // need to load assets (options, public) from the source code directory for testing } initGiteaConf := func() string { From e86d62e497f148dd16629f4c74f0fdda48fcbd13 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 20:24:43 +0800 Subject: [PATCH 25/33] clean up --- models/migrations/base/tests.go | 16 +-- models/unittest/testdb.go | 25 ++--- modules/git/git.go | 4 +- modules/git/gitcmd/command_test.go | 2 +- modules/setting/testenv.go | 4 + modules/testlogger/testlogger.go | 7 +- tests/integration/integration_test.go | 9 +- tests/test_utils.go | 144 +++++++++++++------------- 8 files changed, 108 insertions(+), 103 deletions(-) diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index b822b2d74e51c..c318306192858 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -91,7 +91,7 @@ func deleteDB() error { defer schrows.Close() if !schrows.Next() { - // Create and setup a DB schema + // Create and set up a DB schema _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema) if err != nil { return err @@ -134,7 +134,8 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu ourSkip := 2 ourSkip += skip deferFn := testlogger.PrintCurrentTest(t, ourSkip) - require.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + giteaRoot := setting.GetGiteaTestSourceRoot() + require.NoError(t, unittest.SyncDirs(filepath.Join(giteaRoot, "tests/gitea-repositories-meta"), setting.RepoRootPath)) if err := deleteDB(); err != nil { t.Fatalf("unable to reset database: %v", err) @@ -202,17 +203,18 @@ func LoadTableSchemasMap(t *testing.T, x *xorm.Engine) map[string]*schemas.Table func mainTest(m *testing.M) int { testlogger.Init() - setting.SetupGiteaTestEnv() - tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-") + tempWorkPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-") if err != nil { - testlogger.Panicf("Unable to create temporary data path %v\n", err) + return testlogger.MainErrorf("Unable to create temporary dir for migration test: %v", err) } defer cleanup() - setting.AppDataPath = tmpDataPath + + setting.MockBuiltinPaths(tempWorkPath, "", "") + setting.SetupGiteaTestEnv() if err = git.InitFull(); err != nil { - testlogger.Panicf("Unable to InitFull: %v\n", err) + return testlogger.MainErrorf("Unable to InitFull: %v", err) } setting.LoadDBSetting() setting.InitLoggersForTest() diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 82c8d181ac268..bd832348e7ce8 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/tempdir" + "code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -40,29 +41,21 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) { } func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { - reportError := func(msg string, a ...any) int { - _, _ = fmt.Fprintf(os.Stderr, msg+"\n", a...) - return 1 - } - testOpts := util.OptionalArg(testOptsArg, &TestOptions{}) tempWorkPath, tempCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-dir-") if err != nil { - return reportError("Failed to create temp dir for unit test: %v", err) + return testlogger.MainErrorf("Failed to create temp dir for unit test: %v", err) } defer tempCleanup() defer setting.MockBuiltinPaths(tempWorkPath, "", "")() setting.SetupGiteaTestEnv() - if setting.RepoRootPath == "" || setting.AppDataPath == "" { - return reportError("SetupGiteaTestEnv failed, paths are not initialized") - } giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} if err := CreateTestEngine(fixturesOpts); err != nil { - return reportError("Error creating test database engine: %v", err) + return testlogger.MainErrorf("Error creating test database engine: %v", err) } setting.AppURL = "https://try.gitea.io/" @@ -80,22 +73,22 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { config.SetDynGetter(system.NewDatabaseDynKeyGetter()) if err = cache.Init(); err != nil { - return reportError("cache.Init: %v", err) + return testlogger.MainErrorf("cache.Init: %v", err) } if err = storage.Init(); err != nil { - return reportError("storage.Init: %v", err) + return testlogger.MainErrorf("storage.Init: %v", err) } if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil { - return reportError("util.SyncDirs: %v", err) + return testlogger.MainErrorf("util.SyncDirs: %v", err) } if err = git.InitFull(); err != nil { - return reportError("git.Init: %v", err) + return testlogger.MainErrorf("git.Init: %v", err) } if testOpts.SetUp != nil { if err := testOpts.SetUp(); err != nil { - return reportError("set up failed: %v", err) + return testlogger.MainErrorf("set up failed: %v", err) } } @@ -103,7 +96,7 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { if testOpts.TearDown != nil { if err := testOpts.TearDown(); err != nil { - return reportError("tear down failed: %v", err) + return testlogger.MainErrorf("tear down failed: %v", err) } } return exitStatus diff --git a/modules/git/git.go b/modules/git/git.go index 2df83f9843ad9..d70d24460105c 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -192,13 +192,13 @@ func RunGitTests(m interface{ Run() int }) { func runGitTests(m interface{ Run() int }) int { gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") if err != nil { - testlogger.Panicf("unable to create temp dir: %s", err.Error()) + return testlogger.MainErrorf("unable to create temp dir: %v", err) } defer cleanup() setting.Git.HomePath = gitHomePath if err = InitFull(); err != nil { - testlogger.Panicf("failed to call Init: %s", err.Error()) + return testlogger.MainErrorf("failed to call Init: %s", err) } return m.Run() } diff --git a/modules/git/gitcmd/command_test.go b/modules/git/gitcmd/command_test.go index 6e4214d99532f..19ec02b8088dd 100644 --- a/modules/git/gitcmd/command_test.go +++ b/modules/git/gitcmd/command_test.go @@ -24,7 +24,7 @@ func testMain(m *testing.M) int { // "setting.Git.HomePath" is initialized in "git" package but really used in "gitcmd" package gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") if err != nil { - testlogger.Panicf("failed to create temp dir: %v", err) + return testlogger.MainErrorf("failed to create temp dir: %v", err) } defer cleanup() diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 1c06fbb680938..2e9d786b0360c 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -101,6 +101,10 @@ func SetupGiteaTestEnv() { cleanUpEnv() initWorkPathAndConfig() + if RepoRootPath == "" || AppDataPath == "" { + panic("SetupGiteaTestEnv failed, paths are not initialized") + } + // TODO: some git repo hooks (test fixtures) still use these env variables, need to be refactored in the future _ = os.Setenv("GITEA_ROOT", giteaRoot) _ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index 217121f604beb..2eeede878faaf 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -173,7 +173,8 @@ func Init() { log.RegisterEventWriter("test", newTestLoggerWriter) } -func Panicf(format string, args ...any) { - // don't call os.Exit, otherwise the "defer" functions won't be executed - panic(fmt.Sprintf(format, args...)) +// MainErrorf is used to report an error from TestMain and return a non-zero value to indicate the failure +func MainErrorf(msg string, a ...any) int { + _, _ = fmt.Fprintf(os.Stderr, msg+"\n", a...) + return 1 } diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 33be7d89cb695..d0a33fdec8c61 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -87,16 +87,19 @@ func testMain(m *testing.M) int { graceful.InitManager(managerCtx) defer cancel() - tests.InitTest() + err := tests.InitTest() + if err != nil { + return testlogger.MainErrorf("InitTest error: %v", err) + } testWebRoutes = routers.NormalRoutes() - err := unittest.InitFixtures( + err = unittest.InitFixtures( unittest.FixturesOptions{ Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"), }, ) if err != nil { - testlogger.Panicf("InitFixtures: %v", err) + return testlogger.MainErrorf("InitFixtures: %v", err) } // FIXME: the console logger is deleted by mistake, so if there is any `log.Fatal`, developers won't see any error message. diff --git a/tests/test_utils.go b/tests/test_utils.go index b724a30610a27..7ff7dd7b8aba6 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "testing" "code.gitea.io/gitea/models/db" @@ -15,7 +16,6 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/testlogger" @@ -25,8 +25,9 @@ import ( "github.com/stretchr/testify/assert" ) -func InitTest() { +func InitTest() error { testlogger.Init() + if os.Getenv("GITEA_TEST_CONF") == "" { // By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger. // It's easier for developers to debug bugs step by step with a debugger. @@ -39,101 +40,102 @@ func InitTest() { setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" if err := git.InitFull(); err != nil { - log.Fatal("git.InitOnceWithSync: %v", err) + return err } setting.LoadDBSetting() if err := storage.Init(); err != nil { - testlogger.Panicf("Init storage failed: %v\n", err) + return err } switch { case setting.Database.Type.IsMySQL(): - connType := "tcp" - if len(setting.Database.Host) > 0 && setting.Database.Host[0] == '/' { // looks like a unix socket - connType = "unix" - } - - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/", - setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host)) - defer db.Close() - if err != nil { - log.Fatal("sql.Open: %v", err) - } - if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil { - log.Fatal("db.Exec: %v", err) + { + connType := util.Iif(strings.HasPrefix(setting.Database.Host, "/"), "unix", "tcp") + db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/", + setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host)) + if err != nil { + return err + } + defer db.Close() + if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil { + return err + } } case setting.Database.Type.IsPostgreSQL(): - var db *sql.DB - var err error - if setting.Database.Host[0] == '/' { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s", - setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host)) - } else { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", + openPostgreSQL := func() (*sql.DB, error) { + if strings.HasPrefix(setting.Database.Host, "/") { + return sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s", + setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host)) + } + return sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode)) } - defer db.Close() - if err != nil { - log.Fatal("sql.Open: %v", err) - } - dbrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name)) - if err != nil { - log.Fatal("db.Query: %v", err) - } - defer dbrows.Close() + // create database + { + db, err := openPostgreSQL() + if err != nil { + return err + } + defer db.Close() + dbRows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name)) + if err != nil { + return err + } + defer dbRows.Close() - if !dbrows.Next() { - if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil { - log.Fatal("db.Exec: CREATE DATABASE: %v", err) + if !dbRows.Next() { + if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil { + return err + } } + // Check if we need to set up a specific schema + if setting.Database.Schema == "" { + break + } + db.Close() } - // Check if we need to setup a specific schema - if len(setting.Database.Schema) == 0 { - break - } - db.Close() - if setting.Database.Host[0] == '/' { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s", - setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host)) - } else { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", - setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode)) - } - // This is a different db object; requires a different Close() - defer db.Close() - if err != nil { - log.Fatal("sql.Open: %v", err) - } - schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) - if err != nil { - log.Fatal("db.Query: %v", err) - } - defer schrows.Close() + // create schema + { + db, err := openPostgreSQL() + if err != nil { + return err + } + defer db.Close() + + schemaRows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) + if err != nil { + return err + } + defer schemaRows.Close() - if !schrows.Next() { - // Create and setup a DB schema - if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil { - log.Fatal("db.Exec: CREATE SCHEMA: %v", err) + if !schemaRows.Next() { + // Create and set up a DB schema + if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil { + return err + } } } case setting.Database.Type.IsMSSQL(): - host, port := setting.ParseMSSQLHostPort(setting.Database.Host) - db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", - host, port, "master", setting.Database.User, setting.Database.Passwd)) - if err != nil { - log.Fatal("sql.Open: %v", err) - } - if _, err := db.Exec(fmt.Sprintf("If(db_id(N'%s') IS NULL) BEGIN CREATE DATABASE %s; END;", setting.Database.Name, setting.Database.Name)); err != nil { - log.Fatal("db.Exec: %v", err) + { + host, port := setting.ParseMSSQLHostPort(setting.Database.Host) + db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", + host, port, "master", setting.Database.User, setting.Database.Passwd)) + if err != nil { + return err + } + defer db.Close() + if _, err = db.Exec(fmt.Sprintf("If(db_id(N'%s') IS NULL) BEGIN CREATE DATABASE %s; END;", setting.Database.Name, setting.Database.Name)); err != nil { + return err + } } - defer db.Close() } routers.InitWebInstalled(graceful.GetManager().HammerContext()) + return nil } func PrepareAttachmentsStorage(t testing.TB) { From 9294e969b0b7ace79e130d674b26f09eb5ec86ef Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 20:38:51 +0800 Subject: [PATCH 26/33] fix test --- models/migrations/base/tests.go | 4 ++-- modules/setting/testenv.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index c318306192858..7482829f1f2fc 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -75,7 +75,7 @@ func deleteDB() error { } db.Close() - // Check if we need to setup a specific schema + // Check if we need to set up a specific schema if len(setting.Database.Schema) != 0 { db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode)) @@ -167,7 +167,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu } } - fixturesDir := filepath.Join(filepath.Dir(setting.AppPath), "models", "migrations", "fixtures", t.Name()) + fixturesDir := filepath.Join(giteaRoot, "models", "migrations", "fixtures", t.Name()) if _, err := os.Stat(fixturesDir); err == nil { t.Logf("initializing fixtures from: %s", fixturesDir) diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 2e9d786b0360c..c81b51fd71d16 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -64,8 +64,9 @@ func SetupGiteaTestEnv() { CustomConf = customConfBuiltin _ = os.Remove(CustomConf) } else { - // CustomConf must be absolute path to make tests pass, - CustomConf = filepath.Join(AppWorkPath, giteaConf) + // CustomConf must be absolute path to make tests pass. + // At the moment, GITEA_TEST_CONF is always in Gitea's source root + CustomConf = filepath.Join(*giteaTestSourceRoot, giteaConf) } return giteaConf } From 1d6a95f80a5bd3be4b1d926d2452cef830e81636 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 20:42:15 +0800 Subject: [PATCH 27/33] fix test --- modules/git/git.go | 2 +- modules/testlogger/testlogger.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/git/git.go b/modules/git/git.go index d70d24460105c..69eb07d1f0bbd 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -198,7 +198,7 @@ func runGitTests(m interface{ Run() int }) int { setting.Git.HomePath = gitHomePath if err = InitFull(); err != nil { - return testlogger.MainErrorf("failed to call Init: %s", err) + return testlogger.MainErrorf("failed to call Init: %v", err) } return m.Run() } diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index 2eeede878faaf..151ca39703217 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -118,7 +118,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() { deferHasRun := false t.Cleanup(func() { if !deferHasRun { - stdoutPrintf("!!! %s defer function hasn't been run but Cleanup is called, usually caused by panic\n", t.Name()) + stdoutPrintf("!!! %s: defer function hasn't been run but Cleanup is called, usually caused by panic\n", t.Name()) } }) stdoutPrintf("=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line) From 09cf00430662a93ba7b5b6d29f55548f82cd67dd Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 21:41:52 +0800 Subject: [PATCH 28/33] clean up paths --- modules/setting/testenv.go | 7 ++++++- tests/integration/integration_test.go | 2 +- tests/integration/migration-test/migration_test.go | 2 +- tests/test_utils.go | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index c81b51fd71d16..681e7fddf907b 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) var giteaTestSourceRoot *string @@ -50,7 +51,11 @@ func SetupGiteaTestEnv() { } initGiteaPaths := func() { - StaticRootPath = *giteaTestSourceRoot // need to load assets (options, public) from the source code directory for testing + // need to load assets (options, public) from the source code directory for testing + StaticRootPath = *giteaTestSourceRoot + // during testing, the AppPath must point to the pre-built Gitea binary in the source root + // it needs to be called by git hooks + AppPath = filepath.Join(*giteaTestSourceRoot, "gitea") + util.Iif(IsWindows, ".exe", "") } initGiteaConf := func() string { diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index d0a33fdec8c61..60dc7e77a4811 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -95,7 +95,7 @@ func testMain(m *testing.M) int { err = unittest.InitFixtures( unittest.FixturesOptions{ - Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"), + Dir: filepath.Join(setting.GetGiteaTestSourceRoot(), "models/fixtures/"), }, ) if err != nil { diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go index 1e1dc7bf142cd..c1acdb999b1ca 100644 --- a/tests/integration/migration-test/migration_test.go +++ b/tests/integration/migration-test/migration_test.go @@ -40,7 +40,7 @@ func initMigrationTest(t *testing.T) func() { setting.SetupGiteaTestEnv() assert.NotEmpty(t, setting.RepoRootPath) - assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/gitea-repositories-meta"), setting.RepoRootPath)) assert.NoError(t, git.InitFull()) setting.LoadDBSetting() setting.InitLoggersForTest() diff --git a/tests/test_utils.go b/tests/test_utils.go index 7ff7dd7b8aba6..d5a8008cefb9b 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -156,7 +156,7 @@ func PrepareGitRepoDirectory(t testing.TB) { if !assert.NotEmpty(t, setting.RepoRootPath) { return } - assert.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + assert.NoError(t, unittest.SyncDirs(filepath.Join(setting.GetGiteaTestSourceRoot(), "tests/gitea-repositories-meta"), setting.RepoRootPath)) } func PrepareArtifactsStorage(t testing.TB) { From b6831e0526aeb7669304b665aa66de5e0eba6241 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Apr 2026 22:14:06 +0800 Subject: [PATCH 29/33] fine tune comments --- modules/setting/testenv.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 681e7fddf907b..d8663d07e2452 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/util" ) -var giteaTestSourceRoot *string +var giteaTestSourceRoot *string // intentionally use a pointer to make sure the uninitialized access panics func GetGiteaTestSourceRoot() string { return *giteaTestSourceRoot @@ -30,7 +30,9 @@ func SetupGiteaTestEnv() { log.OsExiter = func(code int) { if code != 0 { - // non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details + // Non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens: + // * Show a full stacktrace for more details. + // * If the "log.Fatal" is abused in tests, should fix. panic(fmt.Errorf("non-zero exit code during testing: %d", code)) } os.Exit(0) @@ -49,13 +51,14 @@ func SetupGiteaTestEnv() { giteaTestSourceRoot = &giteaRoot return giteaRoot } + giteaRoot := initGiteaRoot() initGiteaPaths := func() { // need to load assets (options, public) from the source code directory for testing - StaticRootPath = *giteaTestSourceRoot + StaticRootPath = giteaRoot // during testing, the AppPath must point to the pre-built Gitea binary in the source root // it needs to be called by git hooks - AppPath = filepath.Join(*giteaTestSourceRoot, "gitea") + util.Iif(IsWindows, ".exe", "") + AppPath = filepath.Join(giteaRoot, "gitea") + util.Iif(IsWindows, ".exe", "") } initGiteaConf := func() string { @@ -71,7 +74,7 @@ func SetupGiteaTestEnv() { } else { // CustomConf must be absolute path to make tests pass. // At the moment, GITEA_TEST_CONF is always in Gitea's source root - CustomConf = filepath.Join(*giteaTestSourceRoot, giteaConf) + CustomConf = filepath.Join(giteaRoot, giteaConf) } return giteaConf } @@ -101,7 +104,6 @@ func SetupGiteaTestEnv() { PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy") } - giteaRoot := initGiteaRoot() initGiteaPaths() giteaConf := initGiteaConf() cleanUpEnv() From 632765f5ee001737bafc68c9b0173073dadf3e87 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 25 Apr 2026 17:31:04 +0200 Subject: [PATCH 30/33] Apply suggestion from @silverwind Signed-off-by: silverwind --- cmd/cmdtest/cmd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go index c7c269e56489d..4ff854f8abfb4 100644 --- a/cmd/cmdtest/cmd_test.go +++ b/cmd/cmdtest/cmd_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT // Tests here reload the config system multiple times with uncontrollable details. From 6dd7f76dc2c1df14b03181ebbca0c9bfe4be58d4 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 25 Apr 2026 17:31:22 +0200 Subject: [PATCH 31/33] Apply suggestion from @silverwind Signed-off-by: silverwind --- cmd/main_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/main_test.go b/cmd/main_test.go index b2b4936d93008..f367bf12e425e 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -1,4 +1,4 @@ -// Copyright 2025 The Gitea Authors. All rights reserved. +// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package cmd From 675ad20ea1bc6bec3cdfdf0134250a8c31b84868 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 25 Apr 2026 18:52:11 +0200 Subject: [PATCH 32/33] Apply suggestion from @silverwind Signed-off-by: silverwind --- cmd/cmdtest/cmd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go index 4ff854f8abfb4..c7c269e56489d 100644 --- a/cmd/cmdtest/cmd_test.go +++ b/cmd/cmdtest/cmd_test.go @@ -1,4 +1,4 @@ -// Copyright 2026 The Gitea Authors. All rights reserved. +// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT // Tests here reload the config system multiple times with uncontrollable details. From aee8bfccde12c954177d12c1a55266a77bdc95b7 Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 25 Apr 2026 19:09:39 +0200 Subject: [PATCH 33/33] Apply suggestion from @silverwind Signed-off-by: silverwind --- cmd/cmdtest/cmd_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cmdtest/cmd_test.go b/cmd/cmdtest/cmd_test.go index c7c269e56489d..4ff854f8abfb4 100644 --- a/cmd/cmdtest/cmd_test.go +++ b/cmd/cmdtest/cmd_test.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT // Tests here reload the config system multiple times with uncontrollable details.