0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-04-04 16:46:17 +02:00
gitea/services/actions/task_assignment_test.go
2026-04-01 08:15:57 +02:00

110 lines
4.0 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
"testing"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// countJobsByStatus returns the number of jobs with the given status in allJobs.
func countJobsByStatus(allJobs actions_model.ActionJobList, status actions_model.Status) int {
n := 0
for _, j := range allJobs {
if j.Status == status {
n++
}
}
return n
}
// TestMaxParallel_ServiceLayer verifies the max-parallel invariant: Running+Waiting <= MaxParallel.
func TestMaxParallel_ServiceLayer(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
t.Run("invariant: Running+Waiting <= MaxParallel", func(t *testing.T) {
runID := int64(10000)
jobID := "svc-max-parallel"
maxParallel := 2
run := &actions_model.ActionRun{ID: runID, RepoID: 1, OwnerID: 1, Index: 10000, Status: actions_model.StatusRunning}
require.NoError(t, db.Insert(context.Background(), run))
jobs := []*actions_model.ActionRunJob{
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "r1", Status: actions_model.StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "w1", Status: actions_model.StatusWaiting, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "b1", Status: actions_model.StatusBlocked, MaxParallel: maxParallel},
}
for _, j := range jobs {
require.NoError(t, db.Insert(context.Background(), j))
}
allJobs, err := actions_model.GetRunJobsByRunID(context.Background(), runID)
require.NoError(t, err)
running := countJobsByStatus(allJobs, actions_model.StatusRunning)
waiting := countJobsByStatus(allJobs, actions_model.StatusWaiting)
blocked := countJobsByStatus(allJobs, actions_model.StatusBlocked)
assert.LessOrEqual(t, running+waiting, maxParallel)
assert.Equal(t, 1, blocked)
})
t.Run("slot becomes available after completion", func(t *testing.T) {
runID := int64(20000)
jobID := "svc-slot-free"
maxParallel := 2
run := &actions_model.ActionRun{ID: runID, RepoID: 1, OwnerID: 1, Index: 20000, Status: actions_model.StatusRunning}
require.NoError(t, db.Insert(context.Background(), run))
jobs := []*actions_model.ActionRunJob{
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "r1", Status: actions_model.StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "r2", Status: actions_model.StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "b1", Status: actions_model.StatusBlocked, MaxParallel: maxParallel},
}
for _, j := range jobs {
require.NoError(t, db.Insert(context.Background(), j))
}
jobs[0].Status = actions_model.StatusSuccess
_, err := actions_model.UpdateRunJob(context.Background(), jobs[0], nil, "status")
require.NoError(t, err)
allJobs, err := actions_model.GetRunJobsByRunID(context.Background(), runID)
require.NoError(t, err)
running := countJobsByStatus(allJobs, actions_model.StatusRunning)
assert.Equal(t, 1, running)
assert.Less(t, running, maxParallel)
})
t.Run("no max-parallel means all jobs start as Waiting", func(t *testing.T) {
runID := int64(30000)
jobID := "svc-no-limit"
run := &actions_model.ActionRun{ID: runID, RepoID: 1, OwnerID: 1, Index: 30000, Status: actions_model.StatusRunning}
require.NoError(t, db.Insert(context.Background(), run))
for range 5 {
require.NoError(t, db.Insert(context.Background(), &actions_model.ActionRunJob{
RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "j",
Status: actions_model.StatusWaiting, MaxParallel: 0,
}))
}
allJobs, err := actions_model.GetRunJobsByRunID(context.Background(), runID)
require.NoError(t, err)
assert.Equal(t, 5, countJobsByStatus(allJobs, actions_model.StatusWaiting))
assert.Equal(t, 0, countJobsByStatus(allJobs, actions_model.StatusBlocked))
})
}