0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-24 23:26:34 +02:00
gitea/models/actions/run_job_maxparallel_test.go
Pascal Zimmermann 63be2b1103 feat: Update the conditions of the actions to continue the tasks executions
Signed-off-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
2026-04-01 08:15:29 +02:00

256 lines
7.5 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
)
func TestActionRunJob_MaxParallel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx := context.Background()
t.Run("NoMaxParallel", func(t *testing.T) {
job := &ActionRunJob{
RunID: 1,
RepoID: 1,
OwnerID: 1,
JobID: "test-job-1",
Name: "Test Job",
Status: StatusWaiting,
MaxParallel: 0, // No limit
}
assert.NoError(t, db.Insert(ctx, job))
retrieved, err := GetRunJobByID(ctx, job.ID)
assert.NoError(t, err)
assert.Equal(t, 0, retrieved.MaxParallel)
})
t.Run("WithMaxParallel", func(t *testing.T) {
job := &ActionRunJob{
RunID: 1,
RepoID: 1,
OwnerID: 1,
JobID: "test-job-2",
Name: "Matrix Job",
Status: StatusWaiting,
MaxParallel: 3,
}
assert.NoError(t, db.Insert(ctx, job))
retrieved, err := GetRunJobByID(ctx, job.ID)
assert.NoError(t, err)
assert.Equal(t, 3, retrieved.MaxParallel)
})
t.Run("UpdateMaxParallel", func(t *testing.T) {
// Create ActionRun first
run := &ActionRun{
ID: 1,
RepoID: 1,
OwnerID: 1,
Status: StatusRunning,
}
// Note: This might fail if run already exists from previous tests, but that's okay
_ = db.Insert(ctx, run)
job := &ActionRunJob{
RunID: 1,
RepoID: 1,
OwnerID: 1,
JobID: "test-job-4",
Name: "Updatable Job",
Status: StatusWaiting,
MaxParallel: 5,
}
assert.NoError(t, db.Insert(ctx, job))
// Update max parallel
job.MaxParallel = 10
_, err := UpdateRunJob(ctx, job, nil, "max_parallel")
assert.NoError(t, err)
retrieved, err := GetRunJobByID(ctx, job.ID)
assert.NoError(t, err)
assert.Equal(t, 10, retrieved.MaxParallel)
})
}
func TestActionRunJob_MaxParallelEnforcement(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx := context.Background()
t.Run("EnforceMaxParallel", func(t *testing.T) {
runID := int64(5000)
jobID := "parallel-enforced-job"
maxParallel := 2
// Create ActionRun first
run := &ActionRun{
ID: runID,
RepoID: 1,
OwnerID: 1,
Index: 5000,
Status: StatusRunning,
}
assert.NoError(t, db.Insert(ctx, run))
// Create jobs simulating matrix execution
jobs := []*ActionRunJob{
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 1", Status: StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 2", Status: StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 3", Status: StatusWaiting, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 4", Status: StatusWaiting, MaxParallel: maxParallel},
}
for _, job := range jobs {
assert.NoError(t, db.Insert(ctx, job))
}
// Verify running count
runningCount, err := CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, maxParallel, runningCount, "Should have exactly max-parallel jobs running")
// Simulate job completion
jobs[0].Status = StatusSuccess
_, err = UpdateRunJob(ctx, jobs[0], nil, "status")
assert.NoError(t, err)
// Now running count should be 1
runningCount, err = CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 1, runningCount)
// Simulate next job starting
jobs[2].Status = StatusRunning
_, err = UpdateRunJob(ctx, jobs[2], nil, "status")
assert.NoError(t, err)
// Back to max-parallel
runningCount, err = CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, maxParallel, runningCount)
})
t.Run("MaxParallelOne_SequentialExecution", func(t *testing.T) {
runID := int64(6000)
jobID := "sequential-job"
maxParallel := 1
// Create ActionRun first
run := &ActionRun{
ID: runID,
RepoID: 1,
OwnerID: 1,
Index: 6000,
Status: StatusRunning,
}
assert.NoError(t, db.Insert(ctx, run))
// Create jobs simulating sequential execution with max-parallel=1
jobs := []*ActionRunJob{
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 1", Status: StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 2", Status: StatusWaiting, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 3", Status: StatusWaiting, MaxParallel: maxParallel},
}
for _, job := range jobs {
assert.NoError(t, db.Insert(ctx, job))
}
// Verify initial running count is 1
runningCount, err := CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 1, runningCount, "Should have exactly 1 job running with max-parallel=1")
// Complete first job
jobs[0].Status = StatusSuccess
_, err = UpdateRunJob(ctx, jobs[0], nil, "status")
assert.NoError(t, err)
// Now running count should be 0
runningCount, err = CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 0, runningCount, "Should have 0 jobs running after first job completes")
// Second job can now start
jobs[1].Status = StatusRunning
_, err = UpdateRunJob(ctx, jobs[1], nil, "status")
assert.NoError(t, err)
// Running count should be 1 again
runningCount, err = CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 1, runningCount, "Should have exactly 1 job running after second job starts")
// Complete second job
jobs[1].Status = StatusSuccess
_, err = UpdateRunJob(ctx, jobs[1], nil, "status")
assert.NoError(t, err)
// Third job can now start
jobs[2].Status = StatusRunning
_, err = UpdateRunJob(ctx, jobs[2], nil, "status")
assert.NoError(t, err)
// Running count should still be 1
runningCount, err = CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 1, runningCount, "Should have exactly 1 job running after third job starts")
})
t.Run("MaxParallelOne_WithFailure", func(t *testing.T) {
runID := int64(7000)
jobID := "sequential-with-failure-job"
maxParallel := 1
// Create ActionRun first
run := &ActionRun{
ID: runID,
RepoID: 1,
OwnerID: 1,
Index: 7000,
Status: StatusRunning,
}
assert.NoError(t, db.Insert(ctx, run))
// Create jobs
jobs := []*ActionRunJob{
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 1", Status: StatusRunning, MaxParallel: maxParallel},
{RunID: runID, RepoID: 1, OwnerID: 1, JobID: jobID, Name: "Job 2", Status: StatusWaiting, MaxParallel: maxParallel},
}
for _, job := range jobs {
assert.NoError(t, db.Insert(ctx, job))
}
// First job fails
jobs[0].Status = StatusFailure
_, err := UpdateRunJob(ctx, jobs[0], nil, "status")
assert.NoError(t, err)
// Verify no jobs are running
runningCount, err := CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 0, runningCount, "Should have 0 jobs running after job fails")
// Second job can still start
jobs[1].Status = StatusRunning
_, err = UpdateRunJob(ctx, jobs[1], nil, "status")
assert.NoError(t, err)
runningCount, err = CountRunningJobsByWorkflowAndRun(ctx, runID, jobID)
assert.NoError(t, err)
assert.Equal(t, 1, runningCount, "Should have exactly 1 job running after job starts")
})
}